bash execute command in variable - linux

I have a command in a variable in Bash:
check_ifrunning=\`ps aux | grep "programmname" | grep -v "grep" | wc -l\`
The command checks if a specific program is running at the moment.
Later in my script, I want to query the value of the variable on a point.
If the specific program is running, the script should sleep for 15 minutes.
I solved it like this:
while [ $check_ifrunning -eq 1 ]; do
sleep 300
done
Will the script execute the command in the variable for each single loop-run or will the value in the variable stay after the first execution?
I have more variables in my script which can change their value. This was just one simple example of this.

Notice that check_ifrunning is set only once, in
check_ifrunning=`ps aux | grep "programmname" | grep -v "grep" | wc -l`
and that it is set before the loop:
while [ $check_ifrunning -eq 1 ]; do
sleep 300
done
You could add, for debugging purposes, an echo check_ifrunning is $check_ifrunning statement inside your while loop just before the sleep ...
You probably simply want (using pidof(8)) - without defining or using any check_ifrunning Bash variable:
while [ -n "$(pidof programname)" ]; do
sleep 300
done
Because you want to test if programname is running at every start of the loop!
You should use the more nestable and more readable $(...) instead of backquotes.
Consider reading the Advanced Bash Scripting Guide...
If you are writing a Bash script, consider to start it with
#!/bin/bash -vx
while debugging. When you are satisfied, remove the -vx...

If you want to encapsulate your commands, the proper way to do that is a function.
running () {
ps aux | grep "$1" | grep -q -v grep
}
With grep -q you get the result as the exit code, not as output; you use it simply like
if running "$programname"; then
:
Ideally, the second grep is unnecessary, but I did not want to complicate the code too much. It still won't work correctly if you are looking for grep. The proper solution is pidof.
See also http://mywiki.wooledge.org/BashFAQ/050

Related

How to detect if a bash script is already running, considering its arguments [duplicate]

This question already has answers here:
Quick-and-dirty way to ensure only one instance of a shell script is running at a time
(43 answers)
What is the best way to ensure only one instance of a Bash script is running? [duplicate]
(14 answers)
Closed 1 year ago.
Sorry for my poor english ;)
I need to check if a script is already running or not. I don't want to use a lock file, as it can be tricky (ie: if my script wrote a lock file, but crashed, I will consider it as running).
I also need to take parameters into account. ie:
test.sh 123
should be considered as a different process than
test.sh 456
I tried this :
#!/bin/bash
echo "inside test.sh, script name with arguments: $0 +$*$"
echo " simple pgrep on script name with arguments:"
pgrep -f "$0 +$*$"
echo " counting simple pgrep on script name with arguments with wc -l"
echo $(pgrep -f "$0 +$*$" | wc -l)
echo " counting pgrep echo result with wc -w"
processes=$(pgrep -f "$0 +$*$")
nbProcesses=$(echo $processes | wc -w)
echo $nbProcesses
sleep 300
When I try, I get this result:
[frederic.charrier#charrier tmp]$ /tmp/test.sh 123
inside test.sh, script name with arguments: /tmp/test.sh +123$
simple pgrep on script name with arguments:
123976
counting simple pgrep on script name with arguments with wc -l
2
counting pgrep echo result with wc -w
1
^Z
[1]+ Stoppé /tmp/test.sh 123
[frederic.charrier#charrier tmp]$ /tmp/test.sh 123
inside test.sh, script name with arguments: /tmp/test.sh +123$
simple pgrep on script name with arguments:
123976
124029
counting simple pgrep on script name with arguments with wc -l
3
counting pgrep echo result with wc -w
2
My questions are:
when I run the script the first time, it's running once. So pgrep is returning only one result: 123976, which is fine. But why a "wc -l" on 123976 is returning 2?
when I run the script a second time, I get the same strange behavior: pgrep returns the correct result, pgrep | wc -l returns something wrong, and "echo pgrep ... | wc -w" returns the correct result. Why?
How to detect if a bash script is already running
If you are aware of the drawbacks of your method, using pgrep looks fine. Note that both $0 and $* can have regex-syntax stuff in them, you have to escape them first, and I think I would also do pgrep -f "^$0... to match it from the beginning.
why a "wc -l" on 123976 is returning 2?
Because command substitution $(..) spawns a subshell, so there are two shells running, when pgrep is executed.
Overall, echo $(cmd) is an antipattern. Just run it cmd.
In some cases, like when there is single one command inside command substitution, bash optimizes and replaces (exec) the subshell with the command itself, effectively eliminating the subshell. This is an optimization. That's why processes=$(pgrep ..) returns 1.
Why?
There is one more process running.

Bash: if statement always succeeding

I have the following if statement to check if a service, newrelic-daemon in this case, is running...
if [ $(ps -ef | grep -v grep | grep newrelic-daemon | wc -l) > 0 ]; then
echo "New Relic is already running."
The problem is it's always returning as true, i.e. "New Relic is already running". Even though when I run the if condition separately...
ps -ef | grep -v grep | grep newrelic-daemon | wc -l
... it returns 0. I expect it to do nothing here as the value returned is =0 but my IF condition says >0.
Am I overlooking something here?
You are trying to do a numeric comparison in [...] with >. That doesn't work; to compare values as numbers, use -gt instead:
if [ "$(ps -ef | grep -v grep | grep -c newrelic-daemon)" -gt 0 ]; then
The quotation marks around the command expansion prevent a syntax error if something goes horribly wrong (e.g. $PATH set wrong and the shell can't find grep). Since you tagged this bash specifically, you could also just use [[...]] instead of [...] and do without the quotes.
As another Bash-specific option, you could use ((...)) instead of either form of square brackets. This version is more likely to generate a syntax error if anything goes wrong (as the arithmetic expression syntax really wants all arguments to be numbers), but it lets you use the more natural comparison operators:
if (( "$(ps -ef | grep -v grep | grep -c newrelic-daemon)" > 0 )); then
In both cases I used grep -c instead of grep | wc -l; that way I avoided an extra process and a bunch of interprocess I/O just so wc can count lines that grep is already enumerating.
But since you're just checking to see if there are any matches at all, you don't need to do either of those; the last grep will exit with a true status if it finds anything and false if it doesn't, so you can just do this:
if ps -ef | grep -v grep | grep -q newrelic-daemon; then
(The -q keeps grep from actually printing out the matching lines.)
Also, if the process name you're looking for is a literal string instead of a variable, my favorite trick for this task is to modify that string like this, instead of piping through an extra grep -v grep:
if ps -ef | grep -q 'newrelic[-]daemon'; then
You can pick any character to put the square brackets around; the point is to create a regular expression pattern that matches the target process name but doesn't match the pattern itself, so the grep process doesn't find its own ps line.
Finally, since you tagged this linux, note that most Linux distros ship with a combination ps + grep command called pgrep, which does this for you without your having to build a pipeline:
if pgrep newrelic-daemon >/dev/null; then
(The MacOS/BSD version of pgrep accepts a -q option like grep, which would let you do without the >/dev/null redirect, but the versions I've found on Linux systems don't seem to have that option.)
There's also pidof; I haven't yet encountered a system that had pidof without pgrep, but should you come across one, you can use it the same way:
if pidof newrelic-daemon >/dev/null; then
Other answers have given you more details. I would do what you are trying to do with:
if pidof newrelic-daemon >/dev/null; then
echo "New Relic is already running."
fi
or even
pidof newrelic-daemon >/dev/null && echo "New Relic is already running."
If you want to compare integers with test you have to use the -gt option. See:
man test
or
man [
#Stephen: Try(change [ to [[ into your code along with fi which will complete the if block completely):
if [[ $(ps -ef | grep -v grep | grep newrelic-daemon | wc -l) > 0 ]]; then
echo "New Relic is already running."
fi

Calling a shell script that is stored in another shell script variabl

I searched SO but could not find any relevant post with this specific problem. I would like to know how to call a shell script which is stored in a variable of another shell script.
In the below script I am trying to read service name & corresponding shellscript, check if the service is running, if not, start the service using the shell script associated with that service name. tried multiple options shared in various forums(like 'eval' etc) with no luck. please help to provide your suggestions on this.
checker.sh
#!/bin/sh
while read service
do
servicename=`echo $service | cut -d: -f1`
servicestartcommand=`echo $service | rev | cut -d: -f1 | rev`
if (( $(ps -ef | grep -v grep | grep $servicename | wc -l) > 0 ))
then
echo "$servicename Running"
else
echo "!!$servicename!! Not Running, calling $servicestartcommand"
eval "$servicestartcommand"
fi
done < names.txt
Names.txt
WebSphere:\opt\software\WebSphere\startServer.sh
WebLogic:\opt\software\WebLogic\startWeblogic.sh
Your script can be refactored into this:
#!/bin/bash
while IFS=: read -r servicename servicestartcommand; do
if ps cax | grep -q "$servicename"; then
echo "$servicename Running"
else
echo "!!$servicename!! Not Running, calling $servicestartcommand"
$servicestartcommand
fi
done < names.txt
No need to use wc -l after grep's output as you can use grep -q
No need to use read full line and then use cut, rev etc later. You can use IFS=: and read the line into 2 separate variables
No need to use eval in the end
It is much simpler than you expect. Instead of:
eval "$servicestartcommand"
eval should only be used in extreme circumstances. All you need is
$servicestartcommand
Note: no quotes.
As an example, try this on the command-line:
cmd='ls -l'
$cmd
That should work. But:
"$cmd"
will fail. It will look for a program with a space in its name called 'ls -l'.
May be I don't get the idea, but why not use system variables?
export FOO=bar
echo $FOO
bar

struck in the logic

I need to write a shell script to see if the ADM Process is down we need to get an alert via email,I have done that below the script,it is working fine if we have installed one ADM server on one machine,But my requirement is i have installed 3 ADM servers on one machine not sure how i can write the shell script to achieve this requirement.
script:-
export ADM =`ps -ef | grep Adm | grep -v grep | wc -l`
if [ $ADM == 0 ];
then
echo "AdmServer is down on Dev $hostname" | mail -s xxxx.gmail.com
fi
Please help me out,
Thanks,
Instead of checking whether $ADM is 0, check whether it's not 3.
ADM=`ps -ef | grep -c '[A]dm`
if [ $ADM -ne 3 ]
then
echo "AdmServer is down on Dev $hostname" | mail -s xxxx.gmail.com
fi
The -c option to grep returns the count of matches, so you don't need to use wc -l. And putting one of the characters in brackets is a trick for parsing ps output so you don't need grep -v grep (because now the argument to grep is no longer the same as the process name you're searching for). And you should use -eq and -ne for comparing numbers; == and != are for comparing strings.
There's no need to use export when assigning a variable that will just be used in that shell script. export is for creating environment variables, which are inherited by child processes.

How to get watch to run a bash script with quotes

I'm trying to have a lightweight memory profiler for the matlab jobs that are run on my machine. There is either one or zero matlab job instance, but its process id changes frequently (since it is actually called by another script).
So here is the bash script that I put together to log memory usage:
#!/bin/bash
pid=`ps aux | grep '[M]ATLAB' | awk '{print $2}'`
if [[ -n $pid ]]
then
\grep VmSize /proc/$pid/status
else
echo "no pid"
fi
when I run this script in bash like this:
./script.sh
it works fine, giving me the following result:
VmSize: 1289004 kB
which is exactly what I want.
Now, I want to run this periodically. So I run it with watch, like this:
watch ./script.sh
But in this case I only receive:
no pid
Please note that I know the matlab job is still running, because I can see it with the same pid on top, and besides, I know each matlab job take several hours to finish.
I'm pretty sure that something is wrong with the quotes I have when setting pid. I just can't figure out how to fix it. Anyone knows what I'm doing wrong?
PS.
In the man page of watch, it says that commands are executed by sh -c. I did run my script like sh -c ./script and it works just fine, but watch doesn't.
Why don't you use a loop with sleep command instead?
For example:
#!/bin/bash
pid=`ps aux | grep '[M]ATLAB' | awk '{print $2}'`
while [ "1" ]
do
if [[ -n $pid ]]
then
\grep VmSize /proc/$pid/status
else
echo "no pid"
fi
sleep 10
done
Here the script sleeps(waits) for 10 seconds. You can set the interval you need changing the sleep command. For example to make the script sleep for an hour use sleep 1h.
To exit the script press Ctrl - C
This
pid=`ps aux | grep '[M]ATLAB' | awk '{print $2}'`
could be changed to:
pid=$(pidof MATLAB)
I have no idea why it's not working in watch but you could use a cron job and make the script log to a file like so:
#!/bin/bash
pid=$(pidof MATLAB) # Just to follow previously given advice :)
if [[ -n $pid ]]
then
echo "$(date): $(\grep VmSize /proc/$pid/status)" >> logfile
else
echo "$(date): no pid" >> logfile
fi
You'd of course have to create logfile with touch.
You might try just running ps command in watch. I have had issues in the past with watch chopping lines and such when they get too long.
It can be fixed by making the terminal you are running the command from wider or changing the column like this (may need to adjust the 160 to your liking):
export COLUMNS=160;

Resources