How to extract field from command result in different versions of linux? - linux

I was trying to extract a process size using the command:
size=`ps -eo vsz,pid | grep $pid | cut -'d' -f1`
However, this appeared to only work on some computers but not all. So on the ones where it wasn't working, I tried:
size=`ps -eo vsz,pid | grep $pid | awk '{print $1}'`
however, now this didn't work on the computers where the first command worked.
What I mean by "working" and "not working" is that sometimes:
echo "|$size|"
Will return something like:
|8762348
9835|
And thus the following returns an arithmetic error:
(( $size > $threshold ))
because of the newline or carriage return characters stored in $size. Is there a way to reliable extract simply the first field across different versions of linux?

First you ask ps to displat info for all processes, next you try to select 1 of them.
Your command had problems on some computers, and you tagged the question with both ksh and linux, so I am not sure what command can be used best in your case:
size=$(ps -q ${pid} -o vsz --no-headers)
# or
size=$(ps -p ${pid} -o vsz | grep -v "VSZ")

Related

How can I query the number of the virtual desktop on which the bash script is running in Linux Mint via bash?

Environment:
Linux Mint, Cinnamon desktop manager, with multiple workspaces=virtual desktops, e.g. 4.
Bash script
What is known:
How to determine the number of workspaces:
wmctrl -d | wc -l
What I need:
Get the number of virtual desktops the bash script is running on with a pure bash as var (like with grep, not awk or similar) and echo the var.
With awk (imho still the most appropriate choice for the task at hand):
nr_of_active_workspace=$(wmctrl -d | awk '/\*/{print $NF}')
echo $nr_of_active_workspace
Or a pure bash solution:
nr_of_active_workspace=$(wmctrl -d | while read -r line; do [[ $line =~ '*' ]] && echo ${line: -1} ; done)
echo $nr_of_active_workspace
You can use POSIX shell features and the xprop(1) command to get both details with no other external utilities.
To get the ID number of the current/active desktop:
curdesk=$(xprop -root -notype _NET_CURRENT_DESKTOP)
curdesk="${curdesk##* }"
To get the count/quantity of desktops defined:
deskcnt=$(xprop -root -notype _NET_NUMBER_OF_DESKTOPS)
deskcnt="${deskcnt##* }"
Both depend on xprop(1) giving the answer in the form "foo = 0" (separated by spaces), and use shell pattern matching parameter expansion to match the longest substring ending in space, and remove it, leaving only the last token (the value after the equals sign).
Note that desktops are numbered from 0 (zero), so the count will be a number one higher than the ID number of the last desktop.
This should work with any window manager that adheres to the Extended Window Manager Hints (EWMH) specification (which is practically all of them, these days):
https://specifications.freedesktop.org/wm-spec/1.3/ar01s03.html
Follow a solution which need awk:
nr_of_active_workspace=$(wmctrl -d | grep "*" | awk '{print $11}')
echo $nr_of_active_workspace
It can be a solution without need awk, are possible on other way.
Based on answer of KamilCuk, its possible to output on follow way the line which is including the number of the active desktop:
nr_of_active_desktop=activedesktop=$(wmctrl -d | grep "*" | rev | cut -d ' ' -f1)
echo $nr_of_active_desktop

Bash command not working inside loop

i have this piece of code
for f in ${CURRENT_IMAGE_FILES[#]};
do
echo "Checking if too many process are running"
while :
do
RUNNING_PROCESS=`ps aux | grep [f]fmpeg -c`
if [[ $RUNNING_PROCESS -gt 22 ]]
then
echo "Too many process running now, sleep for 1 second"
sleep 1
else
echo "Total ffmpeg process running is less then 20, continue"
echo "current process count is " $RUNNING_PROCESS
break
fi
done
convert $f -resize 1280x720 -gravity center $f
composite -gravity center $f /var/bash/b.jpg $f
ffmpeg -loop 1 -f image2 -i $f -c:v libx264 -pix_fmt yuvj420p -preset ultrafast -t 3 $DIR/$COUNT.mp4 &
((++COUNT))
done
The thing is that when place inside the for loop, grep command is getting an error. Something along the lines of Usage: grep [OPTION]... PATTERN [FILE]...
Try `grep --help' for more information.
But when place outside the for loop, i do not get such error. Is there something wrong with my loop that is interfering with grep?
You are using grep with the options in the wrong order and the [f] character class is unnecessary. From man grep
grep [OPTIONS] PATTERN [FILE...]
So you should change your RUNNING_PROCESS command to
RUNNING_PROCESS=`ps aux | grep -c ffmpeg`
Additionally, why are you using [f]fmpeg? The character class [f] will only match/expand to f, so get rid of it to prevent grep from interpreting it as part of the pattern. Further, it does not help as a wildcard as fmpeg will match anything ffmpeg will match.
You are using
RUNNING_PROCESS=`ps aux | grep [f]fmpeg -c`
to count the process instances of ffmpeg currently running.
The [f] is used so that grep [f]fmpeg does not match it's own command line, that's good.
But there's a problem: The [f] is unquoted, so the shell may try to interpret it, depending on options.
Quoting it should make the line work:
RUNNING_PROCESS=`ps aux | grep '[f]fmpeg' -c`
There is another problem in this: it matches not only ffmpeg -a foo -b bar ..., but also less ffmpeg-manual.txt or vim notes-on-ffmpeg-commands.txt.
But there is a better way to do that - using the right tool for the job.
ps is the right to list processes, but for matching processes, pgrep is better suited:
RUNNING_PROCESS=$(pgrep -c ffmpeg)
does the what you need, getting the count of processes of ffmpeg into RUNNING_PROCESS.
The above works for pgrep of the package procps, common in Linux. There are versions that do not support -c for count, or use -c for something else. If it does not work, use:
RUNNING_PROCESS=$(pgrep ffmpeg | wc -l)
pgrep takes care of not matching itself on its own, so there is no need for the [f] workaround.
I also replaced the back quotes ` ... ` by $( ... ), which has some advantages if you need to nest it.
If you do need to match not only command name, but command line too, use pgrep -f.
Problem is that you are using [ and]asgrep` pattern but pattern is unquoted. Try this in BASH:
RUNNING_PROCESS=$(ps aux | grep '[f]fmpeg' -c)
Alternatively if you have pgrep then you can use:
RUNNING_PROCESS=$(pgrep -f 'ffmpeg' | wc -l)

Get pid of last started instance of a certain process

I have several instances of a certain process running and I want to determine the process id of the one that has been started last.
So far I came to this code:
ps -aef | grep myProcess | grep -v grep | awk -F" " '{print $2}' |
while read line; do
echo $line
done
This gets me all process ids of myProcess. Somehow I need to compare now the running times of this pids and find out the one with the smallest running time. But I don't know how to do that...
An easier way would be to use pgrep with its -n, --newest switch.
Select only the newest (most recently started) of the matching
processes.
Alternatively, if you don't want to use pgrep, you can use ps and sort by start time:
ps -ef kbsdstart
Use pgrep. It has a -n (newest) option for that. So just try
pgrep -n myProcess

linux shell scripting kiddie's question

an Unix shell script with only purpose - count the number of running processes of qmail (could be anything else). Easy thing, but there must be some bug in code:
#!/bin/bash
rows=`ps aux | grep qmail | wc -l`
echo $rows
Because
echo $rows
always shows greater number of rows (11) than if I just count rows in
ps aux | grep qmail
There are just 8 rows. Does it work this way on your system too?
Nowadays with linux, there is pgrep. If you have it on your system, you can skip grep -v grep
$ var=$(pgrep bash) # or `pgrep bash | wc -l`
$ echo $var
2110 2127 2144 2161 2178 2195 2212 2229
$ set -- $var; echo ${#}
8
also, if your ps command has -C option, another way
$ ps -C bash -o pid= | wc -l
if not, you can set a character class in your grep pattern
$ ps aux|grep [q]mail | wc -l
It appears that you're counting the grep process itself and the header line that ps normally prints before its output.
I'd suggest something more like:
qprocs=$(ps auxwww | grep -c "[q]mail")
... note that GNU grep has a "-c" switch to have it print a "count" of matches rather than the lines themselves. The trick with the regular expression here is to match qmail without matching the literal string that's on the grep command line. So we take any single character in the string and wrap it in square brackets such that it is a single character "class." The regexp: [q]mail matches the string qmail without matching the string [q]mail.
Note that even with this regex you may still find some false positive matches. If you really want to be more precise then you should supply a custom output format string to your ps command (see the man pages) or you should feed it through a pipemill or you should parse the output of the ps command based on fields (using awk or cut or a while read loop). (The -o option to ps is by far the easiest among these).
No, since I'm not running qmail. However, you will want to, at a bare minimum, exclude the process running your grep:
ps aux | grep qmail | grep -v grep
For debugging, you may want to do:
rows=`ps aux | grep qmail`
echo $rows >debug.input
od -xcb debug.input
(to see your input to the script in great detail) and then rewrite your script temporarily as:
#!/bin/bash
rows=`cat debug.input | wc -l`
echo $rows
That way, you can see the input and figure out what effect it's having on your code, even as you debug it.
A good debugger will eventually learn to only change one variable at a time. If your changing your code to get it working, that's the variable - don't let the input to your code change as well.
Use
$ /sbin/pidof qmail
A few ways...
ps -e | grep ' [q]mail' | wc -l
ps -C qmail -opid= | wc -l
pidof qmail | tr ' ' '\n' | wc -l
pgrep is on many Linux distributions, and I imagine available for other Unices.
[dan#khorium ~]$ whatis pgrep
pgrep (1) - look up or signal processes based on name and other attributes
[dan#khorium ~]$ pgrep mingetty
1920
1921
1922
1923
1924
In your case, pgrep qmail | wc -l should do the trick.

Split output of command by columns using Bash?

I want to do this:
run a command
capture the output
select a line
select a column of that line
Just as an example, let's say I want to get the command name from a $PID (please note this is just an example, I'm not suggesting this is the easiest way to get a command name from a process id - my real problem is with another command whose output format I can't control).
If I run ps I get:
PID TTY TIME CMD
11383 pts/1 00:00:00 bash
11771 pts/1 00:00:00 ps
Now I do ps | egrep 11383 and get
11383 pts/1 00:00:00 bash
Next step: ps | egrep 11383 | cut -d" " -f 4. Output is:
<absolutely nothing/>
The problem is that cut cuts the output by single spaces, and as ps adds some spaces between the 2nd and 3rd columns to keep some resemblance of a table, cut picks an empty string. Of course, I could use cut to select the 7th and not the 4th field, but how can I know, specially when the output is variable and unknown on beforehand.
One easy way is to add a pass of tr to squeeze any repeated field separators out:
$ ps | egrep 11383 | tr -s ' ' | cut -d ' ' -f 4
I think the simplest way is to use awk. Example:
$ echo "11383 pts/1 00:00:00 bash" | awk '{ print $4; }'
bash
Please note that the tr -s ' ' option will not remove any single leading spaces. If your column is right-aligned (as with ps pid)...
$ ps h -o pid,user -C ssh,sshd | tr -s " "
1543 root
19645 root
19731 root
Then cutting will result in a blank line for some of those fields if it is the first column:
$ <previous command> | cut -d ' ' -f1
19645
19731
Unless you precede it with a space, obviously
$ <command> | sed -e "s/.*/ &/" | tr -s " "
Now, for this particular case of pid numbers (not names), there is a function called pgrep:
$ pgrep ssh
Shell functions
However, in general it is actually still possible to use shell functions in a concise manner, because there is a neat thing about the read command:
$ <command> | while read a b; do echo $a; done
The first parameter to read, a, selects the first column, and if there is more, everything else will be put in b. As a result, you never need more variables than the number of your column +1.
So,
while read a b c d; do echo $c; done
will then output the 3rd column. As indicated in my comment...
A piped read will be executed in an environment that does not pass variables to the calling script.
out=$(ps whatever | { read a b c d; echo $c; })
arr=($(ps whatever | { read a b c d; echo $c $b; }))
echo ${arr[1]} # will output 'b'`
The Array Solution
So we then end up with the answer by #frayser which is to use the shell variable IFS which defaults to a space, to split the string into an array. It only works in Bash though. Dash and Ash do not support it. I have had a really hard time splitting a string into components in a Busybox thing. It is easy enough to get a single component (e.g. using awk) and then to repeat that for every parameter you need. But then you end up repeatedly calling awk on the same line, or repeatedly using a read block with echo on the same line. Which is not efficient or pretty. So you end up splitting using ${name%% *} and so on. Makes you yearn for some Python skills because in fact shell scripting is not a lot of fun anymore if half or more of the features you are accustomed to, are gone. But you can assume that even python would not be installed on such a system, and it wasn't ;-).
try
ps |&
while read -p first second third fourth etc ; do
if [[ $first == '11383' ]]
then
echo got: $fourth
fi
done
Your command
ps | egrep 11383 | cut -d" " -f 4
misses a tr -s to squeeze spaces, as unwind explains in his answer.
However, you maybe want to use awk, since it handles all of these actions in a single command:
ps | awk '/11383/ {print $4}'
This prints the 4th column in those lines containing 11383. If you want this to match 11383 if it appears in the beginning of the line, then you can say ps | awk '/^11383/ {print $4}'.
Using array variables
set $(ps | egrep "^11383 "); echo $4
or
A=( $(ps | egrep "^11383 ") ) ; echo ${A[3]}
Similar to brianegge's awk solution, here is the Perl equivalent:
ps | egrep 11383 | perl -lane 'print $F[3]'
-a enables autosplit mode, which populates the #F array with the column data.
Use -F, if your data is comma-delimited, rather than space-delimited.
Field 3 is printed since Perl starts counting from 0 rather than 1
Getting the correct line (example for line no. 6) is done with head and tail and the correct word (word no. 4) can be captured with awk:
command|head -n 6|tail -n 1|awk '{print $4}'
Instead of doing all these greps and stuff, I'd advise you to use ps capabilities of changing output format.
ps -o cmd= -p 12345
You get the cmmand line of a process with the pid specified and nothing else.
This is POSIX-conformant and may be thus considered portable.
Bash's set will parse all output into position parameters.
For instance, with set $(free -h) command, echo $7 will show "Mem:"

Resources