Bash script, killing a program (vim and atom->editor) within the running script - bash

Is there Any way to KILL/EXIT/CLOSE VI and ATOM from a running script
example script, test.sh:
EDITOR1=vi
EDITOR2=atom
$EDITOR1 helloWorld.txt
$EDITOR2 file1.txt
kill $EDITOR1
kill $EITOR2
Is there any NOT Set way to kill it, I mean with a variable fore example the Filename.

You can use pkill -f <filename>, as shown below:
[fsilveir#fsilveir ~]$ ps -ef | grep vim
fsilveir 28867 28384 0 00:07 pts/5 00:00:00 vim foo.txt
fsilveir 28870 28456 0 00:07 pts/6 00:00:00 vim bar.txt
[fsilveir#fsilveir ~]$
[fsilveir#fsilveir ~]$ pkill -f bar.txt
[fsilveir#fsilveir ~]$
[fsilveir#fsilveir ~]$ ps -ef | grep vim
fsilveir 22344 11182 0 Mar18 pts/0 00:00:00 vim openfiles.py
fsilveir 28867 28384 0 00:07 pts/5 00:00:00 vim foo.txt
fsilveir 28958 28740 0 00:08 pts/7 00:00:00 grep --color=auto vim
[fsilveir#fsilveir ~]$

I can think of two ways:
Kill a process by its name.
kill `pidof $EDITOR1`
Kill the last started process
vi file &
kill $!
&: start process in the background (for interactive command line sessions)
$!: saves pid of last executed process.

kill won't work on any of the EDITOR variable because none of them are job id, kill only works on job ids or process ids. Running your above script does not place any of that commands in the background, once EDITOR1 executes it blocks the entire program, nothing can run until that process ( created by the $EDITOR1 filename is killed. For you to achieve your desired goal you have to run them on the background using &, when you use & a JOB ID will be created ( which increments ) at every call to &, you have to find a way to keep track of the JOB IDS . For example if you do $EDITOR1 filename.txt &; a job id of 1 is created, if you do $EDITOR2 filename2.txt a job id of 2 is created. Your case
EDITOR1=vi ; EDITOR2=atom
declare -a JOB_ID
declare -i JOB=0;
$EDITOR1 helloword.txt &;
JOB_ID+=( $(( ++JOB )) );
$EDITOR2 file1.txt;
JOB_ID+=( $(( ++JOB )) );
kill ${JOB_ID[0]}; // kills the first job
kill ${JOB_ID[1]}; // kills the second job
or you can use associative arrays
EDITOR1=vi ; EDITOR2=atom
declare -A JOB_ID
declare -I JOB=0;
$EDITOR1 helloword.txt &
JOB_ID[$EDITOR1]=$(( ++JOB ));
$EDITOR2 file1.txt &
JOB_ID[$EDITOR2]=$(( ++JOB ));
kill ${JOB_ID[$EDITOR1]}; // kills the first job
kill ${JOB_ID[$EDITOR2]}; // kills the second job
i have not tried any of this , but it's going to work

Why don't you use killall command, for example:
killall vim
killall atom

Related

how to extract the PID of a process by command line

I want to get the PID of a process namely "cron" by command line.
I tried the following script.
ps ax|grep 'cron'
but I am getting a part of a table,
1427 ? Ss 0:00 /usr/sbin/cron -f
24160 pts/5 S+ 0:00 grep --color=auto cron
How I extract the pid from this ?
The pgrep utility will return the process IDs for the currently running processes matching its argument:
$ pgrep cron
228
It may also be used to "grep for" things on the command line:
$ pgrep -f uerfale
69749
69752
$ pgrep -l -f uerfale
69749 slogin uerfale
69752 slogin: /home/kk/.ssh/sockets/uerfale-9022-kk.sock [mux] m
To kill a process by name, use pkill. It works in the same way as pgrep but will send a signal to the matched processes instead of outputting a process ID.
Just use pidof, rather to use other commands and apply post-processing actions on them.
$ pidof cron
22434
To make the command return only one PID pertaining to to the process, use the -s flag
-s
Single shot - this instructs the program to only return one pid.
Like this, for example:
ps -ef|grep 'cron'|grep -v grep|awk '{print $2}'
You can try this;
ps -o pid,sess,cmd afx | egrep "( |/)cron( -f)?$"
or
pstree -pas <cronPID>

How to kill a background process created in a script

Suppose I input the following in a shell
(while true; do echo hahaha; sleep 1; done)&
Then I know I can kill it by
fg; CTRL-C
However, if the command above is in a script e.g. tmp.sh and I'm running that script, how to kill it?
(while true; do echo hahaha; sleep 1; done)&
RUNNING_PID=$!
kill ${RUNNING_PID}
$! will pick up the PID of the process that is running so you can do with it as you wish
Let's suppose that you have your bash script named tmp.sh with the next content:
#!/bin/bash
(while true; do echo hahaha; sleep 1; done)&
And you execute it! Of course, it will print hahaha to the stdout every 1 second. You can't list it with the jobs command. But... it's still a process! And it's a child in the forest of the current terminal! So:
1- Get the file name of the terminal connected to standard input:
$tty
/dev/pts/2
2- List the processes associated with the terminal (In the example we are using pts/2), and show the status with S and display in a forest format f:
$ps --tty pts/2 Sf
PID TTY STAT TIME COMMAND
3691 pts/2 Ss+ 0:00 /bin/bash
3787 pts/2 S 0:00 /bin/bash
4879 pts/2 S 0:00 \_ sleep 1
3- Now, you can see that the example lists a sleep 1 command that is a child of the /bin/bash process with PID 3787. Now kill it!
kill -9 3787
Note: Don't kill the bash process that has the s+ statuses, is bash process that gives you the prompt! From man(ps):
s is a session leader
+ is in the foreground process group
Recommendations:
In a case like this, you should save the PID in a file:
#!/bin/bash
(while true; do echo hahaha; sleep 1; done)&
echo $! > /path/to/my_script.pid
Then, you could just do some script to shut it down:
#!/bin/bash
kill -9 $(cat /path/to/my_script.pid)

How to get pid of piped command?

(or How to kill the child process)?
inotifywait -mqr --format '%w %f %e' $feedDir | while read dir file event
do
#something
done &
echo $! #5431
ps eg:
>$ ps
PID TTY TIME CMD
2867 pts/3 00:00:02 bash
5430 pts/3 00:00:00 inotifywait
5431 pts/3 00:00:00 bash
5454 pts/3 00:00:00 ps
It seems if I kill 5431 then 5430 (inotifywait) will be left running, but if I kill 5430 then both processes die. I don't suppose I can reliably assume that the pid of inotifywait will always be 1 less than $!?
When we run a pipe, each command is executed in a separated process. The interpreter waits for the last one but if we use ampersand (&).
cmd1 | cmd2 &
The pid of processes will be probably close, but we cannot assume it reliably. In the case where the last command is a bash reserved word as while, it creates a dedicated bash (that's why your 'dir', 'file' variables won't exist after the done keyword). Example:
ps # shows one bash process
echo "azerty" | while read line; do ps; done # shows one more bash
When the first command exits, the second one will terminate because the read on the pipe return EOF.
When the second command exits, the first command will be terminated by the signal SIGPIPE (write on a pipe with no reader) when it tries to write to the pipe. But if the command waits indefinitely... it is not terminated.
echo "$!" prints the pid of the last command executed in background. In your case, the bash process that is executing the while loop.
You can find the pid of "inotifywait" with the following syntax. But it's uggly:
(inotifywait ... & echo "$!">inotifywait.pid) | \
while read dir file event
do
#something
done &
cat inotifywait.pid # prints pid of inotifywait
If you don't want the pid, but just be sure the process will be terminated, you can use the -t option of inotifywait:
(while true; do inotifywait -t 10 ...; done)| \
while read dir file event
do
#something
done &
kill "$!" # kill the while loop
None of this solution are nice. What is your real achievement? Maybe we can find a more elegant solution.
If your goal is to make sure all of the children can be killed or interrupted elegantly. If you're using BusyBox's Ash, you don't have process substitution. If you don't want to use an fd either, check out this solution.
#!/bin/sh
pid=$$
terminate() {
pkill -9 -P "$pid"
}
trap terminate SIGHUP SIGINT SIGQUIT SIGTERM
# do your stuff here, note: should be run in the background {{{
inotifywait -mqr --format '%w %f %e' $feedDir | while read dir file event
do
#something
done &
# }}}
# Either pkill -9 -P "$pid" here
wait
# or pkill -9 -P "$pid" here
Or in another shell:
kill <pid ($$)>

Bash hide "killed"

I cannot see to hide the "killed" output from my script. Please help.
killall -9 $SCRIPT
I have tried:
killall -9 $SCRIPT >/dev/null 2>&1
and everyone redirect combination it seems. Thanks for the help.
* UPDATE *
The main script cannot run in the background. It outputs a bunch on information to the user while running. Thanks for the input though. Any other ideas?
Here is the desired output
HEV Terminated
administrator#HEV-DEV:~/hev-1.2.7$
Here is the current output:
HEV Terminated
Killed
administrator#HEV-DEV:~/hev-1.2.7$
It's not the script printing it, it's the shell. You can suppress it by putting it in the background and waiting for it to finish, redirecting the error from there:
./script &
wait 2>/dev/null
It looks like this can be avoided if you execute the script (including the &) in a subshell:
$ ( ./script.sh & )
$ ps
PID TTY TIME CMD
14208 pts/0 00:00:00 bash
16134 pts/0 00:00:00 script.sh
16135 pts/0 00:00:00 sleep
16136 pts/0 00:00:00 ps
$ killall script.sh
$
If I execute it (without the subshell) like ./script.sh then I get the output
[1]+ Terminated ./script.sh &>/dev/null
Try ...
(killall -9 $SCRIPT) 2>/dev/null
This executes the "killall" in a subshell and all output from that subshell is redirected, including the the "KILLED" output.
Try killall -q,--quiet don't print complaints
killall -q -9 $SCRIPT

How to get the process id of a bash subprocess on command line

I know in bash we can create subshells using round parenthesis ( and ). As per bash man page:
(list) list is executed in a subshell environment
Also to get the current process id we use:
echo $$
Now my question is how to get process id of a subshell created using ( and ) on command line?
If I use this:
echo $$; ( echo $$; )
I will get the parent shell's process id printed twice on stdout since $$ gets expanded even before subshell is created. So how to really force the lazy expansion?
[Solution should work on Mac as well not just Linux]
Update:
Suggested linked answer doesn't work since echo $BASHPID does not work on my Mac and returns blank.
Thanks to all of you for spending your valuable time in finding answer to my question here.
However I am now answering my own question since I've found a hack way to get this pid on bash ver < 4 (will work on all the versions though). Here is the command:
echo $$; ( F='/tmp/myps'; [ ! -f $F ] && echo 'echo $PPID' > $F; )
It prints:
5642
13715
Where 13715 is the pid of the subshell. To test this when I do:
echo $$; ( F='/tmp/myps'; [ ! -f $F ] && echo 'echo $PPID' > $F; bash $F; ps; )
I get this:
5642
13773
PID TT STAT TIME COMMAND
5642 s001 S 0:02.07 -bash
13773 s001 S+ 0:00.00 -bash
Telling me that 13773 is indeed the pid of the subshell.
Note: I reverted back to my original solution since as #ChrisDodd commented that echo $$; ( bash -c 'echo $PPID'; ) doesn't work Linux. Above solution of mine works both on Mac and Linux.
Unfortunately there's no easy way to do this prior to bash version 4, when $BASHPID was introduced. One thing you can do is to write a tiny program that prints its parent PID:
int main()
{
printf("%d\n", getppid());
return 0;
}
If you compile that as ppid and put it in your path, you can call it, eg:
$ (echo $$; ppid)
2139
29519
$ (x=$(ppid); echo $x)
29521
One oddness I noticed, however, is that if you write
$ (ppid)
it doesn't seem to actually run it in a subshell -- you need at least two commands inside the parentheses for bash to actually run them in a subshell.
You can do :
$ ( your_action ) &
[1] 44012
And find subprocess' PID like that :
$ echo "The sub PID : $!"
The Sub PID : 44012
$! returns the last job in background's PID. (see this manual)
Use homebrew to install pgrep on the Mac: brew install pgrep
Check out Link to install Homebrew.
This seems like it works:
(echo $$; echo `ps axo pid,command,args | grep "$$" |awk '{ getline;print $1}'`)
14609
17365
For whatever reason, OSX is limited and doesnt come with pgrep, or one could do (which works in Linux):
(echo $$; echo `pgrep -P $$`)
14609
17390
You can use the ppid of the parent by echoing out the BASHPID of the parent when you first enter the shell, then you background the process and can look up the pid via ppid using the parent pid.
E.g. To get the pid of a sleep 555 command backgrounded within a subshell:
(echo "$BASHPID" > /tmp/_tmp_pid_ && sleep 555 &) && ps -ho pid --ppid=$(< /tmp/_tmp_pid_)

Resources