unix - awk出乎意料的行为

时间:2011-12-27 03:14:48

标签: linux bash shell unix awk

我在名为'findError.sh'的bash文件中有以下代码:

#!/bin/bash
filename="$1"
formatindicator="\"|\""
echo "$formatindicator"
formatarg="\$1"
echo "$formatarg"
count=`awk -F$formatindicator '{print $formatarg}' $filename | perl -ane '{ if(m/ERROR/) { print } }' | wc -l `
command="awk -F$formatindicator '{print $formatarg}' $filename | perl -ane '{ if(m/ERROR/) { print } }' | wc -l"
echo $command
echo $count

然后我在命令行运行它,如下所示: sh findError.sh test.dat

但它给我一个不同的计数而不是运行被回显的命令?这怎么可能?

即 回显的$命令是:

awk -F"|" '{print $1}' test.dat | perl -ane '{ if(m/ERROR/) { print } }' | wc -l

但回馈的$ count是:

3

但是如果我只是在命令行下面运行这一行(而不是通过脚本) - 结果为0:

awk -F"|" '{print $1}' test.dat | perl -ane '{ if(m/ERROR/) { print } }' | wc -l

示例输入文件(test.dat):

sid|storeNo|latitude|longitude
2|1|-28.03720000
9|2
10
jgn352|1|-28.03ERROR720000
9|2|fdERRORkjhn422-405
0000543210|gfERRORdjk39

注意:将SunOS与bash版本4.0.17一起使用

2 个答案:

答案 0 :(得分:4)

您对格式分隔符周围的引号过于谨慎。

键入时:

awk -F"|" ...

程序(awk)将-F|视为其第一个参数; shell剥去双引号。

当你有:

formatindicator="\"|\""
echo "$formatindicator"
formatarg="\$1"
echo "$formatarg"
count=`awk -F$formatindicator ...`

您在$formatindicator中保留了双引号,因此awk-F"|"视为分隔符,并使用双引号作为分隔符。

使用:

formatindicator="|"
echo "$formatindicator"
formatarg="\$1"
echo "$formatarg"
count=`awk -F"$formatindicator" ...`

不同之处在于shell将引号从-F"$formatindicator"中删除,但在$formatindicator本身包含双引号时不会这样做。

(注意:编辑后保留后引号而不是{(1)}符号,这是(a)首选,(b)在此答案的第一个版本中使用。$(...)表示法不是由SunOS $(...)识别,我相信它被用来执行脚本。/bin/shbash都识别ksh符号,但基本的Bourne shell,{ {1}},在Solaris 10(SunOS 5.10)和更早版本(我没有在Solaris 11上工作)无法识别$(...)。)

我注意到/bin/sh$(...)perl中的任何一个都可用于查找错误行的计数,因此awk管道的三元组用grep管道传输到awk的效率不高。

perl

这是Perl,所以TMTOWTDI;接受你的选择...


侧面讨论

在评论中,我们担心如何解释脚本的各个部分。

wc

让我们将其简化为(使用我的主要答案的一部分):

awk -F"|" '$1 ~ /ERROR/ { count++ } END { print count }' $filename

grep -c ERROR $filename                # simple
grep -c '^[^|]*ERROR[^|]*|' $filename  # accurate

perl -anF"|" -e '$count++ if $F[0] =~ m/ERROR/; END { print "$count\n"; }' $filename

目的是通过formatindicator="|" formatarg="\$1" count=`awk -F$formatindicator '{print $formatarg}' $filename | perl -ane '{ if(m/ERROR/) { print } }' | wc -l ` 选项在命令行(成功发生)上指定分隔符。我希望这个问题是“为什么count=`awk -F"$formatindicator" '{print $formatarg}' $filename` 在单引号内扩展?”。答案是“是吗?”。我想不是。所以,正在发生的事情是,-F正在查看脚本$formatarg。由于awk未分配任何值,因此它等于0,因此脚本会打印{print $formatarg},这是整个输入行。如果它在线上的任何地方匹配ERROR,Perl非常乐意回应该行,并且formatarg不关心行中的内容,因此结果大致与预期相符。只有当$0中的行包含ERROR而不是第一个以管道分隔的字段时,才会出现差异。这将由脚本计算在哪里不应该。

答案 1 :(得分:1)

问题在于使用awk中的外部变量。如果您希望在awk中使用外部变量,请使用awk选项和-vvariable name单行中定义变量,并将external variable分配给它。所以

行 -

count=`awk -F$formatindicator '{print $formatarg}' $filename | perl -ane '{ if(m/ERROR/) { print } }' | wc -l `

应该是 -

count=`awk -v fi="$formatindicator" -v fa="$formatarg" 'BEGIN {FS=fi}{print fa}' "$1" | perl -ane '{ if(m/ERROR/) { print } }' | wc -l `

<强>更新

如评论中所述,$formatarg包含值$1。您需要做的只是存储1,然后将其作为 -

传递
count=`awk -v fi=$formatindicator -v fa="$formatarg" 'BEGIN {FS=fi}{print $fa}' "$1" | perl -ane '{ if(m/ERROR/) { print } }' | wc -l

[jaypal:~/Temp] echo $formatindicator
|
[jaypal:~/Temp] echo $formatarg
1
[jaypal:~/Temp] awk -v fi="$formatindicator" -v fa="$formatarg" 'BEGIN {FS=fi}{print $fa}' data.file
sid
2
9
10
jgn352
9
0000543210

<强>脚本:

#!/bin/bash
filename="$1"
formatindicator="|"
echo "$formatindicator"
formatarg="1"
echo "$formatarg"
count=`awk -v fa="$formatarg" -v fi="$formatindicator" 'BEGIN{FS=fi}{print $fa}' $filename | perl -ane '{ if(m/ERROR/) { print } }' | wc -l `
command="awk -F$formatindicator '{print $formatarg}' $filename | perl -ane '{ if(m/ERROR/) { print } }' | wc -l"
echo $command
echo $count