使用cut命令时,将空格作为分隔符

415
我想使用空格作为cut命令的分隔符。
我可以使用什么语法?

53
虽然“untrue”这个词不太确定具体指什么,但是根据语境推测应该是“不正确的”、“不实的”意思。句子的意思是“cut命令的man手册没有解释清楚这一点,总的来说也不是很有用的信息。” - UncleZeiv
2
此外,在这种情况下,“信息剪切”也没有任何改进。 - cardiff space man
@UncleZeiv:man页面没有解释这个,因为它与cut_特别_无关,而与shell如何解析字符串文字以及如何解析POSIX兼容实用程序选项参数_一般_有关。 - mklement0
5
如果我没记错的话,我是在回复一条已被删除的评论,该评论认为这个问题已经在手册中得到了解答,而我认为这是“不真实的”(无论是否有充分的理由)- 现在,虽然我承认可能有很好的理由缺乏这些信息,但我仍然认为没有常见用法示例的文档通常至少会让人感到不舒服,甚至是毫无用处的。 - UncleZeiv
3
明白了,感谢你的澄清。考虑到对这个问题的兴趣,可以说man手册不够充分。 让我们看一下:"-d delim 使用 delim 作为字段定界符而不是制表符。"(适用于BSD cut,但GNU版本和POSIX规范基本上也表示相同)。 因此,使用 shell 调用 cut - 典型情况 - 需要您知道如何使用 shell syntax 通常传递一个空格作为参数,这可以说并不是 cut man页面的工作。然而,真实世界的例子总是有帮助的,而 GNU 手册则缺少这些内容。 - mklement0
4
虽然 所选答案 在技术上是正确的,但建议选择更为全面且更新的答案,即 @mklement0 的答案作为规范答案,以便它能够被筛选出来并排在首位。 - David LeBauer
8个回答

460
cut -d ' ' -f 2

数字 2 是你想要的以空格分隔的字段的字段编号。


6
你能否告诉Cut命令使用某个字符的任意数量作为分隔符,就像在正则表达式中一样?比如任意数量的空格,例如\s+。 - amphibient
4
不,我不相信你能够做到。 - Jonathan Hartley
7
你不能在cut命令中使用正则表达式,但是你可以使用cuts命令,它试图"修复"所有cut的限制:https://github.com/arielf/cuts。 - arielf
你能否获取每三个以空格分隔的字段?就像 cut -d ' ' -f 3,6,9,12,15,18,而不必指定每个数字? - Monocito
将可变数量的连续空格拆分为常见用例,有点有趣,但是跟进“cuts”似乎是个好主意。 - NeilG
我只想指出,因为通常查看此答案的人实际上是在寻找这个而不是其他东西,即可以在管道中使用 tr -s ' ' 来将所有多个空格的出现压缩为一个,使得 cut -d ' ' 在因对齐而导致行中出现可变数量的空格的情况下变得非常有用。实际上可以在此答案中找到:https://dev59.com/AHRA5IYBdhLWcg3wzhbZ#19069428 - NeilG

247
通常,如果您使用空格作为分隔符,您希望将多个空格视为一个空格,因为您要解析某些列与空格对齐的命令的输出。(而且谷歌搜索将我带到了这里)
在这种情况下,单个cut命令是不够的,您需要使用:
tr -s ' ' | cut -d ' ' -f 2

或者

awk '{print $2}'

这个方法有效是因为AWK的默认输入字段分隔符是一个或多个空白字符;在正则表达式术语中,它类似于[ \t]+。AWK解决方案的额外好处是可以透明地处理数据行上的前导/尾随空格,而tr + cut解决方案则不行。

8
感谢awk用法示例,正是我所需要的。 - spazm
1
是的!这应该是被接受的答案,或者至少包含在被接受的答案中。我记不得曾经在不必规范化空格的情况下尝试使用 cut 命令来处理空格分隔的数据。 - Jeremy Brooks
这是钱。tr 可以翻译或删除字符。-s 选项将重复的字符替换为单个出现。 - young_souvlaki
我正在使用 Mac,但是无法运行 cut 或者 tr 的例子。对于我所做的任务(在多个空格之间拆分输出),awk 的例子完美地运行了。 - Alf47
@Alf47 这个操作并不直观,因为大多数程序都不是这样工作的,但是tr是一个"过滤器",_只能_从标准输入接收输入,_只能_将输出写入标准输出。它不理解文件名参数,如果你尝试传递一个文件名给它,甚至可能不会报错。这只是你需要学习的其中一件事情。你的cuttr在Linux或其他类Unix系统上应该基本上是一样的,所以我鼓励你继续尝试,并查看手册页中的示例部分。 - undefined

59
为了补充现有的有用答案,向QZ Support致敬,鼓励我发布一个单独的答案:
这里有两个不同的机制起作用:
(a) cut本身是否需要将分隔符(在这种情况下是空格)传递给-d选项作为一个单独的参数,还是可以直接附加到-d上。
(b) shell在将参数传递给被调用的命令之前如何解析参数。
(a)的答案可以从POSIX guidelines for utilities的引用中得到(重点是我的)
如果标准实用程序的概要显示一个带有强制性选项参数的选项[...]符合规范的应用程序应该使用单独的参数来指定该选项和其选项参数。然而,符合规范的实现还应允许应用程序在同一参数字符串中指定选项和选项参数,而不需要插入字符
换句话说:在这种情况下,因为-d的选项参数是强制性的您可以选择将分隔符指定为
- (s) 要么:作为单独的参数 - (d) 要么:作为一个值直接附加-d
注意:GNU实现的cut命令,在许多Linux发行版中默认支持--delimiter作为-d的更具描述性的别名。与-d相同的考虑也适用于--delimiter,只是直接附加选项参数需要使用=作为分隔符,而-d则不使用分隔符;例如:
echo 'one two' | cut --delimiter=' ' -f 1 vs. echo 'one two' | cut -d' ' -f 1
一旦选择了(s)或(d),就是shell的字符串文字解析(b)才起作用。
使用方法(s),以下所有形式都是等效的: - -d ' ' - -d " " - -d \ (\ 是一个转义空格,用于字面使用)
使用方法(d),以下所有形式都是等效的: - -d' ' - -d" " - "-d " - '-d ' - d\
这种等效性是由于shell的字符串字面处理所解释的:
所有上述解决方案在cut命令看到它们时都会产生完全相同的字符串(在每个组中)。
  • (s): cut 看到 -d,作为它自己的参数,后面跟着一个包含空格字符的单独参数 - 然后没有引号或 \ 前缀!。

  • (d): cut 看到 -d 加上一个空格字符 - 然后没有引号或 \ 前缀! - 作为同一个参数的一部分。

这两个组中的形式最终相同的原因有两个,基于shell 如何解析字符串字面量

外壳允许通过称为“引用”的机制来指定文字,这样可以按原样指定文字。引用可以采用多种形式: - 单引号字符串:在`'...'`内部的内容被直接采用,形成一个单独的参数。 - 双引号字符串:在`"..."`内部的内容也形成一个单独的参数,但会进行插值处理(扩展变量引用,如`$var`,命令替换(`$(...)`或`` `...` ``),或算术扩展(`$(( ... ))`))。 - 通过`\`对个别字符进行引用:在单个字符前加上`\`,使该字符被解释为文字。
引用与“引用删除”相辅相成,这意味着一旦外壳解析了命令行,它就会从参数中删除引号字符(包括`'...'`、`"..."`或未引用的`\`实例),因此被调用的命令永远不会看到引号字符。

1
只有双引号选项才能从 Gow 中剪切:-d" ",-d " ","-d "。所有带单引号或空格的选项都无法使用。 - Frank
@Frank,是的,就支持引号样式而言,它与_shell_有关。鉴于Gow在Windows上运行,您需要使用cmd.exe的语法(仅支持"引号,^作为转义字符),或者PowerShell的语法(```作为转义字符)。 - mklement0
使用--delimiter=参数,所有这些解释都没有意义,尤其是在shell脚本中。 - undefined
@mklement0 在进行Shell编程时,大多数人最不用担心的是MacOS。将边缘案例作为你论点的关键,应该让人质疑你为什么要使用专有工具。顺便说一句,很多人在MacOS上运行VirtualBox,所以使用--delimiter=可能是他们在Linux上进行开发时想要的。 - undefined
它也被标记为bash,那是一个GNU工具。也许安装GNU工具然后就可以了? - undefined
@AnthonyRutledge(这是我之前评论的修订版)问题标记为unix,而不是linux,所以使用可移植的语法非常重要,并指出使用了哪些特定于实现的扩展(我已更新答案,提到了--delimiter)。 更重要的是,无论您使用符合POSIX的-d还是GNU特定的--delimiter选项,对于解决方案来说没有任何区别(唯一的区别是在后一种情况下,直接附加选项参数需要使用=作为分隔符,现在在答案中已经说明)。 - undefined

48

你也可以这样说:

cut -d\  -f 2

请注意反斜杠后面有两个空格。


32
知道 '' 能转义下一个字符的人会特别注意接下来的内容。使用 '' 转义空格这样的字符是一种非常常见的习惯用语。 - Jonathan Hartley
5
通常大部分代码的确是难以阅读的 :) - Luca Borrione
1
从Linux/Unix的角度来看,\是我的第一次尝试并且它起作用了。我同意与' '相比,它不太明显,但我相信许多人很高兴在这里阅读到它以确认其行为。为了更好地理解,请参见@mklement0的下面评论。 - tresf
@JonathanHartley 更正:「自私」的人知道「\」是转义字符,並「假设」其他人也知道。在个人项目中不适用,但在团队环境中,这种假设是非常危险(并且可能代价高昂)的。 - Eduard Nicodei
1
@EduardNicodei 哦,我同意。我们正在谈论代码的读者(“谁会注意到...?”),而不是作者。但是,在某些团队中,假设一定水平的熟练程度是可以接受的。这取决于环境。 - Jonathan Hartley
知道 -d 后面跟着分隔符的人可能会认为 \ 是分隔符 :) - Michael P. Bazos

8

刚刚发现你也可以使用"-d "

cut "-d "

测试

$ cat a
hello how are you
I am fine
$ cut "-d " -f2 a
how
am

5
请注意,从cut的角度来看,以下所有内容都是相同的:"-d "``,'-d ',-d" ",-d' ',和-d\<space>: 所有这些形式都直接将选项参数(一个空格)附加到选项(-d),并在cut看到它们时得到完全相同的字符串:一个包含d后跟一个空格的单个参数,在_shell_执行引号移除之后。 - mklement0
1
@mklement0的回答应该是最佳答案。尽管它只是一条评论,但它是本页面上最全面的。 - tresf
@QZSupport:我很感激你的情感和鼓励 - 它激励我发布了自己的答案,并提供了额外的背景信息。 - mklement0
1
哇,真是个很有趣的发现! - Harry

5

如果数据中有多个空格,使用cut命令无法轻松处理。因此,我发现将输入规范化可以更容易地进行处理。其中一个技巧是使用sed命令进行规范化,如下所示。

echo -e "foor\t \t bar" | sed 's:\s\+:\t:g' | cut -f2  #bar

3

scut是一种类似cut的实用工具(我做得更聪明但速度较慢),它可以使用任何perl正则表达式作为分隔符。默认情况下,以空格为分隔符,但您也可以使用多字符正则表达式、备选正则表达式等进行分隔。

scut -f='6 2 8 7' < input.file  > output.file

因此,上述命令将根据空格分隔列,并按照顺序提取第6列、第2列、第8列和第7列(从0开始计数)。

0

我有一个答案(我承认有点混乱),它涉及到sed、正则表达式和捕获组:

  • \S* - 第一个单词
  • \s* - 分隔符
  • (\S*) - 第二个单词 - 已捕获
  • .* - 行的其余部分

作为一个sed表达式,捕获组需要被转义,即\(\)

\1返回已捕获组的副本,即第二个单词。

$ echo "alpha beta gamma delta" | sed 's/\S*\s*\(\S*\).*/\1/'
beta

当你看这个答案时,它有点令人困惑,你可能会想,为什么要费心呢?嗯,我希望一些人能够恍然大悟,并使用这种模式来解决一些复杂的文本提取问题,只需一个sed表达式即可。

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