作为参数传递给 gawk 的转义序列未解释

作为参数传递给 gawk 的转义序列未解释

我希望能够在命令行上传递一个参数来gawk评估转义序列。

问题:

$ gawk 'BEGIN { print ARGV[1]; }' '\t'
\t

相反,我想获得一个实际的制表符。

来自gawk 文档:

对于字符串常量和正则表达式常量,前面列表中的转义序列始终首先处理。一旦 awk 读取您的程序,这种情况就会很早就发生。

如何解释命令行参数中的字符转义?

最终目标是myscript.awk --sep '\t',其中分隔符是格式字符串,因此传递文字制表符不是一个选项。我也熟悉在 bash 中执行此操作的简单方法,但我对在 [g]awk 中执行此操作的方法感兴趣。

答案1

如何打印命令行参数的未转义版本?

print ARGV[1]

问题是您不想要未转义的命令行参数。你想解释它。您正在传递\t(两个字符的字符串反斜杠,小写 T),并且您希望将其转换为反斜杠。您需要手动执行此操作。只是转换\t为制表符很容易 -gsub(/\\t/, "\t")但如果您还想支持八进制转义,并在不可识别的字符之前删除反斜杠,这在 awk 中会很麻烦。

split ARGV[1], a, "\\";
s = a[1]; delete a[1];
for (x in a) {
    if (skip_next) {
        skip_next = 0;
    } else if (x == "") {
        s = s "\\";
        skip_next = 1;
    } else if (x ~ /^[0-7][0-7][0-7]/) {
        s = s sprintf("%c", 64*substr(x,1,1) + 8*substr(x,2,1) + substr(x,3,1));
        sub(/^.../, x);
    } else if (x ~ /^[0-7][0-7]/) {
        s = s sprintf("%c", 0 + 8*substr(x,1,1) + substr(x,2,1));
        sub(/^../, x);
    } else if (x ~ /^[0-7]/) {
        s = s sprintf("%c", 0 + substr(x,1,1));
        sub(/^./, x);
    } else {
        sub(/^a/, "\a", x) ||
        sub(/^b/, "\b", x) ||
        sub(/^n/, "\n", x) ||
        sub(/^r/, "\r", x) ||
        sub(/^t/, "\t", x) ||
        sub(/^v/, "\v", x);
    }
    s = s x;
}

printf(警告:未经测试的代码!)您可以在子 shell 中调用,而不是使用这种复杂的代码。即使当字符串可能是多行时,这也不是那么容易做到的。

s = ARGV[1]
gsub(/'/, "'\\''", s)
cmd = "printf %b '" s "'."
s = ""
while ((cmd | getline line) > 0) s = s line "\n"
sub(/..$/, "", s)

请注意,当您编写"\t"awk 脚本时,这是一个包含制表符的字符串。 awk 语法是这样的:反斜杠在字符串文字中具有特殊含义。注:在一个字符串字面量,不在一个细绳。如果字符串包含反斜杠,那只是另一个字符。源代码片段"\t"由四个字符组成,是一个表达式,其值为包含制表符的单字符字符串,就像源代码片段2+2由三个字符组成,是一个值为数字的表达式一样4

awk 脚本最好将分隔符参数作为文字字符串。这将使其更易于使用:您的接口要求调用者转义参数中的反斜杠。如果您希望分隔符是制表符,请传递实际的制表符。

答案2

首先,您实际上并没有将选项卡传递到您的awk.请记住 shell 会计算参数将它们传递给引号awk'\t'在引号中被评估为\后跟一个文字\t

$ set -x
$ gawk 'BEGIN { print ARGV[1]; }' '\t'
+ gawk 'BEGIN { print ARGV[1]; }' '\t'
\t

正如您在上面所看到的,您没有传递一个选项卡,gawk因此您很难期望它打印一个选项卡。与下面的版本进行比较,该版本确实传递了一个选项卡:

$ gawk 'BEGIN { print ARGV[1]; }' "$(printf '\t')"
++ printf '\t'
+ gawk 'BEGIN { print ARGV[1]; }' ' '  ## note the tab
                         ## This line contains a printed tab

或者,您可以将选项卡作为变量传递:

gawk -v t='\t' 'BEGIN {print t}'

在这里,'\t'是由 awk 而不是 shell 扩展的,因此选项卡被正确解释。

答案3

解决方案是使用getline.

文件内:

BEGIN { 
    sep = ARGV[1]
    gsub(/'/, "'\\''", sep);
    gsub(/%/, "%%", sep);
    "printf -- '" sep "'" | getline sep; 
    printf sep;
}

相关内容