【问题标题】:unix - awk unexpected behaviourunix - awk 意外行为
【发布时间】:2011-12-27 03:14:47
【问题描述】:

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

注意事项:使用带有 bash 版本 4.0.17 的 SunOS

【问题讨论】:

    标签: linux bash shell unix awk


    【解决方案1】:

    您对格式分隔符周围的引号过于小心了。

    当你输入时:

    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 本身包含双引号时不会这样做。

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

    我注意到perlawkgrep 中的任何一个都可用于自行查找错误行数,因此awk 的三元组通过管道传递到perl 传递到@987654345 @ 效率不高。

    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;随便挑吧……


    边讨论

    在 cmets 中,我们担心脚本的各个部分是如何被解释的。

    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 匹配,并且wc 不太关心行中的内容,因此结果与预期的差不多。唯一存在差异的情况是来自$filename 的行在第一个管道分隔字段之外包含错误。这将被脚本计算在内。

    【讨论】:

    • 不像您声称的那样使用bash - 这是与SunOS /bin/sh 一起运行的,它不是bash 并且不识别$( ... ) 作为使用反引号的替代方法。改用反引号。
    • 但是awk 不会接受这样的外部变量,对吗?难道我们不必通过-v 路由在awk 中分配the external variables 吗?
    • @JaypalSingh 因为他使用的是-F 选项而不是awk 变量FS,所以$formatindicator 将被shell 扩展。事实上,只要该变量不在单引号内,您仍然可以在 awk 脚本中使用 shell 扩展变量。但是,在 awk 脚本周围使用双引号将要求您转义几乎所有对 shell 特殊的东西,这就是 awk 允许 -v 的原因。另一种也是我更喜欢的方法是将变量放在末尾,如var="|";awk '{print}' RS="$var"
    • @JaypalSingh:我看不出外部变量在哪里出现。我正在使用awk 命令行选项来设置字段分隔符;就这样。我什至没有考虑设置任何变量。
    • True 但Field delimitervariable($formatindicator) 获取值,该awk 外部定义。我很困惑。我一直认为如果 awk 需要从定义在其范围之外的变量中获取值,它需要使用 -v option 并将变量分配给它的内部变量。
    【解决方案2】:

    问题在于在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 `
    

    更新:

    如 cmets 中所述,$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
    

    【讨论】:

    • 不应该'{print fa}''{print $fa}'
    • 好吧,但是格式参数变量应该存储为 1 而不是 $1。
    猜你喜欢
    • 1970-01-01
    • 2016-08-02
    • 2014-08-17
    • 1970-01-01
    • 2021-11-11
    • 1970-01-01
    • 2017-08-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多