eval,变量和引号的Bash问题

Ces*_*sar 12 bash rsync eval quoting

我一直在读这里和其他地方的bash引用,但我没有帮助解决这个问题.

问题是,我有一个小循环进行备份的脚本.

如果我不使用eval那么我有$OPTIONS变量的问题rsync.

但是如果我确实使用eval那么问题就变成了变量$CURRENT_DIR......

rsync返回以下消息:'Unexpected local 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
Run Code Online (Sandbox Code Playgroud)

有没有办法可以使用变量$CURRENT_DIR而没有空格引起的问题?

Eri*_*rik 16

eval rsync --delete-excluded -i $OPTIONS  root@example.com$f "\"$CURRENT_DIR/xxx/$DIR/files\""
Run Code Online (Sandbox Code Playgroud)

command "some thing"使用一个参数执行命令some thing.引号由shell解析,参数在执行命令时设置为数组.该命令将参数视为没有引号的东西.

eval命令或多或少地处理它的参数,就像它们被输入shell一样.所以,如果你eval command "some thing",bash eval用两个参数执行:commandsome thing(再次引用,当bash设置参数数组时).因此,eval就像你command some thing在shell中输入一样,这不是你想要的.

我所做的只是为了逃避引号,所以bash直接传递"某些东西",包括引用到eval.然后,eval就像你打字一样command "some thing".

我的命令中的外部引号并不是严格要求的,它们只是习惯.你也可以使用:

eval rsync --delete-excluded -i $OPTIONS  root@example.com$f \"$CURRENT_DIR/xxx/$DIR/files\"
Run Code Online (Sandbox Code Playgroud)


Gor*_*son 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"
Run Code Online (Sandbox Code Playgroud)


Tom*_*ale 5

通用可重复使用的解决方案

虽然理解如何正确引用事物很重要,但为了易于使用并防止出现失误,我更喜欢使用函数:

以下通过引用数组的每个元素来在参数中保留空格:

function token_quote {
  local quoted=()
  for token; do
    quoted+=( "$(printf '%q' "$token")" )
  done
  printf '%s\n' "${quoted[*]}"
}
Run Code Online (Sandbox Code Playgroud)

用法示例:

$ token_quote token 'single token' token
token single\ token token
Run Code Online (Sandbox Code Playgroud)

上面,请注意 的single token空格被引用为\

$ set $(token_quote token 'single token' token)
$ eval printf '%s\\n' "$@"
token
single token
token
$
Run Code Online (Sandbox Code Playgroud)

这表明令牌确实是分开的。


给定一些不受信任的用户输入:

% input="Trying to hack you; date"
Run Code Online (Sandbox Code Playgroud)

构造一个 eval 命令:

% cmd=(echo "User gave:" "$input")
Run Code Online (Sandbox Code Playgroud)

用看似正确的引用来评估它:

% eval "$(echo "${cmd[@]}")"
User gave: Trying to hack you
Thu Sep 27 20:41:31 +07 2018
Run Code Online (Sandbox Code Playgroud)

请注意,您已被黑客攻击。date是被执行而不是按字面印刷。

相反token_quote()

% eval "$(token_quote "${cmd[@]}")"
User gave: Trying to hack you; date
%
Run Code Online (Sandbox Code Playgroud)

eval不是邪恶的——只是被误解了:)