使用 Bash 按列拆分命令输出?
Posted
技术标签:
【中文标题】使用 Bash 按列拆分命令输出?【英文标题】:Split output of command by columns using Bash? 【发布时间】:2010-12-10 10:28:59 【问题描述】:我想这样做:
-
运行命令
捕获输出
选择一行
选择该行的一列
仅作为示例,假设我想从$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 个字段,但我怎么知道,特别是当输出是可变的且事先未知时。
【问题讨论】:
使用 awk(还有 25 个字符)。 【参考方案1】:获取正确的行(例如第 6 行)是使用 head 和 tail 完成的,并且可以使用 awk 捕获正确的单词(单词 4):
command|head -n 6|tail -n 1|awk 'print $4'
【讨论】:
只是提醒未来的读者,awk 也可以按行选择:awk NR=6 print $4
会更有效
当然我的意思是awk NR==6 print $4
*doh*【参考方案2】:
我认为最简单的方法是使用 awk。示例:
$ echo "11383 pts/1 00:00:00 bash" | awk ' print $4; '
bash
【讨论】:
为了与原始问题兼容,ps | awk "\$1==$PIDprint\$4"
或(更好)ps | awk -v"PID=$PID" '$1=PIDprint$4'
。当然,在 Linux 上你可以简单地使用 xargs -0n1 </proc/$PID/cmdline | head -n1
或 readlink /proc/$PID/exe
,但无论如何......
print $4;
中的;
是必需的吗?在 Linux 上删除它似乎对我没有影响,只是好奇它的目的
@igniteflow 如果您想在 print 语句之后继续添加,它不会表示命令结束吗?【参考方案3】:
一种简单的方法是添加@987654321@
的传递以挤出任何重复的字段分隔符:
$ ps | egrep 11383 | tr -s ' ' | cut -d ' ' -f 4
【讨论】:
我喜欢这个,貌似tr
比awk
更轻量级
我倾向于同意,但这也可能是因为我没有学过 awk。 :)
如果您碰巧有一个 PID 包含您感兴趣的 PID 作为子字符串的进程,则将无法工作。
此外,如果某些 PID:s 在左侧填充了空格,而其他没有,则字段编号将关闭。【参考方案4】:
试试
ps |&
while read -p first second third fourth etc ; do
if [[ $first == '11383' ]]
then
echo got: $fourth
fi
done
【讨论】:
@flybywire -- 对于这个简单的例子来说可能有点矫枉过正,但如果您需要对选定的数据进行更复杂的处理,这个习惯用法非常好。 另外,请注意现在默认的脚本 shell 通常不是 bash。【参考方案5】:我建议您使用 ps 更改输出格式的功能,而不是执行所有这些 grep 和其他操作。
ps -o cmd= -p 12345
你得到一个进程的命令行,指定了 pid,没有别的。
这是符合 POSIX 的,因此可以被认为是可移植的。
【讨论】:
flybywire 说他只是以 ps 为例,这个问题比这更笼统。【参考方案6】:使用数组变量
set $(ps | egrep "^11383 "); echo $4
或
A=( $(ps | egrep "^11383 ") ) ; echo $A[3]
【讨论】:
【参考方案7】:请注意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
然后将输出第三列。正如我的评论中所指出的......
管道读取将在不将变量传递给调用脚本的环境中执行。
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 也不会安装在这样的系统上,它不是;-)。
【讨论】:
您应该在echo "$a"
和echo "$c"
中的变量周围使用引号。
似乎每个管道块都在其自己的子外壳或进程中执行,并且您不能将任何变量返回到封闭块?虽然你可以在回显它之后获得它的输出。 var=$(....... | read a b c d; echo $c; )
。这仅适用于单个(字符串),但在 Bash 中,您可以使用 ar=($var)
将其拆分为数组
@tripleee 我认为在这个过程的这个阶段这不是问题。你很快就会发现你是否需要它,如果它在某个时候中断,这是一个学习课。然后你知道为什么你必须使用那些双引号;-)。然后它不再是你从别人那里听到的东西。玩火! :D。 :p.
详细回答:D
这个答案对我来说太有帮助了,我不能不这么说。【参考方案8】:
你的命令
ps | egrep 11383 | cut -d" " -f 4
错过了 tr -s
以压缩空间,正如 unwind 在 his answer 中解释的那样。
但是,您可能想使用awk
,因为它在一个命令中处理所有这些操作:
ps | awk '/11383/ print $4'
这将打印包含 11383
的行中的第 4 列。如果你想让它匹配出现在行首的11383
,那么你可以说ps | awk '/^11383/ print $4'
。
【讨论】:
【参考方案9】:类似于 brianegge 的 awk 解决方案,这里是 Perl 等价物:
ps | egrep 11383 | perl -lane 'print $F[3]'
-a
启用自动拆分模式,该模式使用列数据填充 @F
数组。
如果您的数据是逗号分隔的,而不是空格分隔的,请使用 -F,
。
由于 Perl 从 0 而不是 1 开始计数,因此会打印字段 3
【讨论】:
感谢您的 perl 解决方案 -- 不知道 autosplit,并且仍然认为 perl 是结束其他工具的工具.. ;)。【参考方案10】:Bash 的set
会将所有输出解析为位置参数。
例如,使用set $(free -h)
命令,echo $7
将显示“Mem:”
【讨论】:
此方法仅在命令有单行输出时有用。不够通用。 这不是真的,所有输出都放在位置参数中,而不管行。前set $(sar -r 1 1)
; echo "$23"
我的观点是,当输出量很大并且有很多字段时,很难确定参数的位置。 awk
是最好的方法。
这只是另一种解决方案。 OP 可能不想为这个单一用例学习 awk 语言。标签确实声明 bash
而不是 awk
。以上是关于使用 Bash 按列拆分命令输出?的主要内容,如果未能解决你的问题,请参考以下文章
如何避免作为 sql 查询输出的一部分返回的字符串值被拆分为 bash/shell 脚本中数组中的不同字段
SC2207 来自 subshell 的 Bash 数组分配未按预期拆分
Python使用numpy函数hsplit水平(按列)拆分numpy数组(返回拆分后的numpy数组列表)实战:水平(按列)拆分二维numpy数组split函数水平(按列)拆分二维numpy数组