17

我一直在这里和其他任何地方阅读有关 bash 中的引号,但我没有帮助解决这个问题。

问题是,我有一个用于循环备份的小脚本。

如果我不使用,那么我的变量 ineval有问题。$OPTIONSrsync

但是如果我确实使用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

有什么方法可以让我使用该变量$CURRENT_DIR而不会出现空格引起的问题?

4

7 回答 7

20
eval rsync --delete-excluded -i $OPTIONS  root@example.com$f "\"$CURRENT_DIR/xxx/$DIR/files\""

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

eval 命令或多或少地将其参数视为输入到 shell 中。因此,如果 you eval command "some thing", basheval使用两个参数执行:commandand some 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\"
于 2011-03-10T00:10:46.520 回答
8

使用 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"
于 2011-03-10T07:44:12.073 回答
4

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

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

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

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"

构造一个 eval 的命令:

% 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不是邪恶的——只是被误解了:)

于 2018-09-29T05:45:08.113 回答
3

我同意戈登

在这种情况下,您不需要 eval (您没有从变量中形成变量名,或者以其他方式动态地制作表达式)

并且您想双引号引用所有可能包含您想要保留的空格的变量引用

但另一个好习惯是总是用 {} 引用变量...

  "${CURRENT_DIR}" 

代替

  $CURRENT_DIR

这消除了任何名称歧义

于 2011-03-10T08:31:20.743 回答
0

我知道您可能已经使用过它,但是,单引号呢?(这个类型 ' ' ) ?

于 2011-03-09T23:51:38.460 回答
-1

CURRENT_DIR="/path/with\ spaces/backup"如果这不起作用,您需要转义空格,然后放双反斜杠CURRENT_DIR="/path/with\\ spaces/backup"

于 2011-03-09T23:54:32.617 回答