Bash脚本循环通过shell输出

时间:2016-03-10 21:34:58

标签: linux bash shell

我想写一个脚本来循环输出shell输出(可能是数组?):ps命令

这是shell命令输出:

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

将其转换为bash脚本(Snippet):

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

但输出变为:

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

如何在shell输出中循环遍历每一行但是在bash脚本中?

4 个答案:

答案 0 :(得分:27)

除非您要将内部字段分隔符 for的值更改为“x”,否则从不$IFS循环遍历shell命令的结果\n。这是因为线条将成为分词的主题,这会导致您看到的实际结果。例如,如果你有一个像这样的文件的意思:

foo bar
hello world

以下for循环

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

给你:

foo
bar
hello
world

即使您使用IFS='\n',这些行仍可能会成为Filename expansion

的主题

我建议使用while + read,因为read会逐行阅读。

此外,如果您要搜索属于某个二进制文件的pids,我会使用pgrep。但是,由于python可能显示为不同的二进制文件,例如python2.7python3.4我建议将-f传递给pgrep,这使得它搜索整个命令行而不仅仅是搜索二进制文件称为python。但是,这也将找到像cat foo.py一样开始的流程。你被警告了!最后,您可以按照自己的意愿优化传递给pgrep的正则表达式。

示例:

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

或者如果您还需要进程名称:

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

如果您希望在单独的变量中使用进程名称和pid:

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

您看,read提供了一种灵活而稳定的方式来逐行处理命令的输出。

顺便说一句,如果您更喜欢ps .. | grep命令行而不是pgrep,请使用以下循环:

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

请注意我如何更改etimecmd的顺序。因此,能够将包含空格的cmd读入单个变量。这是有效的,因为read会将行分解为变量,就像指定变量一样多。该行的剩余部分 - 可能包括空格 - 将被分配给在命令行中指定的最后一个变量。

答案 1 :(得分:2)

在bash中使用for循环时,它会默认按whitespaces拆分给定列表,可以使用所谓的内部字段分隔符或简短{{}进行调整。 1}}。

  

IFS内部字段分隔符,用于之后的单词分割   扩展并使用read builtin命令将行拆分为单词。   默认值为“”。

对于您的示例,我们需要告诉IFS使用IFS作为断点。

new-lines

此示例在我的机器上返回以下输出。

IFS=$`\n`

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

答案 2 :(得分:1)

我发现你可以这样做只使用双引号:

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

这会将每一行保存到数组而不是每个项目。

答案 3 :(得分:0)

这是另一种基于bash的解决方案,灵感来自@ Gordon Davisson的评论。

为此,我们需要(至少bash v1.13.5(1992年或更高版本),因为Process-Substitution 234 < / sup> while read var; do { ... }; done < <(...);等。

#!/bin/bash
while IFS= read -a oL ; do {  # reads single/one line
    echo "${oL}";  # prints that single/one line
};
done < <(ps -ewo pid,cmd,etime | grep python | grep -v grep | grep -v sh);
unset oL;

注意:您可以在<(...)内使用任何简单或复杂的命令/命令集,这些命令/命令集可能具有多个输出行。
here显示什么代码执行什么功能。

这是单线/单线方式:
while IFS= read -a oL ; do { echo "${oL}"; }; done < <(ps -ewo pid,cmd,etime | grep python | grep -v grep | grep -v sh); unset oL;

(由于Process-Substitution还不是POSIX的一部分,因此在许多POSIX兼容shell或bash-shell的POSIX shell模式中均不受支持。Process-Substitution自1992年以来就存在于bash中(从现在开始28年之前/ 2020年),并且存在于ksh86中(1985年之前) 1 。因此POSIX应该将其包含在内。)
如果您或任何用户想要在POSIX兼容外壳程序中使用类似于Process-Substitution的东西(例如:sh,ash,dash,pdksh / mksh等),请查看NamedPipes