bash:按列拆分命令输出

时间:2009-10-27 10:26:45

标签: linux bash pipe

我想这样做:

  1. 运行命令
  2. 捕获输出
  3. 选择一行
  4. 选择该行的列
  5. 就像一个例子,假设我想从$PID获取命令名称(请注意这只是一个例子,我并不是说这是获取命令名称的最简单方法。进程ID - 我真正的问题是另一个命令,其输出格式我无法控制。)

    如果我运行ps,我会:

    
      PID TTY          TIME CMD
    11383 pts/1    00:00:00 bash
    11771 pts/1    00:00:00 ps
    
    

    现在我做ps | egrep 11383并获得

    11383 pts/1    00:00:00 bash

    下一步:ps | egrep 11383 | cut -d" " -f 4。输出是:

    <absolutely nothing/>
    

    问题是cut会减少单个空格的输出,并且ps在第2列和第3列之间添加一些空格以保持表格的相似性,cut选择一个空字符串。当然,我可以使用cut来选择第7个而不是第4个字段,但我怎么知道,特别是当输出事先变量和未知时。

10 个答案:

答案 0 :(得分:146)

一种简单的方法是添加tr的通道来挤压任何重复的字段分隔符:

$ ps | egrep 11383 | tr -s ' ' | cut -d ' ' -f 4

答案 1 :(得分:61)

我认为最简单的方法是使用 awk 。例如:

$ echo "11383 pts/1    00:00:00 bash" | awk '{ print $4; }'
bash

答案 2 :(得分:8)

请注意,tr -s ' '选项不会删除任何单个前导空格。如果您的列是右对齐的(与ps pid一样)...

$ ps h -o pid,user -C ssh,sshd | tr -s " "
 1543 root
19645 root
19731 root

如果是第一列,那么切割将导致某些字段出现空白行:

$ <previous command> | cut -d ' ' -f1

19645
19731

除非你在它前面加上一个空格,显然

$ <command> | sed -e "s/.*/ &/" | tr -s " "

现在,对于pid数字(而不是名称)的特殊情况,有一个名为pgrep的函数:

$ pgrep ssh


壳牌功能

但是,一般来说,实际上仍然可以以简洁的方式使用 shell函数,因为read命令有一个很好的东西:

$ <command> | while read a b; do echo $a; done

要阅读的第一个参数a选择第一列,如果还有更多,其他所有内容将被放入b。因此,您永远不需要比列数 +1 更多的变量。

所以,

while read a b c d; do echo $c; done
然后

将输出第3列。如我的评论中所示......

管道读取将在不将变量传递给调用脚本的环境中执行。

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'`


阵列解决方案

因此,我们最后得到@frayser的答案,即使用默认为空格的shell变量IFS,将字符串拆分为数组。它只适用于Bash。 Dash和Ash不支持它。我很难将字符串拆分成Busybox中的组件。很容易获得单个组件(例如使用awk),然后为您需要的每个参数重复该组件。但是最后你反复在同一行上调用awk,或者在同一行重复使用带有echo的读取块。哪个效率不高或漂亮。因此,您最终会使用${name%% *}进行拆分,依此类推。让你渴望一些Python技能,因为实际上,如果你已经习惯的一半或更多的功能,shell脚本就不再那么有趣了。但你可以假设即使python也不会安装在这样的系统上,而且它不是; - )。

答案 3 :(得分:3)

ps |&
while read -p first second third fourth etc ; do
   if [[ $first == '11383' ]]
   then
       echo got: $fourth
   fi       
done

答案 4 :(得分:2)

与brianegge的awk解决方案类似,这里是Perl的等价物:

ps | egrep 11383 | perl -lane 'print $F[3]'

-a启用自动分段模式,该模式使用列数据填充@F数组。
如果您的数据以逗号分隔,而不是以空格分隔,请使用-F,

由于Perl从0开始计数而不是1

,因此打印字段3

答案 5 :(得分:1)

获得正确的线(第6行的示例)是用头部和尾部完成的,并且可以使用awk捕获正确的单词(第4号单词):

command|head -n 6|tail -n 1|awk '{print $4}'

答案 6 :(得分:1)

使用数组变量

set $(ps | egrep "^11383 "); echo $4

A=( $(ps | egrep "^11383 ") ) ; echo ${A[3]}

答案 7 :(得分:0)

我建议你使用改变输出格式的ps功能,而不是做所有这些greps和东西。

ps -o cmd= -p 12345

您可以获得指定了pid的进程的cm​​mand行,而不是其他任何内容。

这符合POSIX标准,因此可以视为便携式。

答案 8 :(得分:0)

你的命令

ps | egrep 11383 | cut -d" " -f 4

错过了tr -s来挤压空格,正如his answer中的展开解释一样。

但是,您可能希望使用awk,因为它在一个命令中处理所有这些操作:

ps | awk '/11383/ {print $4}'

这将在包含11383的行中打印第4列。如果您想要与11383匹配,如果它出现在该行的开头,那么您可以说ps | awk '/^11383/ {print $4}'

答案 9 :(得分:0)

Bash的/会将所有输出解析为位置参数。

例如,使用set命令,set $(free -h)将显示&#34; Mem:&#34;