如何在awk中将定界字符串拆分成数组?


169

字符串中包含管道符号时如何拆分|。我想将它们拆分为数组。

我试过了

echo "12:23:11" | awk '{split($0,a,":"); print a[3] a[2] a[1]}'

哪个工作正常。如果我的字符串是类似的,"12|23|11"那么如何将它们拆分为数组?


3
请注意,您的输出将串联数组元素,没有分隔符。如果您希望将它们用OFS分隔,请在它们之间加上逗号,使print它们成为独立的参数。
dubiousjim

或者您可以使用sed:echo "12:23:11" | sed "s/.*://"
泥泞的

@slushy:您的命令根本不是要求者所需要的。您的命令(echo "12:23:11" | sed "s/.*://")删除所有内容,直到(包括)最后一个“:”,仅保留“ 11” ...即可获取最后一个数字,但需要进行修改(以难以理解的方式)才能获取第二个数字,等等。awk(和awk的拆分)更加优雅和可读。
奥利维尔·杜拉克

如果您需要分割为单个字符,则可以使用cut
ccpizza

Answers:


274

你有没有尝试过:

echo "12|23|11" | awk '{split($0,a,"|"); print a[3],a[2],a[1]}'

2
@Mohamed Saligh,如果您使用的是Solaris,则需要使用/ usr / xpg4 / bin / awk(给定字符串长度)。
Dimitre Radoulov 2011年

5
“不是为我工作”。尤其是在回显值之间存在冒号,并且将拆分设置为在'|'???上拆分?错别字?祝你们好运。
剥壳机

1
更好的语法解释。
Alston

2
这在GNU awk中不起作用,因为to的第三个参数split是正则表达式,并且|是特殊符号,需要转义。使用split($0, a, "\|")
WhiteWind

1
@WhiteWind:“确保” |被视为字符而不是特殊符号的另一种方法是将其置于两者之间[]:即split($0, a, "[|]") ,在某些情况下,尤其是作为regexp的某些变体, #我比“ \ |”更喜欢此字符( perl vs grep vs .. others?)可以有“ |” 字面解释为“ \ |” 被视为正则表达式分隔符,而不是相反... ymmv
Olivier Dulac

119

要将字符串拆分为数组,请awk使用函数split()

 awk '{split($0, a, ":")}'
 #           ^^  ^  ^^^
 #            |  |   |
 #       string  |   delimiter
 #               |
 #               array to store the pieces

如果未指定分隔符,则使用FS,默认为空格:

$ awk '{split($0, a); print a[2]}' <<< "a:b c:d e"
c:d

我们可以给一个分离器,例如:

$ awk '{split($0, a, ":"); print a[2]}' <<< "a:b c:d e"
b c

这等效于通过以下方式进行设置FS

$ awk -F: '{split($0, a); print a[1]}' <<< "a:b c:d e"
b c

在gawk中,您还可以提供分隔符作为正则表达式:

$ awk '{split($0, a, ":*"); print a[2]}' <<< "a:::b c::d e" #note multiple :
b c

甚至通过使用第四个参数来了解分隔符在每个步骤中的作用:

$ awk '{split($0, a, ":*", sep); print a[2]; print sep[1]}' <<< "a:::b c::d e"
b c
:::

让我们引用GNU awk手册页

split(字符串,数组[,fieldsep [,sep]])

字符串划分为由fieldsep分隔的片段,并将片段存储在数组中,并将分隔符字符串存储在seps数组中。第一块存储在中array[1],第二块存储在中array[2],依此类推。第三个参数的字符串值fieldsep是一个正则表达式,描述了在哪里拆分字符串(就像FS可以是一个正则表达式,描述了在哪里拆分输入记录)。如果省略fieldsep,则使用FS的值。split()返回创建的元素数。sepsgawk扩展名,seps[i]是之间的分隔符array[i]array[i+1]。如果fieldsep是单个空格,则任何前导空格都会进入seps[0],任何尾随空格都会进入seps[n],其中n是的返回值split()(即数组中元素的数量)。


刚提到您使用的是gnu awk,而不是常规的awk(它不会在seps []中存储分隔符,并且还有其他限制)
Olivier Dulac

17

请更具体!“不起作用”是什么意思?发布确切的输出(或错误消息),您的操作系统和awk版本:

% awk -F\| '{
  for (i = 0; ++i <= NF;)
    print i, $i
  }' <<<'12|23|11'
1 12
2 23
3 11

或者,使用split:

% awk '{
  n = split($0, t, "|")
  for (i = 0; ++i <= n;)
    print i, t[i]
  }' <<<'12|23|11'
1 12
2 23
3 11

编辑:在Solaris上,您需要使用POSIX awk(/ usr / xpg4 / bin / awk)才能正确处理4000个字段。


for(i = 0还是for(i = 1
PiotrNycz

i = 0,因为我之后使用++ i(而不是i ++)。
Dimitre Radoulov 2015年

3
好的-我没有注意到这一点。我坚信将更具可读性for (i = 1; i <= n; ++i)……
PiotrNycz

5

我不喜欢该echo "..." | awk ...解决方案,因为它调用了不必要的调用forkexec系统调用。

我更喜欢Dimitre的解决方案

awk -F\| '{print $3 $2 $1}' <<<'12|23|11'

或更短的版本:

awk -F\| '$0=$3 $2 $1' <<<'12|23|11'

在这种情况下,输出记录放在一起是真实条件,因此将其打印出来。

在这种特定情况下,stdin可以通过设置 内部变量:

awk -v T='12|23|11' 'BEGIN{split(T,a,"|");print a[3] a[2] a[1]}'

我用了 相当一段时间,但在 这可以通过内部字符串操作来管理。在第一种情况下,原始字符串由内部终止符分割。在第二种情况下,假定字符串始终包含由一个字符分隔符分隔的数字对。

T='12|23|11';echo -n ${T##*|};T=${T%|*};echo ${T#*|}${T%|*}
T='12|23|11';echo ${T:6}${T:3:2}${T:0:2}

在所有情况下的结果是

112312

我认为最终结果应该是awk数组变量引用,无论给出的打印输出示例如何。但是您错过了一个非常简单的bash案例来提供最终结果。T = '12:23:11'; echo $ {T //:}
Daniel Liston

@DanielListon你是对的!谢谢!我不知道在此bash表达式中可以保留尾随/ …
TrueY

4

实际上awk具有称为“输入字段分隔符变量” 链接的功能。这是如何使用它。它实际上不是数组,但是使用内部$变量。对于拆分一个简单的字符串,它更容易。

echo "12|23|11" | awk 'BEGIN {FS="|";} { print $1, $2, $3 }'

3
echo "12|23|11" | awk '{split($0,a,"|"); print a[3] a[2] a[1]}'

应该管用。



1

玩笑?:)

怎么样 echo "12|23|11" | awk '{split($0,a,"|"); print a[3] a[2] a[1]}'

这是我的输出:

p2> echo "12|23|11" | awk '{split($0,a,"|"); print a[3] a[2] a[1]}'
112312

所以我想它毕竟在工作。


是因为字符串的长度吗?从那时起,我的弦长为4000。任何想法
Mohamed Saligh 2011年

1

我知道这是一个古老的问题,但我认为也许有人喜欢我的把戏。特别是由于该解决方案不限于特定数量的项目。

# Convert to an array
_ITEMS=($(echo "12|23|11" | tr '|' '\n'))

# Output array items
for _ITEM in "${_ITEMS[@]}"; do
  echo "Item: ${_ITEM}"
done

输出将是:

Item: 12
Item: 23
Item: 11
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.