I have a bash script (ScreamDaemon.sh) inside which a check that example of it isn't running already is added.
numscr=`ps aux | grep ScreamDaemon.sh | wc -l`;
if [ "${numscr}" -gt "2" ]; then
echo "an instance of ScreamDaemon still running";
exit 0;
fi
Normally, if there are no another copy of script running, ps aux | grep ScreamDaemon.sh | wc -l should return 2 (it should find itself and grep ScreamDaemon.sh), but it returns 3.
So, I try to analyse what happens and after adding some echoes see this:
there are lines I have added into the script
ps aux | grep ScreamDaemon.sh
ps aux | grep ScreamDaemon.sh | wc -l
str=`ps aux | grep ScreamDaemon.sh`
echo $str
numscr=`ps aux | grep ScreamDaemon.sh | wc -l`;
echo $numscr
there is an output:
pamela 27894 0.0 0.0 106100 1216 pts/1 S+ 13:41 0:00 /bin/bash ./ScreamDaemon.sh
pamela 27899 0.0 0.0 103252 844 pts/1 S+ 13:41 0:00 grep ScreamDaemon.sh
2
pamela 27894 0.0 0.0 106100 1216 pts/1 S+ 13:41 0:00 /bin/bash ./ScreamDaemon.sh pamela 27903 0.0 0.0 106100 524 pts/1 S+ 13:41 0:00 /bin/bash ./ScreamDaemon.sh pamela 27905 0.0 0.0 103252 848 pts/1 S+ 13:41 0:00 grep ScreamDaemon.sh
3
I also tried to add the sleep command right inside `ps aux | grep ScreamDaemon.sh; sleep 1m` and see from the parallel terminal how many instances ps aux|grep ScreamDaemon.sh shows:
[pamela#pm03 ~]$ ps aux | grep ScreamDaemon.sh
pamela 28394 0.0 0.0 106100 1216 pts/1 S+ 14:23 0:00 /bin/bash ./ScreamDaemon.sh
pamela 28403 0.0 0.0 106100 592 pts/1 S+ 14:23 0:00 /bin/bash ./ScreamDaemon.sh
pamela 28408 0.0 0.0 103252 848 pts/9 S+ 14:23 0:00 grep ScreamDaemon.sh
So, it seems that
str=`ps aux | grep ScreamDaemon.sh`
contrary to
ps aux | grep ScreamDaemon.sh
found two instances of ScreamDaemon.sh, but why? Where this additional copy of ScreamDaemon.sh come from?
This is an output of pstree -ap command
│ ├─sshd,27806
│ │ └─sshd,27808
│ │ └─bash,27809
│ │ └─ScreamDaemon.sh,28731 ./ScreamDaemon.sh
│ │ └─ScreamDaemon.sh,28740 ./ScreamDaemon.sh
│ │ └─sleep,28743 2m
Why can a single bash script show up multiple times in ps?
This is typical when any constructs which implicitly create a subshell are in play. For instance, in bash:
echo foo | bar
...creates a new forked copy of the shell to run the echo, with its own ps instance. Similarly:
( bar; echo done )
...creates a new subshell, has that subshell run the external command bar, and then has the subshell perform the echo.
Similarly:
foo=$(bar)
...creates a subshell for the command substitution, runs bar in there (potentially exec'ing the command and consuming the subshell, but this is not guaranteed), and reads its output into the parent.
Now, how does this answer your question? Because
result=$(ps aux | grep | wc)
...runs that ps command in a subshell, which itself creates an extra bash instance.
How can I properly ensure that only one copy of my script is running?
Use a lockfile.
See for instance:
How to prevent a script from running simultaneously?
What is the best way to ensure only one instance of a Bash script is running?
Note that I strongly suggest use of a flock-based variant.
Of course, the reason you find an additional process is because:
One process is running the sub-shell (of the command execution `..`)
included in your line: numscr=`ps aux | grep ScreamDaemon.sh | wc -l`;
that's the simplest answer.
However I would like to make some additional suggestions about your code:
First, quote your expansions, it should be: echo "$str".
Not doing so is making several lines collapse into a long one.
Second, you may use: grep [S]creamDaemon.sh to avoid matching the grep command itself.
Third, capture the command just once in a variable, then count lines from the variable. In this case it presents no problem, but for dynamic processes, one capture and the following capture to count could give different results.
Fourth, make an habit of using $(...) command substitutions instead of the more error prone (especially when Nesting) `...`.
### Using a file as the simplest way to capture the output of a command
### that is running in this shell (not a subshell).
ps aux | grep "[S]creamDaemon.sh" > "/tmp/tmpfile$$.txt"
str="$(< "/tmp/tmpfile$$.txt")" ### get the value of var "str"
rm "/tmp/tmpfile$$.txt" ### erase the file used ($$ is pid).
numscr="$(echo "$str" | wc -l)" ### count the number of lines.
echo "$numscr" ### present results.
echo "$str"
str="$( ps aux | grep "[S]creamDaemon.sh" )" ### capture var "str".
numscr="$(echo "$str" | wc -l)" ### count the number of lines.
echo "$numscr" ### present results.
echo "$str"
### The only bashim is the `$(<...)`, change to `$(cat ...)` if needed.
#CharlesDuffy covered the point of a flock quite well, please read it.
Related
This question already has answers here:
How to get process ID of background process?
(9 answers)
Closed 1 year ago.
Below is the output of executing two commands ads2 svcd& and ps -aux|grep ads2
nvidia#nvidia-desktop:~$ ads2 svcd&
[1] 4593
nvidia#nvidia-desktop:~$ ps -aux|grep ads2
nvidia 4593 0.5 0.0 39796 23864 pts/0 Sl 08:20 0:00 /opt/ads2/arm-
linux64/bin/ads2svcd
nvidia 4603 0.0 0.0 6092 672 pts/0 S+ 08:20 0:00 grep --color=auto ads2
nvidia#nvidia-desktop:~$
nvidia#nvidia-desktop:~$
the command ads2 svcd& runs a process related to ads2 software. with ps -aux|grep ads2 i displayed the whole processes that contains the name "ads2".
Now What i'm trying to do is to get the process number of the ads2 which in this example is 4593. So i wrote the follwing bash script:
#!/usr/bin/env bash
process="$(ps -aux|grep ads | grep 'nvidia' | cut -d' ' -f 3)"
echo "The current ads2 process is " $process
The bash script outputs the following:
nvidia#nvidia-desktop:~$ ./test.sh
The current ads2 process is
As you see the process number is not filtered. So what i'm i doing wrong?
thanks in advance
List all the processes in the current shell with $$ built-in variable
ps --forest -gp $$
PID TTY STAT TIME COMMAND
3809 pts/1 Ss 0:01 bash
4896 pts/1 T 0:00 \_ vim file.json
22965 pts/1 S+ 0:00 \_ ssh dw
3607 pts/0 Ss 0:01 bash
2500 pts/0 R+ 0:00 \_ ps --forest -gp 3607
3327 tty2 Ssl+ 0:00 /usr/lib/gdm3/gdm-x-session --run-script i3
3329 tty2 Sl+ 8:12 \_ /usr/lib/xorg/Xorg vt2 -displayfd 3 -auth /run/user/1000/gdm/Xauthority -background none -noreset -keeptty -verbose 3
3346 tty2 S+ 0:03 \_ i3
Just see the pid of them:
ps -opid --forest -gp $$
PID
3809
4896
22965
3607
2688
3327
3329
3346
If you need to use grep for any reason, use -opid,cmd with current shell:
ps -opid,cmd -gp $$ | grep vim
3851 grep --color=auto vim
4896 vim file.json
For all, just use -e
ps -e -opid,cmd | grep vim
4141 grep --color=auto vim
4896 vim file.json
The complete one, we have to ignore the grep itself:
ps -e -opid,cmd | grep vim | grep -v grep | cut -d' ' -f 2
4896
Without double grep using comm option for ps
ps -opid,comm -gp $$ | grep vim
4896 vim
of course the simplest one is pgrep
pgrep vim
4896
NOTE for variable assignment there should NOT be any space:
# wrong
ads2ProcessId = $(pgrep ads2)
# right
ads2ProcessId=$(pgrep ads2)
when I give
ps -aux|grep -w 'bash'|grep -v 'grep'|awk '{print $2}'
I get output:
32356
Also when I give
echo $(ps -aux|grep -w 'bash'|grep -v 'grep'|awk '{print $2}')
i get output:
32356 32551
Why there is difference in the outputs?
What is happening is that the $(...) syntax starts a subshell. So the ps command inside those parentheses will find both your current shell (which you can get using $$, as #tink said his their answer), and the subshell that is invoked by the parentheses.
Using ps fux and rediction into a file, you can see exactly what is happening:
ps fux | grep -w bash > a
cat a
me 11603 0.1 0.0 114408 3728 pts/1 Ss 13:37 0:00 \_ -bash
me 11955 0.0 0.0 103348 872 pts/1 S+ 13:41 0:00 \_ grep -w bash
echo $(ps fux | grep -w bash > b)
cat b
me 11603 0.1 0.0 114408 3728 pts/1 Ss+ 13:37 0:00 \_ -bash
me 11981 0.0 0.0 114408 2304 pts/1 S+ 13:41 0:00 \_ -bash
me 11983 0.0 0.0 103348 872 pts/1 S+ 13:41 0:00 \_ grep -w bash
Although #tink gave you a good solution, I hope this answer helps understand what is happening.
It would appear that you're after the pid of the shell you're currently running. That's best achieved with
echo $$ # which is a bash special variable
I'm a newbie to unix-like. And I met a weird issue that I really cannot find answers by searching.
#!/bin/bash
me=`basename "$0"`
echo $(ps -e | grep "$me" | wc -l)
ps -e | grep "$me" | wc -l
After executing that bash script, the echo shows me 2, and ps just shows me 1 which is what I want. How can this happen? Why echo shows me an extra process?
As Charles Duffy pointed out, $() creates a subshell. That answers my question. Apparently I still have a lot to learn. Thanks for all the help.
As noted by a comment by Cyrus; this script:
me=$(basename $0)
ps -ef |grep $me
when launched with "./ps.sh", prints:
auser#pc:/tmp$ ./ps.sh
auser 4425 4422 0 08:42 pts/3 00:00:00 grep ps.sh
auser#pc:/tmp$
No subshells are involved here, it is the grep(1) itself that is listed by ps(1). The same script, launched with "bash ps.sh" outputs:
auser 4426 3946 0 08:44 pts/3 00:00:00 bash ps.sh
auser 4429 4426 0 08:44 pts/3 00:00:00 grep ps.sh
This is the result the OP gets, even without subshells. Even more explicit:
auser#pc:/tmp$ ps -ef |grep grep
auser 4467 3946 0 08:49 pts/3 00:00:00 grep grep
although you are creating a sub shell by using $() you can grep this out by using grep -v grep.
So:
$(ps -e | grep "$me" | grep -v grep | wc -l)
which will return 1 instead of 2
bash: 4.3.42(1)-release (x86_64-pc-linux-gnu)
Executing the following script:
# This is myscript.sh
line=$(ps aux | grep [m]yscript) # A => returns two duplicates processes (why?)
echo "'$line'"
ps aux | grep [m]yscript # B => returns only one
Output:
'tom 31836 0.0 0.0 17656 3132 pts/25 S+ 10:33 0:00 bash myscript.sh
tom 31837 0.0 0.0 17660 1736 pts/25 S+ 10:33 0:00 bash myscript.sh'
tom 31836 0.0 0.0 17660 3428 pts/25 S+ 10:33 0:00 bash myscript.sh
Why does the inline executed ps-snippet (A) return two lines?
Summary
This creates a subshell and hence two processes are running:
line=$(ps aux | grep [m]yscript)
This does not create a subshell. So, myscript.sh has only one process running:
ps aux | grep [m]yscript
Demonstration
Let's modify the script slightly so that the process and subprocess PIDs are saved in the variable line:
$ cat myscript.sh
# This is myscript.sh
line=$(ps aux | grep [m]yscript; echo $$ $BASHPID)
echo "'$line'"
ps aux | grep [m]yscript
In a bash script, $$ is the PID of the script and is unchanged in subshells. By contrast, when a subshell is entered, bash updates $BASHPID with the PID of the subshell.
Here is the output:
$ bash myscript.sh
'john1024 30226 0.0 0.0 13280 2884 pts/22 S+ 18:50 0:00 bash myscript.sh
john1024 30227 0.0 0.0 13284 1824 pts/22 S+ 18:50 0:00 bash myscript.sh
30226 30227'
john1024 30226 0.0 0.0 13284 3196 pts/22 S+ 18:50 0:00 bash myscript.sh
In this case, 30226 is the PID on the main script and 30227 is the PID of the subshell running ps aux | grep [m]yscript.
a command substitution ($(...))
each segment of a pipeline[1]
cause Bash to create a subshell (a child process created by forking the current shell process), but then Bash optimizes away subshells if they result in a single call to an external utility.
(What I think is happening in the optimization scenario is that a subshell is actually created but then instantly replaced by the external utility's process, via something like exec. Do let me know if you know for sure.)
Applied to your example:
line=$(ps aux | grep [m]yscript) creates 3 child processes:
1 subshell - the fork of your script you see as an additional match returned by grep.
2 child processes (1 for each pipeline segment) - ps and grep; they take the place of the optimized-away subshells; their parent process is the 1 remaining subshell created by the command substitution.
ps aux | grep [m]yscript creates 2 child processes (1 for each pipeline segment):
ps and grep; they take the place of the optimized-away subshells; their parent process is the current shell.
For an overview of the scenarios in which a subshell is created in Bash, see this answer of mine, which, however, doesn't cover the optimizing-away scenarios.
[1] In Bash v4.2+ you can set option lastpipe (off by default) in order to make the last pipeline segment run in the current shell instead of a subshell; aside from a slight efficiency gain, this allows you to declare variables in the last segment that the current shell can see after the pipeline exits.
When I check list of processes and 'grep' out those that are interesting for me, the grep itself is also included in the results. For example, to list terminals:
$ ps aux | grep terminal
user 2064 0.0 0.6 181452 26460 ? Sl Feb13 5:41 gnome-terminal --working-directory=..
user 2979 0.0 0.0 4192 796 pts/3 S+ 11:07 0:00 grep --color=auto terminal
Normally I use ps aux | grep something | grep -v grep to get rid of the last entry... but it is not elegant :)
Do you have a more elegant hack to solve this issue (apart of wrapping all the command into a separate script, which is also not bad)
The usual technique is this:
ps aux | egrep '[t]erminal'
This will match lines containing terminal, which egrep '[t]erminal' does not! It also works on many flavours of Unix.
Use pgrep. It's more reliable.
This answer builds upon a prior pgrep answer. It also builds upon another answer combining the use of ps with pgrep. Here are some pertinent training examples:
$ pgrep -lf sshd
1902 sshd
$ pgrep -f sshd
1902
$ ps up $(pgrep -f sshd)
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1902 0.0 0.1 82560 3580 ? Ss Oct20 0:00 /usr/sbin/sshd -D
$ ps up $(pgrep -f sshddd)
error: list of process IDs must follow p
[stderr output truncated]
$ ps up $(pgrep -f sshddd) 2>&-
[no output]
The above can be used as a function:
$ psgrep() { ps up $(pgrep -f $#) 2>&-; }
$ psgrep sshd
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1902 0.0 0.1 82560 3580 ? Ss Oct20 0:00 /usr/sbin/sshd -D
Compare with using ps with grep. The useful header row is not printed:
$ ps aux | grep [s]shd
root 1902 0.0 0.1 82560 3580 ? Ss Oct20 0:00 /usr/sbin/sshd -D
You can filter in the ps command, e.g.
ps u -C gnome-terminal
(or search through /proc with find etc.)
One more alternative:
ps -fC terminal
Here the options:
-f does full-format listing. This option can be combined
with many other UNIX-style options to add additional
columns. It also causes the command arguments to be
printed. When used with -L, the NLWP (number of
threads) and LWP (thread ID) columns will be added. See
the c option, the format keyword args, and the format
keyword comm.
-C cmdlist Select by command name.
This selects the processes whose executable name is
given in cmdlist.
Disclaimer: I'm the author of this tool, but...
I'd use px:
~ $ px atom
PID COMMAND USERNAME CPU RAM COMMANDLINE
14321 crashpad_handler walles 0.01s 0% /Users/walles/Downloads/Atom.app/Contents/Frameworks/Electron Framework.framework/Resources/crashpad_handler --database=
16575 crashpad_handler walles 0.01s 0% /Users/walles/Downloads/Atom.app/Contents/Frameworks/Electron Framework.framework/Resources/crashpad_handler --database=
16573 Atom Helper walles 0.5s 0% /Users/walles/Downloads/Atom.app/Contents/Frameworks/Atom Helper.app/Contents/MacOS/Atom Helper --type=gpu-process --cha
16569 Atom walles 2.84s 1% /Users/walles/Downloads/Atom.app/Contents/MacOS/Atom --executed-from=/Users/walles/src/goworkspace/src/github.com/github
16591 Atom Helper walles 7.96s 2% /Users/walles/Downloads/Atom.app/Contents/Frameworks/Atom Helper.app/Contents/MacOS/Atom Helper --type=renderer --no-san
Except for finding processes with a sensible command line interface it also does a lot of other useful things, more details on the project page.
Works on Linux and OS X, easily installed:
curl -Ls https://github.com/walles/px/raw/python/install.sh | bash
Using brackets to surround a character in the search pattern excludes the grep process since it doesn't contain the matching regex.
$ ps ax | grep 'syslogd'
16 ?? Ss 0:09.43 /usr/sbin/syslogd
18108 s001 S+ 0:00.00 grep syslogd
$ ps ax | grep '[s]yslogd'
16 ?? Ss 0:09.43 /usr/sbin/syslogd
$ ps ax | grep '[s]yslogd|grep'
16 ?? Ss 0:09.43 /usr/sbin/syslogd
18144 s001 S+ 0:00.00 grep [s]yslogd|grep
Depending on the ultimate use case, you often want to prefer Awk instead.
ps aux | awk '/[t]erminal/'
This is particularly true when you have something like
ps aux | grep '[t]erminal' | awk '{print $1}' # useless use of grep!
where obviously the regex can be factored into the Awk script trivially:
ps aux | awk '/[t]erminal/ { print $1 }'
But really, don't reinvent this yourself. pgrep and friends have been around for a long time and handle this entire problem space much better than most ad hoc reimplementations.
Another option is to edit your .bash_profile (or other file that you keep bash aliases in) to create a function that greps 'grep' out of the results.
function mygrep {
grep -v grep | grep --color=auto $1
}
alias grep='mygrep'
The grep -v grep has to be first otherwise your --color=auto won't work for some reason.
This works if you're using bash; if you're using a different shell YMMV.