在Bash脚本中,我想创建一个包装命令的函数,在执行它们之前打印它们。
所以,在这样的脚本命令中:
mkdir -p "~/new/dir/tree/"
rsync -e 'ssh -p 22' -av "src/" "user@${HOST}:dest/"
我可以像其他人一样把命令放在其他人面前:
run mkdir -p "~/new/dir/tree/"
run rsync -e 'ssh -p 22' -av "src/" "user@${HOST}:dest/"
我将函数定义为:
run (){
echo -e "FANCY FORMATING CMD> $@ FANCY FORMAT ENDING"
eval "$@"
return $?
}
大多数情况下都能正常工作。当我需要run
前面的命令时,无论是删除run
部分还是取消设置run
,它都可以使用它或不使用它。我的意思是,所有三个命令都应该以完全相同的方式工作:
run original_comand arg1 "arg2 subarg2" etc;
unset run;
run original_comand arg1 "arg2 subarg2" etc;
original_comand arg1 "arg2 subarg2" etc;
但如果我尝试这一行:
run rsync -e 'ssh -p 22' -av "src/" "user@${HOST}:dest/"
-e
参数变为不带引号且实际运行的命令是
run rsync -e ssh -p 22 -av src/ user@${HOST}:dest/
如果我在-e
参数上转义引号,它可能与run命令一起使用,但没有它就行不了。
就我从Bash文档中读到的那样,"$@"
应该有效,但显然我在这里遗漏了一些东西。
答案 0 :(得分:1)
传递给run
的参数在传递给它们时已经经历了各种扩展,并且已经正确定位,因此您不需要eval
来调用{}中的命令{1}}。实际上,在这种情况下使用$1
是错误的(错误地在另一次错误地应用扩展和分词),正如您可以通过大量错误消息所知道的那样。
在eval
中调用命令并将$1
中的所有参数(第一个除外,即"$@"
)传递给它的正确方法是简单地放"${@:2}"
在没有"$@"
的单行上。
答案 1 :(得分:0)
通过调用您的函数,您的参数不会“未引用”。通过将它们放入一个字符串并回显它们,它们是“不加引号”的。引号不是参数的一部分,而是shell中防止分词的特殊字符(在单引号的情况下,变量扩展)。
shell执行分词(引用保护),然后引用删除。这意味着引号将被删除,是的,但参数不会在引用的空格上拆分。
在
utility a "b c"
该实用程序不会将"b c"
作为其第二个参数,而是b c
(不是b
和c
,而是b<space>c
)。你的功能也是如此。
这表明您仍然在函数中获得了正确分隔的参数:
#!/bin/sh
run () {
printf 'Arg: %s\n' "$@"
}
HOST=example.com
run mkdir -p "~/new/dir/tree/"
echo '---'
run rsync -e 'ssh -p 22' -av "src/" "user@${HOST}:dest/"
这会生成
Arg: mkdir
Arg: -p
Arg: ~/new/dir/tree/
---
Arg: rsync
Arg: -e
Arg: ssh -p 22
Arg: -av
Arg: src/
Arg: user@example.com:dest/
正如您所看到的,ssh -p 22
仍作为单独的参数传递给printf
,而不是三个参数。
在你的函数中执行eval "$@"
会做正确的事情。转义引号肯定不做正确的事情,因为它会在实际参数中包含引号(mkdir \"dir\"
会创建一个名为"dir"
的目录,包括引号,以及{ {1}}将rsync -e \"ssh -p 22\"
,rsync
,-e
,"ssh
传递给-p
。
另请注意,22"
未展开,因为它是双引号。请改用~
。