我希望能够在命令行上传递一个参数来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;
}