When launching script in background I get two processes running - linux

I came across a weird scenario that is stumping me.
I have a script that I launch in the background with an &
example:
root## some_script.sh &
After running it, I do a ps -ef | grep some_script and I see TWO processes running where the 2nd process keeps getting an different PID but it's Parent is the process that I started (like the parent process is spawning children that die off - but this was never written in the code).
example:
root## ps -ef | grep some_script.sh
root 4696 17882 0 13:30 pts/2 00:00:00 /bin/bash ./some_script.sh
root 4778 4696 0 13:30 pts/2 00:00:00 /bin/bash ./some_script.sh
root## ps -ef | grep some_script.sh
root 4696 17882 0 13:30 pts/2 00:00:00 /bin/bash ./some_script.sh
root 4989 4696 0 13:30 pts/2 00:00:00 /bin/bash ./some_script.sh
What gives here? It seems to be messing up the output and functionality of the script too and basically makes it a never ending process (when I have a defined start and stop in the script).
the script:
`
#! /bin/bash
# Set Global Variables
LOGDIR="/srv/script_logs"
OUTDIR="/srv/audits"
BUCKET_LS=$OUTDIR"/LSOUT_"$i"_"$(date +%d%b%Y)".TXT"
MYCMD1="aws s3api list-objects --bucket viddler-flvs"
MYCMD2="--starting-token"
MAX_ITEMS="--max-items 10000"
MYSTARTING_TOKEN='""'
rm tokenlog.txt flv_out.txt
while [[ $MYSTARTING_TOKEN != "null" ]]
do
# First - Get the token for the next batch
CMD_PRE="$MYCMD1 $MAX_ITEMS $MYCMD2 $MYSTARTING_TOKEN"
MYSTARTING_TOKEN=($($CMD_PRE | jq -r .NextToken))
echo $MYSTARTING_TOKEN >> tokenlog.txt
# Now - get the values of the files for the existing batch
# First - re-run the batch and get the file values we want
MYOUT2=$($CMD_PRE | (jq ".Contents[] | {Key, Size, LastModified,StorageClass }"))
echo $MYOUT2 | sed 's/[{},"]//g;s/ /\n/g;s/StorageClass://g;s/LastModified://g;s/Size://g;s/Key://g;s/^ *//g;s/ *$//g' >> flv_out.txt
#echo $STARTING_TOKEN
done
`

I guess you have
(
some shell instructions
)
inside of your .sh
This syntax executes commands in the new process (but command line would be the same).

Related

Bash: Inline Execution returns Duplicate "Process". Why?

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.

running script through ssh fails when locally same script succeeds

I'm experiencing a very strange behavior. I have found what appears to be a work around, but I am hoping that someone can explain to me WHY I'm seeing this crazy behavior.
Highlevel of what I'm doing: I'd like to have a shell script to stop my process. I'd like it to be robust enough to kill one or more instances of the process I'm grepping for. I don't want it to fail if there's NO process running (meaning I want a 0 return code...not an empty arg list passed to the kill command)
What I'm seeing is that a script is behaving differently when invoked by passing a command through ssh than if that same script was executed locally. What is very strange is that by adding a seemingly arbitrary command to my ssh command, I'm able to get my script to execute properly and I DONT KNOW WHY!
The stop scipt (echo statments were there to help me debug - not part of real script)
echo "Stopping myProcess"
echo "-->ps aux | grep myProcess | grep -v grep"
pid=ps -ef | grep myProcess | grep -v grep | awk '{ print $2 }'
echo "Here: ${pid}"
if [[ ! -z $pid ]]; then
echo "Here2"
kill -9 $pid
else
echo "Here3"
echo "not stopping anything - no myProcess process running."
fi
echo "Here4"
exit 0
Result of local execution of script when NO processes is running:
Stopping myProcess
-->
Here:
Here3
not stopping anything - no myProcess running.
Here4
Result of execution of script from a different machine though the following command:
Command:
ssh eak0703#myServer 'source ${HOME}/.bash_profile;cd /usr/local/myprocess/bin/;./stop-myProcess'
Result:
Stopping myProcess
--> eak0703 2099 0.0 0.0 10728 1500 ? Ss 17:08 0:00 bash -c source ${HOME}/.bash_profile;cd /usr/local/myProcess/bin/;./stop-myProcess
eak0703 2100 0.0 0.0 10740 992 ? S 17:08 0:00 bash -c source ${HOME}/.bash_profile;cd /usr/local/myProcess/bin/;./stop-myProcess
eak0703 2101 0.0 0.0 10740 668 ? S 17:08 0:00 bash -c source ${HOME}/.bash_profile;cd /usr/local/myProcess/bin/;./stop-myProcess
Here: 2099
2100
2105
Here2
Notice: for some strange and unexplained to me reason there appear to be 3 invocations of my command. I also know that this command doesn't terminate with an exit code of 0. I am assuming this is because by the time the kill -9 is invoked, the process ids picked up by the grep are gone.
Now - here's the SAME ssh command with an extra "date | grep crap" thrown in:
Command:
ssh eak0703#myServer 'source ${HOME}/.bash_profile;cd /usr/local/myprocess/bin/;date | grep crap;./stop-myProcess'
Result:
Stopping myProcess
-->
Here:
Here3
not stopping anything - no myProcess running.
Here4
Putting "date | grep crap" fixes things. It appears that the magic is in the "|" (pipe) operator. So I am actually able to make this work with "anycommand | anyothercommand".
I can make it work - but how can I justify randomly leaving such a nugget in a bash script??? No one will ever know why this is there. Not even me! If anyone has encountered this please help!
Parsing ps to find a process is fragile and error prone. Your example is a nice illustration why:
An unrelated process (the bash process started by ssh) contains the process name as part of the command line, and is accidentally picked up by your ps parser.
The unrelated process is removed by your grep -v grep when you make the command line include the word "grep".
Instead, just use pgrep or pkill. These tools list/kill processes based on the executable name and are therefore far more robust than parsing ps.

kill one instance of ffserver out of multiple using specific configuration file

I have two instances of ffserver running both using different configuration file kept at different path.
I want to kill one of them using a script.
when i write following command :
ps -ef |grep ffs
it gives output :
root 6421 6394 0 18:47 pts/11 00:00:00 /root/bin/ffserver -f /root/newff/ffserver.conf
root 6575 6562 0 18:49 pts/11 00:00:02 /root/bin/ffserver -f /root/test/downloaded/complete/ffserver.conf
root 8453 3720 0 19:09 pts/11 00:00:00 grep ffs
Now i want to kill only one .
Is there any way to kill using command name like i can give command name with kill
pkill_like_command /root/bin/ffserver -f /root/newff/ffserver.conf
Please tell me how to do that
as simple pkill will not work.
There is an -f switch that works for pkill, so that matching considers the full command line. You can test without killing using pgrep instead. So the command line would be for example (tested on Debian with procps 1:3.2.8-9):
pkill -f "ffserver.*/root/newff/ffserver.conf"
without pkill:
kill $( ps -ef | grep "ffserver.*/root/newff/ffserver.conf" | awk '{ print $2 }' )

Cron job generate duplicate processess

My cron is like below:
$ crontab -l
0,15,30,45 * * * * /vas/app/check_cron/cronjob.sh 2>&1 > /vas/app/check_cron/cronjob.log; echo "Exit code: $?" >> /vas/app/check_cron/cronjob.log
$ more /vas/app/check_cron/cronjob.sh
#!/bin/sh
echo "starting script";
/usr/local/bin/rsync -r /vas/app/check_cron/cron1/ /vas/app/check_cron/cron2/
echo "completed running the script";
$ ls -l /usr/local/bin/rsync
-rwxr-xr-x 1 bin bin 411494 Oct 5 2011 /usr/local/bin/rsync
$ ls -l /vas/app/check_cron/cronjob.sh
-rwxr-xr-x 1 vas vas 153 May 14 12:28 /vas/app/check_cron/cronjob.sh
if i run it manually ... the script is running well.
$ /vas/app/check_cron/cronjob.sh 2>&1 > /vas/app/check_cron/cronjob.log; echo "Exit code: $?" >> /vas/app/check_cron/cronjob.log
if run by crontab, the cron generate double processes more than 30 in 24hours until i kill them manually.
$ ps -ef | grep cron | grep -v root | grep -v grep
vas 24157 24149 0 14:30:00 ? 0:00 /bin/sh /vas/app/check_cron/cronjob.sh
vas 24149 8579 0 14:30:00 ? 0:00 sh -c /vas/app/check_cron/cronjob.sh 2>&1 > /vas/app/check_cron/cronjob.log; ec
vas 24178 24166 0 14:30:00 ? 0:00 /usr/local/bin/rsync -r /vas/app/check_cron/cron1/ /vas/app/check_cron/cron2/
vas 24166 24157 0 14:30:00 ? 0:01 /usr/local/bin/rsync -r /vas/app/check_cron/cron1/ /vas/app/check_cron/cron2/
Please give me advice how to make running well and no processes still running in the system
and processes stop properly.
BR,
Noel
The output you provide seems normal, the first two processes is just /bin/sh running your cron script and the later two are the rsync processes.
It might be a permission issue if the crontab is not the same user as the one you use for testing, causing the script to take longer when run from cron. You can add -v, -vv, or even -vvv to the rsync command for increased output and then observe the cron email after each run.
One method to prevent multiple running instances of scripts is to use lock files of some sort, I find it easy to use mkdir for this purpose.
#!/bin/sh
LOCK="/tmp/$0.lock"
# If mkdir fails then the lock already exists
mkdir $LOCK > /dev/null 2>&1
[ $? -ne 0 ] && exit 0
# We clean up the lock when the script exists for any reason
trap "{ rmdir $LOCK ; exit 0 ; }" EXIT
echo "starting script";
/usr/local/bin/rsync -r /vas/app/check_cron/cron1/ /vas/app/check_cron/cron2/
echo "completed running the script";
Just make sure you have some kind of cleanup when the OS starts in case it doesn't clean up /tmp by itself. The lock might be left there if the script crashes, is killed or is running when the OS is rebooted.
Why do you worry? Is something not working? From the parent process ID's I can deduce that the shell (PID=24157) forks an rsync (24166), and the rsync forks another rsync (24178). Looks like that's just how rsync operates...
It's certainly not cron starting two rsync processes.
Instead of CRON, you might want to have a look at the Fat Controller
It works similarly to CRON but has various built-in strategies for managing cases where instances of the script you want to run would overlap.
For example, you could specify that the currently running instance is killed and a new one started, or you could specify a grace period in which the currently running instance has to finish before then terminating it and starting a new one. Alternatively, you can specify to wait indefinitely.
There are more examples and full documentation on the website:
http://fat-controller.sourceforge.net/

Inconsistency between perl grep and cli grep

I am doing the following:
#!/usr/bin/perl
use strict;
use warnings;
my $proc = `ps -ef|grep -c myscriptname`;
print $proc;
This prints 2 when I run it inside the script.
ps -ef|grep -c myscriptname on the command line just shows: 1
Why?
same for my $proc = qx/ps -ef|grep -c myscriptname/
UPDATE
To be clear I run this snippet from somerandomscript.pl
Update 2
Following the advice of edorqui I remove -c getting:
12013 15777 15776 0 14:11 pts/6 00:00:00 sh -c ps -ef | grep myscriptname
12013 15779 15777 0 14:11 pts/6 00:00:00 grep myscriptname Argument "12013 15777 15776 0 14:11 pts/6 00:00:00 sh -c ps..." isn't numeric in numeric gt (>) at somerandomscript.pl line 8
from inside the script
The ps -ef command is showing the grep itself.
To skip this behaviour, try grepping for a regex condition that does not match the grep itself:
ps -ef | grep myscript[n]ame
or whatever similar can make it:
ps -ef | grep myscriptnam[e]
Explanation
If you run a sleep command in the background:
$ sleep 100 &
[1] 9768
and then look for it with ps -ef:
$ ps -ef | grep sleep
me 9768 3673 0 14:00 pts/6 00:00:00 sleep 100
me 9771 3673 0 14:00 pts/6 00:00:00 grep --color=auto sleep
You get two lines: the process itself and the grep command looking for it. To avoid it and show just the process itselves, we can either:
$ ps -ef | grep -v grep | grep sleep
or use a regex in the code so that the grep process is not matched:
$ ps -ef | grep slee[p]
me 9768 3673 0 14:00 pts/6 00:00:00 sleep 100
because line
me 9771 3673 0 14:00 pts/6 00:00:00 grep --color=auto sleep
does not match in grep slee[p].
See explanation in a related topic.
I suposse your perl script is named "myscriptname".
When you run this script, you have a new process (perl myscriptname.pl), and it's showed by the ps -ef command. The other one is related to the grep command (it has the text you are looking for)
#fedorqui's answer is right on -- the grep is matching its own invocation in the process table, and perhaps that of its parent shell, too, though timing issues mean this does not always happen from the CLI.
However, another approach, avoiding grep in favor of perl, would be:
my $count = () = qx(ps -e -o cmd) =~ /myscriptname/mg;
# Now $count tells you the number of times myscriptname appears in the process table
See this answer for why the empty parens are used above. Note, too, that you don't need the full ps output (-f), you just want to match on the command name (-o cmd).
Take a look at the pgrep and the pkill commands. These are standard Linux commands are are way easier to use than trying to do a ps and then a grep.
Also, if you do use ps, take a look at the -o options. These let you display the columns you want, and give you a way to strip out the heading.

Resources