What is wrong in this Shell script? - shell

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.

Related

Prevent grep from exiting in case of nomatch

Prevent grep returning an error when input doesn't match. I would like it to keep running and not exit with exit code: 1
set -euo pipefail
numbr_match=$(find logs/log_proj | grep "$name" | wc -l);
How could I solve this?
In this individual case, you should probably use
find logs/log_proj -name "*$name*" | wc -l
More generally, you can run grep in a subshell and trap the error.
find logs/log_proj | ( grep "$name" || true) | wc -l
... though of course grep | wc -l is separately an antipattern;
find logs/log_proj | grep -c "$name" || true
I don't know why you are using -e and pipefail when you don't want to have this behaviour. If your goal is just to treat exit code 2 (by grep) as error, but exit code 1 as no-error, you could write a wrapper script around grep, which you
call instead of grep:
#!/bin/bash
# This script behaves exactly like grep, only
# that it returns exit code 0 if there are no
# matching lines
grep "$#"
rc=$?
exit $((rc == 1 ? 0 : rc))

ps -ef | grep * command not working properly

I have simple unix shell script as follows. Which is giving different count for a service
#!/bin/bash
service=$1
ps -ef | grep $service | grep -v "grep" | wc -l
PROCESS_NUM=$(ps -ef | grep $service | grep -v "grep"| wc -l)
echo $PROCESS_NUM
In the above code below line gives output of 2.
ps -ef | grep $service | grep -v "grep" | wc -l
But when same line is assigned to variable as code below its giving output as 3.
PROCESS_NUM=$(ps -ef | grep $service | grep -v "grep"| wc -l)
echo $PROCESS_NUM
Why this is getting increased by 1 and how to tackle it.
You can see what is happening if the script tees the output to a file before counting the lines and then displaying the output after:
#!/bin/bash
service=$1
echo Directly in Script:
ps -ef | grep $service | grep -v grep | tee test.txt | wc -l
cat test.txt
echo Inside Subshell:
RESULT=$(ps -ef | grep $service | grep -v grep | tee test.txt | wc -l)
echo $RESULT
cat test.txt
When the output of a command is captured, bash starts another shell to run the command - but that subshell also shows up in the process list.
When I run that script I get:
$ ./test.sh lca
Directly in Script:
2
gcti 4268 1 0 2018 ? 21:59:03 ./lca 4999
t816826 9159 7009 0 09:22 pts/1 00:00:00 /bin/bash ./test.sh lca
Inside Subshell:
3
gcti 4268 1 0 2018 ? 21:59:03 ./lca 4999
t816826 9159 7009 0 09:22 pts/1 00:00:00 /bin/bash ./test.sh lca
t816826 9166 9159 0 09:22 pts/1 00:00:00 /bin/bash ./test.sh lca

Sed finding itself and throwing off script results

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

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).

Prevent grep returning an error when input doesn't match

I want to write in a bash script a piece of code that checks if a program is already running.
I have the following in order to search whether bar is running
foo=`ps -ef | grep bar | grep -v grep`
The
grep -v grep
part is to ensure that the "grep bar" is not taken into account in ps results
When bar isn't running, foo is correctly empty. But my problem lies in the fact tha the script has
set -e
which is a flag to terminate the script if some command returns an error.
It turns out that when bar isn't running, "grep -v grep" doesn't match with anything and grep returns an error. I tried using -q or -s but to no avail.
Is there any solution to that? Thx
Sure:
ps -ef | grep bar | { grep -v grep || true; }
Or even:
ps -ef | grep bar | grep -v grep | cat
Short answer
Write
ps -ef | grep bar | { grep -v grep || test $? = 1; }
if you are using set -e.
If you use bash's pipefail option (set -o pipefail), remember to apply the exception handling (||test) to every grep in the pipeline:
ps -ef | { grep bar || test $? = 1; } | { grep -v grep || test $? = 1; }
In shell scripts I suggest you to use the ”catch-1-grep“ (c1grep) utility function:
c1grep() { grep "$#" || test $? = 1; }
Explained
grep's exit status is either 0, 1 or 2: [1]
0 means a line is selected
1 means no lines were selected
2 means an error occurred
grep can also return other codes if it's interrupted by a signal (e.g. 130 for SIGINT).
Since we only want to ignore exit status 1, we use test to suppress that specific exit status.
If grep returns 0, test is not run.
If grep returns 1, test is run and returns 0.
If grep returns any other value, test is run and returns 1.
In the last case, the script will exit immediately due to set -e or set -o pipefail. However, if you don't care about grep errors at all, you can of course write
ps -ef | grep bar | { grep -v grep || true; }
as suggested by Sean.
[additional] usage in shell scripts
In shell scripts, if you are using grep a lot, I suggest you to define an utility function:
# "catch exit status 1" grep wrapper
c1grep() { grep "$#" || test $? = 1; }
This way your pipe will get short & simple again, without losing the features of set -e and set -o pipefail:
ps -ef | c1grep bar | c1grep -v grep
FYI:
I called it c1grep to emphasize it's simply catching exit status 1, nothing else.
I could have called the function grep instead (grep() { env grep "$#" ...; }), but I prefer a less confusing and more explicit name, c1grep.
[additional] ps + grep
So if you want to know how to avoid grep -v grep or even the | grep part of ps|grep at all, take a look at some of the other answers; but this is somewhat off-topic imho.
[1] grep manpage
A good trick to avoid grep -v grep is this:
ps -ef | grep '[b]ar'
That regular expression only matches the string "bar". However in the ps output, the string "bar" does not appear with the grep process.
In the days before I learned about pgrep, I wrote this function to automate the above command:
psg () {
local -a patterns=()
(( $# == 0 )) && set -- $USER
for arg do
patterns+=("-e" "[${arg:0:1}]${arg:1}")
done
ps -ef | grep "${patterns[#]}"
}
Then,
psg foo bar
turns into
ps -ef | grep -e '[f]oo' -e '[b]ar'
Why ask ps to provide massive amounts of output with -ef if you only are going to throw away 99% of it? ps and especially the GNU version is a swiss army knife of handy functionality. Try this:
ps -C bar -o pid= 1>/dev/null
I specify -o pid= here just because, but in fact it's pointless since we throw away all of stdout anyway. It would be useful if you wanted to know the actual running PID, though.
ps automatically will return with a non-zero exist status if -C fails to match anything and with zero if it matches. So you could simply say this
ps -C bar 1>/dev/null && echo bar running || echo bar not running
Or
if ps -C bar 1>/dev/null ; then
echo bar running
else
echo bar not running
fi
Isn't that simpler? No need for grep, not twice or even once.
foo=`ps -ef | grep bar | grep -v grep` || true
Try to make so:
ps auxw | grep -v grep | cat
cat returns always 0 and ignores exit code of grep

Resources