繁体   English   中英

UNIX-AWK意外行为

[英]unix - awk unexpected behaviour

我在名为“ 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

但这给了我与运行回显命令不同的计数? 这怎么可能?

即回显的$ command是:

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一起使用

您对格式定界符的引号过于谨慎。

当您键入:

awk -F"|" ...

程序( awk )看到-F| 作为第一个论点; 外壳会去除双引号。

当你有:

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" ...`

区别在于外壳程序将-F"$formatindicator"引号引起来,但是当$formatindicator本身包含双引号时,shell不会这样做。

(注:经过编辑以保留反引号,而不是(a)首选的$(...)表示法,并且在该答案的第一版中使用了(b)。 $(...)表示法无法识别我相信,SunOS /bin/sh是用来执行脚本的bashksh识别$(...)表示法,但是在Solaris 10(SunOS)上可以使用基本的Bourne shell /bin/sh 。 5.10)和更早的版本(我还没有接触过Solaris 11)无法识别$(...) 。)

我注意到,可以使用perlawkgrep中的任何一个自行查找错误行的数量,因此,将awk的三元组通过管道传输到perl传输到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

是Perl,所以是TMTOWTDI 选择...


侧面讨论

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

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正在查看脚本{print $formatarg} 由于formatarg没有分配任何值,所以它等效于0,因此脚本将输出$0 ,这是整个输入行。 如果Perl在行中的任何地方匹配ERROR,Perl都会很高兴地回显该行,并且wc不在乎行中的内容,因此结果与预期的差不多。 唯一的差异是$filename中的行在第一个以竖线分隔的字段之外的地方包含ERROR。 脚本会在不应该计数的位置进行计数。

问题在于在awk使用外部变量。 如果希望在awk使用外部变量,请使用-v选项和variable nameawk一线式中定义一个变量,然后将其分配给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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM