Sed finding itself and throwing off script results - bash

The script:
#!/bin/bash
SERVICE="$1"
RESULT=`ps -a | sed -n /${SERVICE}/p`
MEM=$(ps aux | sort -rk +4 | grep $1 | grep -v grep | awk '{print $4}' | awk 'NR == 1')
if [ "${RESULT:-null}" = null ]; then
echo "$1 is NOT running"
else
echo "$MEM"
fi
if [ "$MEM" -ge 1 ]; then
mailx -s "Alert: server needs to be checked" me#admins.com
fi
The problem with the ps output piped to sed is even if no process is running it finds itself:
ps aux | sed -n /nfdump/p
root 30724 0.0 0.0 105252 884 pts/1 S+ 14:16 0:00 sed -n /process/p
and them my script bypasses the expected result of "service is not running" and goes straight to echo'ing $MEM, which in this case will be 0.0. Does sed have a grep -v grep eqivalent to get itself out of the way?

Let me add one more example in addition to my comments above. Sed has indeed an equivalent to grep -v: You can negate a match with /RE/! (append a ! to the regex) thus you can try:
ps aux | sed -n '/sed/!{ /nfdump/ p;}'
Here the part inside { ... } is only applied to lines not matching sed.
For the record: there is a command pgrep that can replace your ps sed pipeline, see pgrep in Wikipedia or its manpage.

Try :
ps aux | sed -n '/[n]fdump/p'
or :
ps aux | grep '[n]fdump'
The regex won't be find in the processes list

Related

Bash if -gt also triggered when values equal?

I want my script to check if it's already running in another instance:
$ cat test.sh
#!/bin/bash
ps -ef | grep -v grep | grep -i "test.sh" | grep bash
ps -ef | grep -v grep | grep -i "test.sh" | grep -c bash
if [ `ps -ef | grep -v grep | grep -i "test.sh" | grep -c bash` -gt 1 ]; then echo "There's another instance running."
else echo "Only this instance is running."
fi
However the output is
$ ./test.sh
noes 9503 7494 0 09:32 pts/1 00:00:00 /bin/bash ./test.sh
1
There's another instance running.
Clearly, 1 is not greater than 1, so why is the if condition triggered?
Thanks
From test man page:
INTEGER1 -gt INTEGER2
INTEGER1 is greater than INTEGER2
So the answer is no, -gt is not triggered when values are equal. In fact, as you can see if you modify the script in this way:
$ cat test.sh
#!/bin/bash
ps -ef | grep -v grep | grep -i "test.sh" | grep bash
ps -ef | grep -v grep | grep -i "test.sh" | grep -c bash
STRINGS=`ps -ef | grep -v grep | grep -i "test.sh"`
echo "$STRINGS"
COUNT=`ps -ef | grep -v grep | grep -i "test.sh" | grep -c bash`
echo $COUNT
if [ `ps -ef | grep -v grep | grep -i "test.sh" | grep -c bash` -gt 1 ]; then echo "There's another instance running."
else echo "Only this instance is running."
fi
You get this:
$ ./test.sh
lucio 5097 4736 0 10:10 pts/2 00:00:00 /bin/bash ./test.sh
1
lucio 5097 4736 0 10:10 pts/2 00:00:00 /bin/bash ./test.sh
lucio 5106 5097 0 10:10 pts/2 00:00:00 /bin/bash ./test.sh
2
There's another instance running.
If you modify the script in this way, it will work:
#!/bin/bash
ps -ef | grep -v grep | grep -i "test.sh" | grep bash
pgrep -c test.sh
if [ $(pgrep -c test.sh) -gt 1 ]; then
echo "There's another instance running."
else
echo "Only this instance is running."
fi
This is the output:
$ ./test.sh
lucio 5197 4736 0 10:17 pts/2 00:00:00 /bin/bash ./test.sh
1
Only this instance is running.
Note the use $() instead of backticks. Check this answer for this change.

Bash outputting kill usage

I have a bash script as follows:
if [[ "$1" == "stop" ]]; then
echo "[$(date +'%d/%m/%Y %H:%M:%S:%s')]: Killing all active watchers" >> $LOG
kill -9 $(ps -ef | grep "processname1" | grep -v "grep" | grep -v "$$" | awk
'{print $2}' | xargs)
echo "[$(date +'%d/%m/%Y %H:%M:%S:%s')]: Killing all current processname2
processes" >> $LOG
kill -9 $(ps -ef | grep "processname2" | grep -v "grep" | awk '{print $2}' |
xargs)
exit 0
when i run 'x service stop', the following is outputted:
kill: usage: kill [-s sigspec | -n signum | -sigspec] pid | jobspec ... or kill
-l [sigspec]
Killed
How do i stop the kill usage being displayed? It is successfully killing the process, however the fact that the usage is displayed is causing AWS CodeDeploy to fail.
Thanks!
Adam, please note that this is really just a comment with formatting. Don't take this as a real answer to your question. Please focus on the constructive comments to your question.
In my mis-spent youth, I wrote this bash function to do the ps -ef | grep .... madness:
# ps-grep
psg() {
local -a patterns=()
(( $# == 0 )) && set -- $USER
for arg do
patterns+=("-e" "[${arg:0:1}]${arg:1}")
done
ps -ef | grep "${patterns[#]}"
}
using the knowledge that the pattern [p]rocessname will not match the string [p]rocessname

bash fork subprocess with command link $(cmd1|cmd2)

Question
I wrote some command lines like output=$(cmd1|cmd2) in a bash script file (aaaa.sh) and found a subprocess generated.
code in the file aaaa.sh
echo "The name of this file is $(basename $0)."
echo "The pid of this program is $$."
echo -e '\n---- 0 ----'
echo -e 'CMD: ps -ef | grep "aaaa.sh" | grep -v grep'
ps -ef | grep "aaaa.sh" | grep -v grep
echo -e '\n---- 1 ----'
echo -e 'CMD: ps -ef | grep "aaaa.sh" | grep -v "bbbb" | grep -v grep'
ps -ef | grep "aaaa.sh" | grep -v "bbbb" | grep -v grep
echo -e '\n---- 2 ----'
echo -e 'CMD: pgrep -a "aaaa.sh" | grep -v "bbbb"'
pgrep -a "aaaa.sh" | grep -v "bbbb"
echo -e '\n---- 3 ----'
echo -e 'CMD: pgrep -a "aaaa.sh" | grep -v "$$"'
pgrep -a "aaaa.sh" | grep -v "$$"
echo -e '\n---- 4 ----'
echo -e 'CMD: output=$(ps -ef | grep "aaaa.sh" | grep -v "bbbb" | grep -v grep)'
output=$(ps -ef | grep "aaaa.sh" | grep -v "bbbb" | grep -v grep)
echo -e "$output"
echo -e '\n---- 5 ----'
echo -e 'CMD: output=$(ps -ef | grep "aaaa.sh" | grep -v "$$" | grep -v grep)'
output=$(ps -ef | grep "aaaa.sh" | grep -v "$$" | grep -v grep)
echo -e "$output"
echo -e '\n---- 6 ----'
echo -e 'CMD: output=$(pgrep -a "aaaa.sh" | grep -v "bbbb")'
output=$(pgrep -a "aaaa.sh" | grep -v "bbbb")
echo -e "$output"
echo -e '\n---- 7 ----'
echo -e 'CMD: output=$(pgrep -a "aaaa.sh" | grep -v "$$")'
output=$(pgrep -a "aaaa.sh" | grep -v "$$")
echo -e "$output"
echo -e '\n---- 8 ----'
echo -e 'CMD: output=$(pgrep -a "aaaa.sh")'
output=$(pgrep -a "aaaa.sh")
echo -e "$output"
output
output
Question
There is a subprocess generated in 4,5,6 and 7. why?
Shells typically execute command substitution ($()) and command piping (|) in subshells.
output=$(ps -ef | grep "aaaa.sh" | grep -v "$$" | grep -v grep)
This statement actually results in the creation of five processes--one for the command substitution subshell, and one more for each of the commands in the pipeline.
From the bash man page:
Each command in a pipeline is executed as a separate process (i.e., in a subshell).
Edit - To try this out for yourself run the following:
$ echo $BASHPID >&2 | echo $BASHPID >&2 | echo $BASHPID
We can see a different PID for each subshell in the pipeline.
That's probably because of
$(...)
Quote from bash man page:
Command substitution allows the output of a command to replace the command name. There are two forms:
$(command)
or
`command`.
Bash performs the expansion by executing command in a subshell environment and replacing the command substitution with the standard output of the command, with any trailing newlines deleted. Embedded newlines are not deleted, but they may be removed during
word splitting.
So, you actually see the subshell created to execute your commands

What is wrong in this Shell script?

I keep getting unary operator expected. It seems PRC is not being assigned the value.
PRC=`ps -ef | grep test | wc -l`
if [ ${PRC} -eq 1 ]
then
echo "Congrats"
Note that ps -ef | grep test will usually include the grep process in the output, which you probably don't want. A "clever trick" to avoid this is to match for the string "test" with a regular expression that is not the simple string "test":
$ ps -ef | grep test
jackman 27787 24572 0 09:53 pts/2 00:00:00 grep --color=auto test
$ ps -ef | grep test | wc -l
1
versus
$ ps -ef | grep '[t]est'
(no output)
$ ps -ef | grep '[t]est' | wc -l
0
I do this often enough that I wrote this bash function psg (for "ps grep"):
psg () {
local -a patterns=()
(( $# == 0 )) && set -- $USER # no arguments? vanity search
for arg do
patterns+=("-e" "[${arg:0:1}]${arg:1}")
done
ps -ef | grep "${patterns[#]}"
}
You could also just use
pgrep -f test
Don't forget your closing "fi":
PRC=`ps -ef | grep test| wc -l`
if [ "${PRC}" -eq 1 ]
then
echo "Congrats"
fi
You didn't mention what shell, but this works in bash.
Save a process by using the -c (count) option to grep:
PRC=`ps -ef | grep -c test`
Be advised your pipeline includes in the count the grep command itself, so as you mentioned in your comment above your count is most likely misleading as it is only counting itself. Instead, use this:
PRC=`ps -ef | grep -c [t]est`
This will match commands with "test" in them but not the grep command itself. This is because this is using a regular expression that matches a word starting with a "t". Your command starts with a square bracket so it won't match itself. Resist doing a "grep test | grep -v grep" which is sloppy and just unnecessarily uses a process.

Shell Script to find PID of ssh and kill the PID if present

I am trying to write a script to find a reverse SSH PID and kill it if present. I am stuck on "awk" as it gives error. below is the script:
a=('ps -aef | grep "ssh -fN" | grep -v grep | awk '{ print $2 }'')
if [ -n "$a" ]
then
echo "String \"$a\" is not null."
kill -9 "$a"
fi
I commented out if, then, kill and fi lines to debug the script. I get following error:
String "ps -aef | grep "ssh -fN" | grep -v grep | awk {" is not null.
I believe parenthesis for awk is creating the problem and I am unable to get a workaround for this. On Command line, this works perfectly and returns the correct PID.
ps -aef | grep "ssh -fN" | grep -v grep | (awk '{ print $2 }'
Once the PID is passed on to variable "a", I need to issue kill command. OS is Centos 6.4
P.S: I am not fluent on scripting but trying to achieve an objective. Help will be highly appreciated!
There are multiple problems with your script.
You need command substitution to store the output of ps pipeline into an array.
You need to check for the number of elements in the array.
Refer to the array instead of the variable.
The following might work for you:
pids=( $(ps -ef | grep '[s]sh -fN' | awk '{print $2}') )
if [ "${#pids[#]}" -gt 0 ]; then
kill -9 "${pids[#]}";
fi
First, if you have grep and then awk, you can get rid of the greps:
ps -aef | grep "ssh -fN" | grep -v grep | awk '{ print $2 }'
ps -aef |awk ' { if ( ($0 ~ /ssh -FN/) && (! $0 ~ /grep/) ) { print $2 } }'
However, instead of using ps, use pgrep.
pgrep -f "ssh -[fN][fN]" # Will match against either 'ssh -fN' or 'ssh -Nf'
There is even a pkill that will do the entire command for you:
pkill -f "ssh -[fN][fN]"
That will find all of the processes that match that particular string and kill them (if they exist).

Resources