从 bash cut 命令返回的序列中排除定界符

Exclude delimiters from sequence returned by bash cut command

惊奇地发现,下面的剪切命令:

for n in {1..10}; do echo "[$(echo ' a    b c   de f ' | cut -d' ' -f$n)]"; done

returns:

[]
[a]
[]
[]
[]
[b]
[c]
[]
[]
[de]

虽然我可能可以设置一个 awk 来获得所需的(仅限非定界符)方法 - 有没有办法以更智能的方式使用 cut 本身?

我正在寻找剪切输出:

[a]
[b]
[c]
[de]
[f]

更新。我得到的答案提供了 替代 方式(不使用 cut)来做到这一点。这不是本文 post 的目的。例如。另一种使用 awk 的方法是:

 echo "[$(echo ' a    b c   de f ' | awk -F' ' -f3)]"

 [c]

好吧,cut 考虑了空字段(这是合乎逻辑的)。如果你有一个字符串"a~bb~~c"(~是一个space),第一个是"a",第二个是"bb"第三个"" 第 4 个 "c".

您可能想预先使用 tr,如图 here

for n in {1..10}; do echo "[$(echo ' a    b c   de f ' | tr -s ' ' | cut -d' ' -f$n)]"; done

不确定为什么要在 for 循环中使用 cut,但您可以在 bash 中获得所需的输出,只需:

$ for i in ' a    b c   de f '; do printf "[%s]\n" $i  ; done
[a]
[b]
[c]
[de]
[f]

是你期望的(bash shell):

$ ar=(a b c de e)
$ for i in ${ar[@]}; do echo "[$i]"; done
[a]
[b]
[c]
[de]
[e]

或:

for i in {a,b,c,de,f}; do echo "[$i]"; done
[a]
[b]
[c]
[de]
[f]

这里用cut感觉不自然

cut 是工作 分隔符是单个不变字符 的出色工具。 /etc/passwd/etc/group 等文件的解析属于此类。考虑来自 /etc/passwd 的这些行:

sshd:x:103:65534::/var/run/sshd:/usr/sbin/nologin
messagebus:x:104:106::/var/run/dbus:/bin/false

请注意 (1) 这些文件中的分隔符始终是冒号,:,并且从不改变,并且 (2) 两个冒号在一起表示有一个空字段。这就是 cut 的设计目的。

默认情况下,cut 使用的分隔符是制表符。可以选择将分隔符更改为 space。但是,无法告诉 cut 分隔符可以是 或者 制表符或 space。也没有办法告诉 cut 将 repeated 分隔符视为一个分隔符。重复的分隔符总是被解释为空字段。

当分隔符不符合上述要求时,cut 是错误的工具。

当字段分隔符需要更大的灵活性时,应考虑 awk 或 shell。默认情况下,awk 接受任何 whitespace 序列作为字段分隔符。这可以自定义,甚至可以通过更改 FS 变量来自定义字段分隔符的正则表达式。 shell 的默认值也是任意白色序列 space 并且可以使用 IFS 变量将其更改为其他字符,但不是正则表达式。

例如,这里有一个 awk 解决方案:

$ echo ' a    b c   de f ' | awk '{for (i=1;i<=NF;i++) print "["$i"]"}'
[a]
[b]
[c]
[de]
[f]

使 shell 和 awk 一起工作

要将 shell 变量传递给 awk,最简单的方法是使用 -v 变量赋值。例如,下面使用 -vn shell 的值分配给名为 m 的 awk 变量:

$ for n in {1..5}; do echo ' a    b c   de f ' | awk -v m=$n '{printf "[%s]\n", $m}'; done
[a]
[b]
[c]
[de]
[f]

请注意,awk 代码全部用单引号引起来。这意味着 shell 不会混淆它。在awk代码中,$m指的是字段号m的值。 $m 与任何 shell 变量或 shell 替换无关。