Can't understand ps | wc output differences - bash

I was trying to write a set of functions that could check to see if a process name was running when I encountered some unexpected output. I've condensed the issue in the following script names isRunning.sh which depends on a system ps command that can take the '-fC' arguments...
#!/bin/bash
progname=isRunning.sh
ps -fC isRunning.sh
pRet=`ps -fC ${progname} | wc -l`
echo pRet $pRet
psOut=`ps -fC ${progname}`
wcOut=`echo "${psOut}" | wc -l`
echo
echo ps output
echo "${psOut}"
echo
echo wcOut $wcOut
The first attempt at piping the ps output to wc gets a return of 3. The second attempt gets the expected return value of 2. Can anyone explain this behavior? I figure it's got to be something stupid I am overlooking.
Thanks,
bbb
edit: my output
UID PID PPID C STIME TTY TIME CMD
root 6717 5940 0 13:10 pts/0 00:00:00 /bin/bash ./isRunning.sh
pRet 3
ps output
UID PID PPID C STIME TTY TIME CMD
root 6717 5940 0 13:10 pts/0 00:00:00 /bin/bash ./isRunning.sh
wcOut 2

I get 2 both attempts. Your ps might be outputting an extra blank line, or somesuch, and then your shell's backtick expansion stripping it. Or maybe you actually had two processes matching the first time you ran it.
If you just want to see if its running, check the exit code from your ps:
if ps -C "${progname}" > /dev/null; then
echo its running
else
echo not running
fi
Even better, you should take a look at pidof and pgrep if you can rely on them being present on whichever systems you're targeting. Or use the LSB functions, if you're on Linux.
edit: Actually, since you're looking for copies of yourself running, you might be picking up the shell doing a fork to implement the pipe.

Related

pipe of "ps -e", "tee" and "head" behaves strange

I meant to use the following command to grep the "zsh" process with header. But this command only showed the header and then exited. The grep didn't run. I can do this task by other ways but I want to understand what happened here. The interesting thing is that the pipe works as expected if other command like ls replaces ps -e. In fact, the pipe works even with ps (without -e). The pipe also works if head is replaced by another grep. Seemingly, there are some interesting interactions between ps -e, head and tee in this pipe. Any explanation? I ran the commands in MacOS-iterm2 and zsh. Thanks.
ps -e|tee >(head -n 1) >(grep "zsh") >/dev/null
the command above produces the following incorrect output: only header is shown, grep didn't run.
PID TTY TIME CMD
The correct output can be generated by the following command:
ps -e|awk 'FNR==1{print};/zsh/{print}'
PID TTY TIME CMD
1658 ttys000 0:00.30 -zsh
2817 ttys001 0:00.49 /bin/zsh -i
12890 ttys002 0:00.26 -zsh
13332 ttys003 0:00.23 -zsh
13469 ttys004 0:00.19 /bin/zsh -i
Update: The problem is indeed in the tee shipped with MacOS. As #OndrejK. pointed out in the comments below, tee from GNU works as expected.
I think Ondrej K. nailed it in a comment: head is reading just the first line it receives from tee, then exiting, so when tee tries to write the second line to that pipe, it gets an error (probably actually a SIGPIPE signal) and exits without writing the rest of ps's output.
I was able to reproduce this problem under both macOS and Raspbian, using both zsh and bash as my shell. I was able to get the full output by replacing head with something that reads (but doesn't output) the rest of the input from tee. Here's an example:
% ps -e|tee >(head -n 1) >(grep "zsh") >/dev/null
PID TTY TIME CMD
% ps -e|tee >(head -n 1; cat>/dev/null) >(grep "zsh") >/dev/null
PID TTY TIME CMD
13127 pts/0 00:00:00 zsh
13353 pts/0 00:00:00 zsh
% ps -e|tee >(sed -n '1p') >(grep "zsh") >/dev/null
PID TTY TIME CMD
13127 pts/0 00:00:00 zsh
In the second version above, adding cat>/dev/null after head lets it consume (and discard) the rest of the input after head exits. In the third, sed -n '1p' reads its entire input, but only prints the first line.
Note that the result here will depend on things like buffer sizes and the timing of various events, so it may be inconsistent between different environments or even vary randomly under apparently consistent conditions. I suspect the reason it works with ps (without the -e option) is that it doesn't produce enough output to trigger the problem: tee may be able to forward the entire output from ps before head gets around to exiting.
EDIT: At least in my environments, adding a delay after head exits prevents the problem:
% ps -e|tee >(head -n 1; sleep 1) >(grep "zsh") >/dev/null
PID TTY TIME CMD
8002 pts/0 00:00:00 zsh
8164 pts/0 00:00:00 zsh
This means it's timing-limited, rather than buffer-limited. But if the output from ps were big enough, it'd run into buffer limits at some point, and the output would be truncated no matter what delays were inserted.

bash script - how to get running process lapsed time and argument

assuming im running python scripts with arguments like the following:
501 8694 8590 0 11:01PM ttys011 0:02.03 /Library/Frameworks/Python.framework/Versions/2.7/Resources/Python.app/Contents/MacOS/Python test.py 100 yes
501 8699 8696 0 11:01PM ttys012 0:01.03 /Library/Frameworks/Python.framework/Versions/2.7/Resources/Python.app/Contents/MacOS/Python test.py 100 no
and now i want to use bash script to extract those two processes that are currently running, and get the amount of time each process has been running and argument yes/no. this is what i have done so far to just extract the time taken for each process:
#!/bin/bash
commands=$(ps -ef | grep -v grep | grep "while-file")
var=0
for command in $commands;do
((var++))
if [ $var = 7 ];then
echo "time taken:$command"
fi
if [ $var = 17 ];then
echo "time taken:$command"
fi
done
but this is not clean. if i happen to have 3 processes running, my code will break
So my questions are:
how can i get the amount of time each process has been running. in this case the 1st process has been running for 2mins3sec while the 2nd one is 1min3sec.
how can i get the argument yes or no from each line.
--Edit--
ok so based on twalberg comment i did this:
$ps -ef | grep [w]hile-file.py | awk '{print $7, $NF}'
0:00.00 while-file.py
0:00.02 yes
0:00.03 no
how do i ignore the first line: 0:00.00 while-file.py

Get the username and the process ID of a process in bash

I am writing a bash script where I need to find out the userID of a process. For an example let the process be bash itself.
I tried ps aux | grep ba[s]h but the following was returned:
1000 2745 0.0 0.1 28360 5440 pts/1 Ss 10:11 0:01 bash
I see the userID 1000 displayed, but I want the username.
This can happen if the username is longer than 8 characters (OR) id has no name. But, If you want the username in the ps output then try this,
ps -eo uname:20,pid,pcpu,pmem,sz,tty,stat,time,cmd | grep '[b]ash'
You can parse out the /proc entry if you are on Linux and if you just need a numeric pid (or are OK with it). Here is an example for the mysqld process:
grep -e '^Uid:' /proc/$(pidof mysqld)/status | cut -f 2
The shortest way I found so far ( $PID - ID of the process inspected):
ps -p $PID -o euid=
Here is an example for gedit process
grep -w Pid /proc/$(pidof gedit)/status | cut -f 2

Orphan Child (ksh Shell Script not terminating first upon CTRL-X)

I have a program (C++ Executable) on AIX 5.3 that launches a Shell Script (ksh).
When I launch the program and the shell script, i see two processes
AIX:>ps -ef | grep 3657892
u001 **3657892** 3670248 0 18:16:34 pts/11 0:00 /u0012006/bin/Launcher
u001 3723398 **3657892** 0 18:16:41 pts/11 0:00 /usr/bin/ksh /u0012006/shell/Trjt_Slds.sh -m
Now, When I do a CTRL-X key combination on the Keyboard to end and go out of the Shell Script, the main launching program (C++ Executable) process gets killed while the shell script continues to execute.
AIX:>ps -ef | grep 3723398
u001 3723398 1 106 18:16:41 pts/11 0:01 /usr/bin/ksh /u0012006/shell/Trjt_Slds.sh -m
u001 3731504 3723398 0 0:00 <defunct>
u001 3735612 3723398 0 0:00 <defunct>
u001 3739838 3723398 0 0:00 <defunct>
This is leading to the CPU Consumption going to 100% and a lot of defunct processes get launched.
Is there a way to have the AIX Shell Script terminate first when I do a CTRL-X?
Note: Launcher is broken and should be fixed. Thus, any "solution" will be a hack.
One thought is to check $PPID in various places in the script. If it is set to 1 (init), then exit the script.
I don't understand the use of control-X. That is not going to generate any tty signal. I guess that is what you want. Perhaps the tty is also in raw mode. But you might consider hooking control-X up to one of the various tty signals like SIGINT. e.g. stty intr ^X but you will also need to remember to unset it with stty intr ^C
Last, you could wrap the script in a script and use the technique to kill the child and exit. e.g. (untested)
#!/bin/ksh
# launch original program in background
/path/to/real/program "$#" &
# get child's pid
child=$!
while : ; do
# when we become an orphan
if [[ $$PPID -eq 1 ]] ; then
# kill the child and exit
kill $child
exit
fi
# poll once a second
sleep 1
done
Update
./s1 is:
#!/bin/ksh
./s2 &
sleep 10
exit
./s2 is:
#!/bin/ksh
while : ; do
if kill -0 $PPID ; then
echo still good
else
echo orphaned
exit
fi
sleep 1
done
ksh always does this. Just got bitten by this, unlike bash, ksh does not forward hup signals when you exit. if you can find the child pids you can hup them yourself.

Finding number of child processes

How do find the number of child processes of a bash script, from within the script itself?
To obtain the PID of the bash script you can use variable $$.
Then, to obtain its children, you can run:
bash_pid=$$
children=`ps -eo ppid | grep -w $bash_pid`
ps will return the list of parent PIDs. Then grep filters all the processes not related to the bash script's children. In order to get the number of children you can do:
num_children=`echo $children | wc -w`
Actually the number you will get will be off by 1, since ps will be a child of the bash script too. If you do not want to count the execution of ps as a child, then you can just fix that with:
let num_children=num_children-1
UPDATE: In order to avoid calling grep, the following syntax might be used (if supported by the installed version of ps):
num_children=`ps --no-headers -o pid --ppid=$$ | wc -w`
I prefer:
num_children=$(pgrep -c -P$$)
It spawns just one process, you do not have to count words or adjust the numbers of PIDs by the programs in the pipe.
Example:
~ $ echo $(pgrep -c -P$$)
0
~ $ sleep 20 &
[1] 26114
~ $ echo $(pgrep -c -P$$)
1
You can also use pgrep:
child_count=$(($(pgrep --parent $$ | wc -l) - 1))
Use pgrep --parent $$ to get a list of the children of the bash process.
Then use wc -l on the output to get the number of lines: $(pgrep --parent $$ | wc -l)
Then subtract 1 (wc -l reports 1 even when pgrep --parent $$ is empty)
If job count (instead of pid count) is enough I just came with a bash-only version:
job_list=($(jobs -p))
job_count=${#job_list[#]}
Use ps with the --ppid option to select children of the current bash process.
bash_pid=$$
child_count=$(ps -o pid= --ppid $bash_id | wc -l)
let child_count-=1 # If you don't want to count the subshell that computed the answer
(Note: this requires the Linux version of ps for --ppid. I don't know if there is an equivalent for BSD ps or not.)
You may evaluate the shell builtin command jobs like:
counter = `jobs | wc -l`

Resources