Bash 的 eval、变量和引号问题

发布于 2024-10-21 13:59:02 字数 847 浏览 2 评论 0原文

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

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

如果我不使用eval,那么我就会遇到rsync中的$OPTIONS变量的问题。

但如果我确实使用 eval 那么问题就出在变量 $CURRENT_DIR...

rsync 返回以下消息:“意外的本地参数:/path/with”

I”已经尝试了引用变量 $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  [email protected]$f $CURRENT_DIR/xxx/$DIR/files

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

I've been reading about quotes in bash here and everywhere else, but i got no help solving this problem.

The thing is, I have a little script for doing backups in a loop.

If I don't use eval then I have problems with $OPTIONS variable in rsync.

But if I do use eval then the problem goes to the variable $CURRENT_DIR...

rsync returns the following message: 'Unexpected local arg: /path/with'

I've tried every way of quoting the variable $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  [email protected]$f $CURRENT_DIR/xxx/$DIR/files

Is there any way that i can use the variable $CURRENT_DIR without problems caused by spaces?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

遗失的美好 2024-10-28 13:59:02
eval rsync --delete-excluded -i $OPTIONS  [email protected]$f "\"$CURRENT_DIR/xxx/$DIR/files\""

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

eval 命令对待它的参数或多或少就像它们被输入到 shell 中一样。因此,如果您eval命令“some thing”,bash会使用两个参数执行evalcommandsome thing (当 bash 设置参数数组时,引号再次被吃掉)。因此,eval 的行为就好像您在 shell 中输入了命令某些内容,这不是您想要的。

我所做的只是转义引号,以便 bash 从字面上传递“一些东西”,包括 eval 的引号。然后,eval 的行为就像您键入命令“some thing”

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

eval rsync --delete-excluded -i $OPTIONS  [email protected]$f \"$CURRENT_DIR/xxx/$DIR/files\"
eval rsync --delete-excluded -i $OPTIONS  [email protected]$f "\"$CURRENT_DIR/xxx/$DIR/files\""

command "some thing" executes command with one argument some thing. The quotes are parsed by the shell and arguments are setup as an array when executing the command. The command will see an argument as some thing without the quotes.

The eval command treats its arguments more or less as if they were typed into the shell. So, if you eval command "some thing", bash executes eval with two arguments: command and some thing (again the quotes are eaten while bash sets up the array of arguments). So, eval then acts as if you typed command some thing in the shell, which is not what you want.

What I did was simply to escape the quotes, so that bash passes literally "some thing" including the quotes to eval. eval then acts as if you typed command "some thing".

The outer quotes in my command is not strictly required, they're just habit. You could just as well use:

eval rsync --delete-excluded -i $OPTIONS  [email protected]$f \"$CURRENT_DIR/xxx/$DIR/files\"
羞稚 2024-10-28 13:59:02

使用 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[@]}"  "[email protected]$f" "$CURRENT_DIR/xxx/$DIR/files"

Using eval is dangerous, and should be avoided whenever possible. In this case, the primary problem is that you're trying to define OPTIONS as containing multiple words, and bash variables don't handle this very well. There is a solution: put OPTIONS in an array, instead of a simple variable (and use double-quotes around all variable references, to keep spaces from getting treated as word separators).

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[@]}"  "[email protected]$f" "$CURRENT_DIR/xxx/$DIR/files"
花心好男孩 2024-10-28 13:59:02

通用可重用解决方案

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

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

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

示例用法:< /strong>

$ token_quote token 'single token' token
token single\ token 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 并不是邪恶的 - 它只是被误解了:)

Generic reusable solution

While understanding how to correctly quote things is important, for ease of use and to prevent slip-ups I prefer to use a function:

The following keeps spaces in arguments by quoting each element of array:

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

Example usage:

$ token_quote token 'single token' token
token single\ token token

Above, note the single token's space is quoted as \.

$ set $(token_quote token 'single token' token)
$ eval printf '%s\\n' "$@"
token
single token
token
$

This shows that the tokens are indeed kept separate.


Given some untrusted user input:

% input="Trying to hack you; date"

Construct a command to eval:

% cmd=(echo "User gave:" "$input")

Eval it, with seemingly correct quoting:

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

Note you were hacked. date was executed rather than being printed literally.

Instead with token_quote():

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

eval isn't evil - it's just misunderstood :)

ヅ她的身影、若隐若现 2024-10-28 13:59:02

我同意戈登的观点,

在这种情况下,您不需要 eval (您不是从变量形成变量名,或者以其他方式动态创建表达式)

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

但是另一个好习惯是始终使用 {} ...

  "${CURRENT_DIR}" 

而不是

  $CURRENT_DIR

引用变量这可以消除任何名称歧义

I agree with Gordon

You have no need for eval in this case (you are not forming a variable name from variables, or otherwise making an expression on the fly)

And and you want to double-quote all variable referensces that COULD have spaces that you would want to preserve

But Another good habit is to always refrence variables with {} ...

  "${CURRENT_DIR}" 

instead of

  $CURRENT_DIR

This removes any name ambiguity

摘星┃星的人 2024-10-28 13:59:02

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

I know probably you have used it already, but, what about single quotes? (this type ' ' ) ?

玩世 2024-10-28 13:59:02

您需要在 CURRENT_DIR="/path/with\spaces/backup" 中转义空格,如果这不起作用,则添加双反斜杠 CURRENT_DIR="/path/with\\spaces/backup ”

You need to escape space in CURRENT_DIR="/path/with\ spaces/backup" if this doesn't work then put double backslash CURRENT_DIR="/path/with\\ spaces/backup"

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文