bash:按列分割命令的输出
我想做这个:
- 运行一个命令
- 捕获输出
- select一条线
- 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
在第二列和第三列之间添加了一些空格来保持表格的某些相似性,所以cut
select一个空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
读取的第一个参数a
select第一列,如果有更多的话, 其他的一切都会放在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:”