我一直在读这里和其他地方的bash引用,但我没有帮助解决这个问题。
问题是,我有一个用于循环备份的小脚本。
如果我不使用eval
,那么$OPTIONS
中的rsync
变量存在问题。
但是,如果我使用eval
,则问题会转到变量$CURRENT_DIR
...
rsync返回以下消息:'意外的本地arg:/ path / with'
我尝试过引用变量$CURRENT_DIR
CURRENT_DIR="/path/with spaces/backup"
DIR="dir_by_project"
f=":/home/project_in_server"
OPTIONS="-avr --exclude 'public_html/cms/cache/**' --exclude 'public_html/cms/components/libraries/cmslib/cache/**' --delete"
eval rsync --delete-excluded -i $OPTIONS root@example.com$f $CURRENT_DIR/xxx/$DIR/files
有没有办法可以使用变量$CURRENT_DIR
而没有空格引起的问题?
答案 0 :(得分:16)
eval rsync --delete-excluded -i $OPTIONS root@example.com$f "\"$CURRENT_DIR/xxx/$DIR/files\""
command "some thing"
使用一个参数some thing
执行命令。引号由shell解析,参数在执行命令时设置为数组。该命令将看到一个参数为某些东西而没有引号。
eval命令或多或少地处理它的参数,就像它们被输入shell一样。所以,如果你eval command "some thing"
,bash用两个参数执行eval
:command
和some thing
(当bash设置参数数组时,再次引用引号)。因此,eval就像你在shell中键入command some thing
一样,这不是你想要的。
我所做的只是为了逃避引号,所以bash直接传递“some thing”包括引用到eval。然后,eval就像您键入command "some thing"
一样。
我的命令中的外部引号并非严格要求,它们只是习惯。你也可以使用:
eval rsync --delete-excluded -i $OPTIONS root@example.com$f \"$CURRENT_DIR/xxx/$DIR/files\"
答案 1 :(得分:7)
使用eval很危险,应尽可能避免使用。在这种情况下,主要问题是您尝试将OPTIONS定义为包含多个单词,而bash变量不能很好地处理这个问题。有一个解决方案:将OPTIONS放在一个数组中,而不是一个简单的变量(并在所有变量引用周围使用双引号),以防止空格被视为单词分隔符。
CURRENT_DIR="/path/with spaces/backup"
DIR="dir_by_project"
f=":/home/project_in_server"
OPTIONS=(-avr --exclude 'public_html/cms/cache/**' --exclude 'public_html/cms/components/libraries/cmslib/cache/**' --delete)
rsync --delete-excluded -i "${OPTIONS[@]}" "root@example.com$f" "$CURRENT_DIR/xxx/$DIR/files"
答案 2 :(得分:3)
答案 3 :(得分:3)
我同意戈登的意见
在这种情况下你不需要eval(你没有从变量形成变量名,或者在运行中制作表达式)
而且你想要引用所有可能有可能保留空格的变量引用
但另一个好习惯是始终用{} ...
来引用变量 "${CURRENT_DIR}"
而不是
$CURRENT_DIR
这会删除任何名称歧义
答案 4 :(得分:0)
我知道你可能已经使用过了,但是,单引号怎么样? (这种类型'')?
答案 5 :(得分:0)
尽管了解如何正确引用事物很重要,但是为了易于使用和防止滑动,我更喜欢使用以下功能:
以下内容通过引用数组的每个元素在参数中保留空格:
function token_quote {
local quoted=()
for token; do
quoted+=( "$(printf '%q' "$token")" )
done
printf '%s\n' "${quoted[*]}"
}
用法示例:
$ token_quote token 'single token' token
token single\ token token
上面,请注意single token
的空格引用为\
。
$ set $(token_quote token 'single token' token)
$ eval printf '%s\\n' "$@"
token
single token
token
$
这表明令牌确实分开存放。
提供一些不受信任的用户输入:
% input="Trying to hack you; date"
构造一个命令进行评估:
% cmd=(echo "User gave:" "$input")
用正确的报价貌似对它进行评估:
% eval "$(echo "${cmd[@]}")"
User gave: Trying to hack you
Thu Sep 27 20:41:31 +07 2018
请注意,您被黑了。 date
被执行,而不是照字面意思打印。
用token_quote()
代替:
% eval "$(token_quote "${cmd[@]}")"
User gave: Trying to hack you; date
%
eval
不是邪恶的-只是被误解了:)
答案 6 :(得分:-1)
如果这不起作用,您需要在CURRENT_DIR="/path/with\ spaces/backup"
中转义空格然后再加上反斜杠CURRENT_DIR="/path/with\\ spaces/backup"