为什么双引号 awk 命令替换在 csh 中失败

why is a double-quoted awk command substitution failing in csh

使用Cshell,如下命令行

set pf = "`awk -v var=$pd '{if(<0) print var, , }' test.txt`"

returns awk 中的一个错误:

awk: {if( <0) print var, , } syntax error. 

这尤其令人费解,因为命令本身没有任何问题:

awk -v var=$pd '{if(<0) print var, , }' test.txt

有没有办法可以将单个 Awk 命令行的所有输出存储到单个变量中?上述失败的原因是什么?

经过一些修补,我只能得出结论,它是那些 C-Shell 怪癖之一。 C-shell(cshtcsh)显然因其 peculiarities 而臭名昭著,我相信这正是这里发生的事情。以下是一些基于 OP 之谜的示例。

未引用:

$ set a = `echo a_b c | awk '{print }'` ; echo $a
a_b
$ set a = `echo a_b c | awk '{print ,2}'` ; echo $a
a_b 2
$ set a = `echo a_b c | awk '{print  OFS 2}'` ; echo $a
a_b 2

引用:

$ set a = "`echo a_b c | awk '{print }'`" ; echo $a
a_b c
$ set a = "`echo a_b c | awk '{print ,2}'`" ; echo $a
awk: cmd. line:1: {print ,2}
awk: cmd. line:1:        ^ syntax error
$ set a = "`echo a_b c | awk '{print  OFS 2}'`" ; echo $a
2

因此,在双引号示例中,</code> 看起来像是被一个空字符串替换了。这解释了为什么第一种情况打印整行 <code>a_b c 而第三种情况只打印数字 2。第二个失败,因为 Awk 语句 print ,2 无效,而第一个作为 print 相当于 Awk 中的 print [=23=]

如果你玩多一点,你实际上会注意到 C-shell 试图进行变量替换。你实际上不需要在上面的所有内容中使用 set ,只是一个简单的双引号命令替换。下面的例子完全展示了这是多么疯狂:

$ echo [=12=]
csh
$ echo "`echo a_b c | awk '{print [=12=]}'`"

$ echo "`echo a_b c | awk -v csh=foo '{print [=12=]}'`"
foo
$ echo `echo a_b c | awk -v csh=foo '{print [=12=]}'`
a_b c

因此,您看到 C-Shell 正在执行变量替换,并且 [=25=] 正在被字符串 csh 替换。 但仅限双引号版本!

那么,为什么会这样呢?原因是双引号。双引号字符串允许变量替换,忽略双引号字符串中嵌套引号的使用。因此,即使 Awk 行在向后引用的字符串中用单引号引起来,双引号仍然会在 $n 上进行变量替换。这与 Bash:

相反
$ echo [=13=]
bash
$ echo "`echo a_b c | awk -v csh=foo '{print [=13=]}'`"
a_b c

Lexical structure

Furthermore, all Substitutions (see below) except History substitution can be prevented by enclosing the strings (or parts of strings) in which they appear with single quotes or by quoting the crucial character(s) (e.g., $ or ` for Variable substitution or Command substitution respectively) with \. (Alias substitution is no exception: quoting in any way any character of a word for which an alias has been defined prevents substitution of the alias. The usual way of quoting an alias is to precede it with a backslash.) History substitution is prevented by backslashes but not by single quotes. Strings quoted with double or backward quotes undergo Variable substitution and Command substitution, but other substitutions are prevented.

Quoting complex strings, particularly strings which themselves contain quoting characters, can be confusing. Remember that quotes need not be used as they are in human writing! It may be easier to quote not an entire string, but only those parts of the string which need quoting, using different types of quoting to do so if appropriate.

source: man csh

那么,如何解决这个问题? 虽然 C-shell 是完全不直观的,并且在精神上是一个引用噩梦,但可以通过以下方式解决问题提前终止引用并在短时间内从双引号更改为单引号。

$ echo "`echo a_b c | awk -v csh=foo '{print "'[=14=],'"}'`"
a_b c a_b
$ echo "`echo a_b c | awk -v csh=foo '"'{print [=14=],}'"'`"
a_b c a_b

解法:所以在这之后,我想我们可以得出结论

$ set pf = "`awk -v var=$pd '"'{if(<0) print var, , }'"' test.txt`"

可能会解决问题。