bash:按列分割命令的输出

我想做这个:

  1. 运行一个命令
  2. 捕获输出
  3. select一条线
  4. select该行的一列

举一个例子,假设我想从$PID获得命令名(请注意,这只是一个例子,我并不是说这是从进程id获取命令名的最简单的方法 – 我真正的问题是另一个命令的输出格式,我无法控制)。

如果我运行ps我得到:

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

现在我做ps | egrep 11383 ps | egrep 11383并得到

 11383 pts/1 00:00:00 bash 

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

 <absolutely nothing/> 

问题在于, cut通过单个空格来cut输出,并且由于ps在第二列和第三列之间添加了一些空格来保持表格的某些相似性,所以cutselect一个空string。 当然,我可以使用cut来select第7个而不是第4个字段,但是我怎么能知道,特别是当输出variables和事先未知的时候。

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

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

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

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

请注意, tr -s ' '选项不会删除任何单个前导空格。 如果你的列是右alignment的(与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 

壳牌function

但是,一般情况下,实际上仍然可以使用简洁的方式使用shell函数 ,因为read命令有一个很整洁的地方:

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

读取的第一个参数aselect第一列,如果有更多的话, 其他的一切都会放在b 。 因此,您永远不需要比列+1更多的variables。

所以,

 while read abcd; do echo $c; done 

然后会输出第三列。 正如我的评论所示…

pipe道读取将在不会将variables传递给调用脚本的环境中执行。

 out=$(ps whatever | { read abcd; echo $c; }) arr=($(ps whatever | { read abcd; echo $c $b; })) echo ${arr[1]} # will output 'b'` 

arrays解决scheme

所以我们最后得到@frayser的答案,即使用默认为空格的shellvariablesIFS将string拆分成数组。 它只适用于Bash。 破折号和灰不支持它。 我已经很难将一个string拆分成一个Busybox中的组件。 获得单个组件(例如使用awk)很容易,然后为每个需要的参数重复一次。 但是最终你会在同一行上反复调用awk,或者在同一行上反复使用带有echo的读取块。 这是不高效或漂亮。 所以你最终分裂使用${name%% *}等等。 让你渴望一些Python技能,因为事实上,如果你已经习惯了一半或者更多的function,shell脚本已经不再那么有趣了。 但是你可以假设即使python不会被安装在这样的系统上,也不是;-)。

尝试

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

类似于brianegge的awk解决scheme,这里是Perl的等价物:

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

-a启用autosplit模式,用列数据填充@F数组。
使用-F,如果你的数据是逗号分隔的,而不是空格分隔。

因为Perl从0开始计数而不是1,所以打印字段3

使用数组variables

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

要么

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

通过头部和尾部获得正确的行(第6行的示例),可以使用awk捕获正确的单词(第4个单词):

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

而不是做所有这些greps和东西,我build议你使用psfunction来改变输出格式。

 ps -o cmd= -p 12345 

你得到了一个指定了pid的进程的命令行,没有其他的东西。

这符合POSIX标准,因此可以被认为是便携式的。

你的命令

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

错过了一个tr -s挤压空间,解开他的答案解释。

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

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

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

Bash的set将parsing所有的输出到位置参数。

例如, set $(free -h)命令, echo $7将显示“Mem:”