eval,变量和引号的Bash问题

时间:2011-03-09 23:47:38

标签: bash eval rsync quoting

我一直在读这里和其他地方的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而没有空格引起的问题?

7 个答案:

答案 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用两个参数执行evalcommandsome 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"