awk认为双引号string作为一个标记,并忽略之间的空间

数据文件 – data.txt:

ABC "I am ABC" 35 DESC DEF "I am not ABC" 42 DESC 

cat data.txt | awk '{print $2}'

将导致“我”,而不是被引用的string

如何使awk忽略引号内的空格,并认为它是一个单一的标记?

是的,这可以在awk中很好地完成。 很容易得到所有的领域没有任何严重的黑客攻击。

(这个例子在一个真正的Awk和gawk中都有效。)

 { split($0, a, "\"") $2 = a[2] $3 = $(NF - 1) $4 = $NF print "and the fields are ", $1, "+", $2, "+", $3, "+", $4 } 

尝试这个:

 $ cat data.txt | awk -F\" '{print $2}' I am ABC I am not ABC 

我已经把一个将$ 0重新分割成一个名为B的数组的函数一起缩小了。双引号之间的空格不作为字段分隔符。 适用于任何领域,混合引用和不引用的字段。 开始:

 #!/usr/bin/gawk -f # Resplit $0 into array B. Spaces between double quotes are not separators. # Single quotes not handled. No escaping of double quotes. function resplit( a, l, i, j, b, k, BNF) # all are local variables { l=split($0, a, "\"") BNF=0 delete B for (i=1;i<=l;++i) { if (i % 2) { k=split(a[i], b) for (j=1;j<=k;++j) B[++BNF] = b[j] } else { B[++BNF] = "\""a[i]"\"" } } } { resplit() for (i=1;i<=length(B);++i) print i ": " B[i] } 

希望能帮助到你。

这个问题的最佳答案只适用于带有单引号字段的行。 当我发现这个问题时,我需要一些可以用于任意数量的引用字段的东西。

最后,我在另一个线程中找到了Wintermute的一个答案 ,他为这个问题提供了一个很好的一般化的解决方案。 我刚刚修改它删除引号。 请注意,在运行下面的程序时,需要用-F\"调用awk。

 BEGIN { OFS = "" } { for (i = 1; i <= NF; i += 2) { gsub(/[ \t]+/, ",", $i) } print } 

这是通过观察数组中的每个其他元素在用“-character”分隔的引号内部的,因此它用逗号代替不用引号括起来的空格。

然后,您可以轻松链接另一个awk实例,以执行您所需的任何处理(只需再次使用字段分隔符开关-F, )即可。

请注意,如果第一个字段被引用,这可能会中断 – 我还没有测试过。 如果是这样的话,应该很容易通过添加一个if语句来从2开始,而不是1,如果该行的第一个字符是“。

另一种方法是使用FPAT变量,它定义了描述每个字段内容的正则表达式。

将这个AWK脚本保存为parse.awk

 #!/bin/awk -f BEGIN { FPAT = "([^ ]+)|(\"[^\"]+\")" } { print $2 } 

使用chmod +x ./parse.awk使其可执行,并将数据文件解析为./parse.awk data.txt

 "I am ABC" "I am not ABC" 

好吧,如果你真的想要所有的三个领域,你可以得到他们,但需要很多的管道:

 $ cat data.txt | awk -F\" '{print $1 "," $2 "," $3}' | awk -F' ,' '{print $1 "," $2}' | awk -F', ' '{print $1 "," $2}' | awk -F, '{print $1 "," $2 "," $3}' ABC,I am ABC,35 DEF,I am not ABC,42 

通过最后一个管道,你有三个领域做任何你想做的事情。

这就像我最终得到的工作,这是我的项目更通用。 注意它不使用awk。

 someText="ABC \"I am ABC\" 35 DESC '1 23' testing 456" putItemsInLines() { local items="" local firstItem="true" while test $# -gt 0; do if [ "$firstItem" == "true" ]; then items="$1" firstItem="false" else items="$items $1" fi shift done echo "$items" } count=0 while read -r valueLine; do echo "$count: $valueLine" count=$(( $count + 1 )) done <<< "$(eval putItemsInLines $someText)" 

哪些产出:

 0: ABC 1: I am ABC 2: 35 3: DESC 4: 1 23 5: testing 6: 456