ter*_*don 18 shell-script quoting variable
shell 脚本中的一般规则是,除非有令人信服的理由,否则应该始终引用变量。有关您可能想知道的更多详细信息,请查看这个很棒的问答:忘记在 bash/POSIX shell 中引用变量的安全隐患。
但是,请考虑如下函数:
run_this(){
$@
}
Run Code Online (Sandbox Code Playgroud)
应该$@在那里引用还是不引用?我玩了一会儿,找不到任何缺少引号引起问题的情况。另一方面,在传递包含空格的命令作为引用变量时,使用引号会使它中断:
#!/usr/bin/sh
set -x
run_this(){
$@
}
run_that(){
"$@"
}
comm="ls -l"
run_this "$comm"
run_that "$comm"
Run Code Online (Sandbox Code Playgroud)
运行上面的脚本返回:
$ a.sh
+ comm='ls -l'
+ run_this 'ls -l'
+ ls -l
total 8
-rw-r--r-- 1 terdon users 0 Dec 22 12:58 da
-rw-r--r-- 1 terdon users 45 Dec 22 13:33 file
-rw-r--r-- 1 terdon users 43 Dec 22 12:38 file~
+ run_that 'ls -l'
+ 'ls -l'
/home/terdon/scripts/a.sh: line 7: ls -l: command not found
Run Code Online (Sandbox Code Playgroud)
如果我使用run_that $comm而不是,我可以解决这个问题run_that "$comm",但是因为run_this(未加引号)函数同时适用于两者,这似乎是更安全的选择。
那么,在特定情况下$@在其工作是$@作为命令执行的函数中使用时,应该$@引用吗?请解释为什么应该/不应该引用它,并给出一个可以破坏它的数据示例。
mur*_*uru 20
问题在于如何将命令传递给函数:
$ run_this ls -l Untitled\ Document.pdf
ls: cannot access Untitled: No such file or directory
ls: cannot access Document.pdf: No such file or directory
$ run_that ls -l Untitled\ Document.pdf
-rw------- 1 muru muru 33879 Dec 20 11:09 Untitled Document.pdf
Run Code Online (Sandbox Code Playgroud)
"$@"应该在您的run_this函数以正常编写的命令为前缀的一般情况下使用。run_this导致引用地狱:
$ run_this 'ls -l Untitled\ Document.pdf'
ls: cannot access Untitled\: No such file or directory
ls: cannot access Document.pdf: No such file or directory
$ run_this 'ls -l "Untitled\ Document.pdf"'
ls: cannot access "Untitled\: No such file or directory
ls: cannot access Document.pdf": No such file or directory
$ run_this 'ls -l Untitled Document.pdf'
ls: cannot access Untitled: No such file or directory
ls: cannot access Document.pdf: No such file or directory
$ run_this 'ls -l' 'Untitled Document.pdf'
ls: cannot access Untitled: No such file or directory
ls: cannot access Document.pdf: No such file or directory
Run Code Online (Sandbox Code Playgroud)
我不确定应该如何将带空格的文件名传递给run_this.
它是:
interpret_this_shell_code() {
eval "$1"
}
Run Code Online (Sandbox Code Playgroud)
或者:
interpret_the_shell_code_resulting_from_the_concatenation_of_those_strings_with_spaces() {
eval "$@"
}
Run Code Online (Sandbox Code Playgroud)
或者:
execute_this_simple_command_with_these_arguments() {
"$@"
}
Run Code Online (Sandbox Code Playgroud)
但:
execute_the_simple_command_with_the_arguments_resulting_from_split+glob_applied_to_these_strings() {
$@
}
Run Code Online (Sandbox Code Playgroud)
没有多大意义。
如果你想执行ls -l命令(不是ls带有ls和-l作为参数的命令),你会这样做:
interpret_this_shell_code '"ls -l"'
execute_this_simple_command_with_these_arguments 'ls -l'
Run Code Online (Sandbox Code Playgroud)
但是,如果(更有可能)是ls带有ls和-l作为参数的命令,您将运行:
interpret_this_shell_code 'ls -l'
execute_this_simple_command_with_these_arguments ls -l
Run Code Online (Sandbox Code Playgroud)
现在,如果你想执行的不仅仅是一个简单的命令,如果你想做变量赋值、重定向、管道……,只会interpret_this_shell_code做:
interpret_this_shell_code 'ls -l 2> /dev/null'
Run Code Online (Sandbox Code Playgroud)
当然,你总是可以这样做:
execute_this_simple_command_with_these_arguments eval '
ls -l 2> /dev/null'
Run Code Online (Sandbox Code Playgroud)
看着它从在bash / KSH / zsh的角度来看,
$*和$@是一般阵列扩展的一种特殊情况。数组扩展不像普通的变量扩展:
$ a=("a b c" "d e" f)
$ printf ' -> %s\n' "${a[*]}"
-> a b c d e f
$ printf ' -> %s\n' "${a[@]}"
-> a b c
-> d e
-> f
$ printf ' -> %s\n' ${a[*]}
-> a
-> b
-> c
-> d
-> e
-> f
$ printf ' -> %s\n' ${a[@]}
-> a
-> b
-> c
-> d
-> e
-> f
Run Code Online (Sandbox Code Playgroud)
通过$*/${a[*]}扩展,您可以将数组与第一个值连接起来IFS(默认为空格)连接成一个巨大的字符串。如果你不引用它,它会像普通字符串一样被拆分。
对于$@/${a[@]}扩展,行为取决于$@/${a[@]}扩展是否被引用:
"$@"或"${a[@]}"),你会得到等价的
"$1" "$2" "$3" #...
或"${a[1]}" "${a[2]}" "${a[3]}" # ...$@或${a[@]}),你会得到等价的
$1 $2 $3 #...
或${a[1]} ${a[2]} ${a[3]} # ...对于包装命令,您绝对需要带引号的 @扩展 (1.)。
关于 bash(和类似 bash)数组的更多好信息:https : //lukeshu.com/blog/bash-arrays.html
| 归档时间: |
|
| 查看次数: |
720 次 |
| 最近记录: |