我发现,当将文本写入另一个程序的输入时,目标文本中双引号中的任何命令替换都会由 shell 解释和扩展
答案中的链接这里指出单引号可用于防止参数扩展或命令替换。但是我发现将命令替换括在单引号中也无法阻止 shell 扩展命令替换
如何防止 shell 将命令替换解释为文本而不是要执行的命令?
演示
$ echo "`wc -l *`"
尝试计算当前目录中所有文件的行数
$ echo "'`wc -l *`'"
结果相同,即统计当前目录下所有文件的行数
更新从这个演示中我发现问题似乎是我引用了单引号。我认为将单引号和`
(反引号)括在双引号中保留字面意思(即抑制)单引号,但不保留引入命令替换的反引号(即反引号)的字面含义。
在我的用例中,需要引用另一个命令的输入。有了这个文档说:
单引号内不能出现单引号
当单引号命令替换位于(双)引号字符串内时,如何防止单引号命令替换被扩展?除了使用反斜杠转义之外,应该有一种方法可以做到这一点
实际情况
在程序中,我使用的将任务描述分成单独行的唯一方法是将描述用双引号引起来:
$ task add "first line doesn\'t say much
Second line says a lot but part of this line does not appear in the resulting description 'truncate -s0 !(temp_file | temp_dir)' truncates all files to 0 bytes as shown by: '`wc -l *`'"
结果描述:
first line doesn\ -s0 !(temp_file | temp_dir)' truncates all files to 0 bytes as shown by: 0 file1 10 file2 0 directory1 0 directory2 502 file3 123 file4 162 file5 0 directory3
如你看到的
't say much
Second line says a lot but part of this line does not appear in the resulting description 'truncate
描述中缺少,并且 shell 已将其解释'wc -l *'
为命令替换,从而将当前目录中所有文件的行数作为描述的一部分包括在内
是什么导致 shell 删除(反斜杠) 和task
之间的参数部分,以及如何防止 shell 解释上述单引号命令替换(即)?\
-s
'`wc -l *`'
答案1
使用单引号强引用:
printf '%s\n' '`wc -l *`'
如果您还想在传递给 的参数中包含单引号printf
,则需要为其'
本身使用不同的引号,例如:
printf '%s\n' '`wc -l *` and a '"'"' character'
或者:
printf '%s\n' '`wc -l *` and a '\'' character'
其他替代方法包括`
在双引号内使用反斜杠转义:
printf '%s\n' "\`wc -l *\` and a ' character"
或者是`
一些扩展的结果:
backtick='`'
printf '%s\n' "${backtick}wc -l *${backtick} and a ' character"
另请注意:
cat << 'EOF'
`wc -l *` and a ' character and a " character
EOF
输出任意文本,而不必担心引用(请注意第一个周围的引号EOF
)。
您还可以这样做:
var=$(cat << 'EOF'
echo '`wc -l *`'
EOF
)
使用ksh93
或mksh
您可以优化到:
var=$(<<'EOF'
echo '`wc -l *`'
EOF
)
(也适用于zsh
,但仍然cat
在子 shell 中运行)用于$var
包含字面意义echo '`wc -l *`'
。
在shell中fish
,您可以'
嵌入'...'
:\'
printf '%s\n' '`wc -l *` and a \' character'
但无论如何`
那里并不特别,所以:
printf '%s\n' "`wc -l *` and a ' character"
也可以。
在 rc、es 或 中,您可以在 with内zsh -o rcquotes
插入:'
'...'
''
printf '%s\n' '`wc -l *` and a '' character'
看如何像普通字符一样使用特殊字符?更多细节。
答案2
在这里(添加换行符),
$ task add "first line doesn\'t say much
Second line says a lot but part of this line does not appear in the
resulting description 'truncate -s0 !(temp_file | temp_dir)' truncates
all files to 0 bytes as shown by: '`wc -l *`'"
整个字符串用双引号引起来,因此命令替换和其他扩展将在那里运行。这种情况发生在 shell 中,在task
看到该字符串之前,您需要使用反斜杠或将该部分放在单引号中来防止它。
例如
$ printf "%s\n" "...shown by: '\`wc -l *\`'"
...shown by: '`wc -l *`'
所以,
task add "...shown by: '\`wc -l *\`'"
会将字符串传递...shown by: '`wc -l *`'
给task
.这取决于它做什么。
如果您不想使用反斜杠,可以按照以下方法将其放在单引号中:
# aaaaaaaaaaaaaaaaBBBBBBBBBBBaaa
$ printf "%s\n" "...shown by: '"'`wc -l *`'"'"
...shown by: '`wc -l *`'
(a
' 标记双引号部分,B
' 标记单引号部分。它们只是在 shell 命令行上连接起来。单引号文字位于双引号字符串内。)
至于单引号和反斜杠,您不需要转义双引号内的单引号,实际上反斜杠将保留在那里:
$ printf "%s\n" "foo'bar"
foo'bar
$ printf "%s\n" "foo\'bar"
foo\'bar
从您所显示的情况来看,似乎task
至少从参数中删除了第一个单引号字符串(之后加上一个单词,因为删除的部分是't say much ... 'truncate
)
这壳将要不是这样做,效果很好:
$ printf "%s\n" "a 'quoted string' to test"
a 'quoted string' to test
是什么导致 shell 删除(反斜杠) 和,
task
之间的参数部分\
-s
很可能不是 shell 这样做的。
以及如何防止 shell 解释上述单引号命令替换(即
'`wc -l *`'
)?
它不是单引号,而是双引号,旁边有单引号。
答案3
您可以使用反斜杠转义反引号,如下所示:
echo "\`wc -l *\`"
答案4
逃离任何任意字符序列,以便不处理特殊字符并且不进行替换:
- 将每个替换
'
为'\''
- 将结果字符串括在单引号中。
例如:
`wc -l 'my file.txt'`
成为
'`wc -l '\''my file.txt'\''`'
请注意,如果字符串以 开头或结尾'
,则最终会在开头/结尾处出现''
,这不会执行任何操作并且可以将其删除,但将其保留在那里是有效的(例如,为了保持字符串转义函数简单)。