Bash Script Loop through shell output

I want to write a script that executes an output loop (array, maybe?) Of shell output: ps command

Here it is displayed by the shell command:

ps -ewo pid,cmd,etime | grep python | grep -v grep | grep -v sh 3089 python /var/www/atm_securit 37:02 17116 python /var/www/atm_securit 00:01 17119 python /var/www/atm_securit 00:01 17122 python /var/www/atm_securit 00:01 17125 python /var/www/atm_securit 00:00 

Convert it to a bash script (snippet):

 for tbl in $(ps -ewo pid,cmd,etime | grep python | grep -v grep | grep -v sh) do echo $tbl done 

But the output will be:

 3089 python /var/www/atm_securit 38:06 17438 python /var/www/atm_securit 00:02 17448 python /var/www/atm_securit 00:01 

How do I scroll through each line, as in the shell output file, but in a bash script?

+6
source share
3 answers

Never for loop the results of a shell command if you want to process it line by line, unless you change the value of the internal field delimiter $IFS to \n . This is due to the fact that the lines will be subject to word splitting, which will lead to the actual results that you see. If you, for example, have a file like this:

 foo bar hello world 

Below for the cycle

 for i in $(cat file); do echo "$i" done 

gives you:

 foo bar hello world 

Even if you use IFS='\n' , strings may still be perceived as a file name extension


I recommend using while + read , because read is read line by line.

Also, I would use pgrep if you are looking for pids belonging to a specific binary. However, since python can appear as different binary files, like python2.7 or python3.4 , I suggest passing -f to pgrep , which causes it to search the entire command line, rather than just looking for binary files called python . But it will also find processes that were started as cat foo.py You have been warned! At the end, you can refine the regular expression passed to pgrep as you wish.

Example:

 pgrep -f python | while read -r pid ; do echo "$pid" done 

or if you also want the process name:

 pgrep -af python | while read -r line ; do echo "$line" done 

If you want the process name and pid to be in separate variables:

 pgrep -af python | while read -r pid cmd ; do echo "pid: $pid, cmd: $cmd" done 

You see, read offers a flexible and stable way to handle command line output.


Btw, if you prefer your command line ps .. | grep ps .. | grep on top of pgrep used the following loop:

 ps -ewo pid,etime,cmd | grep python | grep -v grep | grep -v sh \ | while read -r pid etime cmd ; do echo "$pid $cmd $etime" done 

Notice how I etime and cmd . Thus, to read cmd , which may contain spaces, in a single variable. This works because read will break the string into variables, as many times as the variables you specified. The rest of the line — possibly including a space — will be assigned to the last variable specified on the command line.

+16
source

When using for loops in bash, it splits this list by default into whitespaces , this can be adapted using the so-called internal field separator or short IFS .

IFS An internal field delimiter that is used to separate words after expansion and dividing lines into words using the readin built-in command. The default value is "".

For your example, we need to tell IFS to use new-lines as a breakpoint.

 IFS=$`\n` for tbl in $(ps -ewo pid,cmd,etime | grep python | grep -v grep | grep -v sh) do echo $tbl done 

This example returns the following output on my computer.

  668 /usr/bin/python /usr/bin/ud 03:05:54 27892 python 00:01 
+1
source

I found that you can do this, just use double quotes:

 while read -r proc; do #do work done <<< "$(ps -ewo pid,cmd,etime | grep python | grep -v grep | grep -v sh)" 

This will save every row in the array, not in every element.

0
source

All Articles