Bash

如何防止命令行上的命令替換?

  • July 31, 2019

我發現當將文本作為輸入寫入另一個程序時,預期文本中雙引號中的任何命令替換都會由 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 刪除了taskbetween \(backslash) and的部分參數-s,以及如何防止 shell 解釋上述單引號命令替換(即'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 *`'"

整個字元串是雙引號的,因此命令替換和其他擴展將在那裡執行。這發生在 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

很可能不是外殼在這樣做。

以及如何防止外殼解釋上述單引號命令替換(即'wc -l *')?

它不是單引號,它是雙引號,旁邊是帶引號的單引號。

使用單引號強引用:

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
)

使用ksh93mksh您可以優化到:

var=$(<<'EOF'
echo '`wc -l *`'
EOF
)

(也可以zsh在.cat``$var``echo 'wc -l *'

fish外殼中,您可以'嵌入'...'其中\'

printf '%s\n' '`wc -l *` and a \' character'

但無論如何```在那裡並不特別,所以:

printf '%s\n' "`wc -l *` and a ' character"

也可以。

在 rc、es 或中,您可以在其中zsh -o rcquotes插入一個with :'``'...'``''

printf '%s\n' '`wc -l *` and a '' character'

請參閱如何將特殊字元用作普通字元?更多細節。

引用自:https://unix.stackexchange.com/questions/532811