为什么这个例子如此冗长和复杂?这说明了这个问题:
$ echo "" | awk '{s="a\t%s"; printf s"\n","b"}'
a b
$ echo "a\t%s" | awk '{s=$0; printf s"\n","b"}'
a\tb
在第一种情况下,字符串“a\t%s”是字符串文字,因此会被解释两次 - 一次是在 awk 读取脚本时,另一次是在执行脚本时,因此\t
在第一次传递时展开,然后在执行时 awk 在格式化字符串中有一个文本制表符。
在第二种情况下,awk 在格式化字符串中仍然具有字符反斜杠和 t - 因此行为不同。
您需要一些东西来解释这些转义字符,一种方法是调用 shell 的 printf 并读取结果(根据 @EtanReiser 的出色观察进行更正,我在应该使用单引号的地方使用了双引号,在这里由 \047 实现,以避免 shell 扩展):
$ echo 'a\t%s' | awk '{"printf \047" $0 "\047 " "b" | getline s; print s}'
a b
如果你不需要变量中的结果,你可以调用system()
.
如果您只是想扩展转义字符,那么您不需要提供%s
shell 中的参数printf
打电话,你只需要逃避所有%
s(注意已经逃逸的%
s).
你可以调用 awk 而不是 shellprintf
如果你更喜欢。
请注意,这种方法虽然笨拙,但比调用eval
这可能只是执行一个输入行,例如rm -rf /*.*
!
在 Arnold Robbins(gawk 的创建者)和 Manuel Collado(另一位著名的 awk 专家)的帮助下,以下脚本将扩展单字符转义序列:
$ cat tst2.awk
function expandEscapes(old, segs, segNr, escs, idx, new) {
split(old,segs,/\\./,escs)
for (segNr=1; segNr in segs; segNr++) {
if ( idx = index( "abfnrtv", substr(escs[segNr],2,1) ) )
escs[segNr] = substr("\a\b\f\n\r\t\v", idx, 1)
new = new segs[segNr] escs[segNr]
}
return new
}
{
s = expandEscapes($0)
printf s, "foo", "bar"
}
.
$ awk -f tst2.awk <<<"hello: %s\nworld: %s\n"
hello: foo
world: bar
或者,这应该在功能上等效,但不是特定于 gawk 的:
function expandEscapes(tail, head, esc, idx) {
head = ""
while ( match(tail, /\\./) ) {
esc = substr( tail, RSTART + 1, 1 )
head = head substr( tail, 1, RSTART-1 )
tail = substr( tail, RSTART + 2 )
idx = index( "abfnrtv", esc )
if ( idx )
esc = substr( "\a\b\f\n\r\t\v", idx, 1 )
head = head esc
}
return (head tail)
}
如果您愿意,可以将 split() RE 更改为
/\\(x[0-9a-fA-F]*|[0-7]{1,3}|.)/
以及之后的十六进制值\\
:
c = sprintf("%c", strtonum("0x" rest_of_str))
对于八进制值:
c = sprintf("%c", strtonum("0" rest_of_str))