如何使用cut命令指定更多的分隔符空间?

226

有没有办法在cut命令中指定多个空格作为字段分隔符?(比如“ ”+) 例如:对于以下字符串,我想要获得值“3744”,我应该使用什么字段分隔符?

$ps axu | grep jboss

jboss     2574  0.0  0.0   3744  1092 ?        S    Aug17   0:00 /bin/sh /usr/java/jboss/bin/run.sh -c example.com -b 0.0.0.0

cut -d' ' 并不是我想要的,因为它只适用于一个单独的空格。 awk 也不是我想要的,那么如何使用 'cut'?

谢谢。


16
最佳答案是使用 tr,如此展示:https://dev59.com/02855IYBdhLWcg3wy3kb#4483833。 - John Bachir
1
与实际问题无直接关系,但是你可以使用在大多数现代发行版中可用的 pgrep 替代 ps+grep。它将会以你需要的形式返回结果。 - ccpizza
1
可能是如何使'cut'命令将多个字符视为一个分隔符?的重复问题。 - user9645477
现在我只是使用 hck 作为 cut 的替代品。默认情况下,它会像 awk 一样在所有空格上拆分。关键功能是您可以像 cut 一样使用 -d 指定分隔符,但与 cut 不同的是,该分隔符可以是正则表达式!不再需要在传递给 cut 之前预处理 tr -s。您可以在此处找到 hck:https://github.com/sstadick/hck - Chris
这个回答解决了你的问题吗?CUT是否支持多个空格作为分隔符? - dsimic
请查看下方的我的回答,这是一种非常方便的方法来实现所需的结果,请帮助将我的补丁上游,以使每个人都受益。 - undefined
13个回答

356

实际上,awk 正是你应该研究的工具:

ps axu | grep '[j]boss' | awk '{print $5}'

或者你可以完全放弃使用 grep,因为 awk 已经了解正则表达式:

ps axu | awk '/[j]boss/ {print $5}'

但如果由于某种奇怪的原因,您真的无法使用awk,那么还有其他更简单的方法可供选择,比如先将所有空格折叠为一个空格:

ps axu | grep '[j]boss' | sed 's/\s\s*/ /g' | cut -d' ' -f5

顺便说一下,那个grep技巧是一个聪明的方法,只获取jboss进程而不是grep jboss进程(同样适用于awk变体)。

grep进程在其进程命令中具有字面值grep [j]boss,因此不会被grep本身捕获,后者正在寻找字符类[j]后跟着boss

这是避免一些人使用的| grep xyz | grep -v grep范式的巧妙方法。


2
很棒的答案。下次需要时我会再回来查看它。 - funroll
“grep” 技巧在 crontab 文件中似乎无法工作。有什么原因吗? - Amir Ali Akbari
3
我不断学习并忘记grep技巧。感谢最近的提醒,也许这一次会牢记在心。但我不敢打赌。 - Michael Burr
这是一个很好的回答,但是OP要求使用cut命令来完成,所以我认为https://dev59.com/GWw05IYBdhLWcg3wmC-_#29685565比它目前获得的赞更加值得。 - Oliver
4
Oliver,有时候对于“我如何使用 Y 来实现 X?”这个问题,最好的答案是“不要使用 Y,而是使用 Z”。由于提问者接受了这个答案,所以很可能我已经说服了他们 :-) - paxdiablo
显示剩余2条评论

135

awk 版本可能是最好的选择,但如果您首先使用 tr 压缩重复内容,也可以使用 cut

ps axu | grep jbos[s] | tr -s ' ' | cut -d' ' -f5
#        ^^^^^^^^^^^^   ^^^^^^^^^   ^^^^^^^^^^^^^
#              |            |             |
#              |            |       get 5th field
#              |            |
#              |        squeeze spaces
#              |
#        avoid grep itself to appear in the list

12
华丽插图。 - Haggra
2
tr -s ' ' is mighty nice! I hope I can remember that better than awk - Chris
@Chris 我必须反对 :D Awk 对于这些事情来说更好!! - fedorqui
@fedorqui 当涉及到打印从第N个字段到结尾的内容时,cut -f5-语法中的"-fN-"awk简单得多。 - Weekend
@Weekend 同意。 - fedorqui

46

我喜欢使用tr -s命令来实现这一点

 ps aux | tr -s [:blank:] | cut -d' ' -f3
这会将所有的空格压缩成一个空格。这样,告诉 cut 命令使用空格作为定界符就能按预期执行了。

2
我认为这应该是答案,它更接近 OP 的要求(要求使用 cut)。这种方法比 awk 方法慢5-10%(因为需要处理一个额外的管道 tr),但总体上这将是无关紧要的。 - Oliver

12

我将提名 tr -s [:blank:] 为最佳答案。

为什么我们要使用 cut 呢?它有一个神奇的命令,表示“我们想要第三个字段以及之后的每个字段,省略前两个字段”。

cat log | tr -s [:blank:] |cut -d' ' -f 3- 

我不相信awk或perl split有一个等效的命令,可以用于我们不知道有多少字段的情况下,即输出第三个字段到第X个字段。


9
更简短、更简单的解决方案:使用我编写的cuts(增强版剪切)工具。
ps axu | grep '[j]boss' | cuts 4

请注意,cuts字段索引是从0开始的,因此第5个字段应指定为4。 http://arielf.github.io/cuts/ 甚至更短的方式(根本不使用cut)是:
pgrep jboss

8
一种解决此问题的方法是采取以下步骤:
$ps axu | grep jboss | sed 's/\s\+/ /g' | cut -d' ' -f3

将多个连续的空格替换为一个空格。


奇怪,这在OS X上不起作用。sed命令不能将多个空格改为一个空格。 - rjurney
2
\s 是 GNU sed 的扩展。在 OS X 上,您可以传递 -E 标志给 sed 以启用扩展正则表达式,然后使用 [[:space:]] 替换 \s,例如:sed -E 's/[[:space:]]+/ /g' - Jared Ng

5

个人而言,我倾向于像这样的任务使用awk。例如:

ps axu| grep jboss | grep -v grep | awk '{print $5}'

6
这段话可以压缩成 ps axu | awk '/[j]boss/ {print $5}' - zwol
1
awk 不是比 sed/grep/cut 慢一些吗(特别是当有其他多余的进程时)? - pihentagy

2
如果您想从ps输出中选择列,没有任何理由不使用-o选项吗?
例如:
ps ax -o pid,vsz
ps ax -o pid,cmd

最小列宽分配,无填充,仅有单个空格字段分隔符。

ps ax --no-headers -o pid:1,vsz:1,cmd

3443 24600 -bash
8419 0 [xfsalloc]
8420 0 [xfs_mru_cache]
8602 489316 /usr/sbin/apache2 -k start
12821 497240 /usr/sbin/apache2 -k start
12824 497132 /usr/sbin/apache2 -k start

将Pid和vsz以10个字符宽度呈现,使用1个空格作为字段分隔符。

ps ax --no-headers -o pid:10,vsz:10,cmd

  3443      24600 -bash
  8419          0 [xfsalloc]
  8420          0 [xfs_mru_cache]
  8602     489316 /usr/sbin/apache2 -k start
 12821     497240 /usr/sbin/apache2 -k start
 12824     497132 /usr/sbin/apache2 -k start

在脚本中使用:

oldpid=12824
echo "PID: ${oldpid}"
echo "Command: $(ps -ho cmd ${oldpid})"

2
作为替代方案,总是可以使用 Perl:
ps aux | perl -lane 'print $F[3]'

或者,如果您想获取从第3个字段开始的所有字段(如上面其中一个答案中所述):

ps aux | perl -lane 'print @F[3 .. scalar @F]'

这与lsof的输出不兼容。我尝试过lsof|perl -lane 'print $F[5]',有时会得到第五列,有时会得到第六列。 - rubo77
我认为问题只是如何使用可能包含不同数量空格的分隔符。对于这个目的,答案是正确的。 - flitz
在 lsof 中的问题是每行中列数不总是一致的。 - flitz

1
我已经实现了一个补丁,为cut(1)添加了一个新的-m命令行选项,它在字段模式下工作,并将多个连续的分隔符视为一个分隔符。这基本上以一种相当高效的方式解决了OP的问题,通过在cut(1)中将多个空格视为一个分隔符。

特别是,应用了我的补丁后,以下命令将执行所需的操作。就是这么简单,只需在cut(1)的调用中添加-m,然后使用-d ' ' -f 5ps(1)生成的进程列表中提取PID值:

ps axu | grep jboss | cut -d ' ' -m -f 5

我也向上游提交了这个补丁,希望它最终能被接受并合并到coreutils项目中。
关于在cut(1)中添加更多与空白相关的功能,我还有一些进一步的想法,希望能从不同的人那里得到一些反馈,最好是在coreutils的邮件列表上。我愿意为cut(1)实现更多的补丁,并将它们提交到上游,这将使这个实用程序在各种实际场景中更加多功能和易用。

我的之前回答这个问题的回答被删除了,因为它没有针对这个问题进行具体的调整。因此,我再次回答了这个问题,提供了一个更加具体的答案。希望现在可以了。 - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接