我正在尝试在 bash 脚本中执行一个字符串。字符串包含变量(因此我可以根据情况替换一些 CLI 参数)。
问题是当我使用包含变量时--argument=value
,一切都会崩溃,并给出错误:
Missing trailing-' in remote-shell command.
示例代码:
#!/bin/bash
rsync_path="/usr/bin/rsync -azhrP"
rsync_ssh_cmd="--rsh='ssh -o Compression=no'"
`$rsync_path /path/to/file $rsync_ssh_cmd username@localhost:/tmp`
如果我只是echo
这段代码中的最后一个字符串,它看起来不错。但是,如果我尝试从脚本执行该字符串,它不会按预期执行。
我找到了一个解决方法 - 如果我尝试一切都会好起来
eval "my-command-string"
代替
`my-command-string`
或者
(my-command-string)
为什么会这样呢?我怎样才能(我真的应该吗?)避免使用eval
?
我使用 bash 4.3.11(1)-release (x86_64-pc-linux-gnu)
(Kubuntu 14.04.3)。
答案1
好的。您可以eval
通过想出一些其他方法来两次评估字符串来避免使用。无论你做什么,你都需要两次评估因为你的要求是解析一个解析。
我的意思是:
this is 'a parsed" command string"'
^ 计算结果为 3字:
1: this 2: is 3: a parsed" command string"
3 中的引号并不重要——它们根本不重要,因为它们只是单词中的某些字符。'
原始命令行中使用的语法引号做过很重要,因为它们被 shell 的解析器解释来分隔单词。其中的任何内容都只是单词的一部分。
所以,如果你想得到更多的的话来自a单身的,预解析的word,你必须将它作为输入返回给 shell。这就是它的全部内容 — shell 处理输入上的输入引号。引号用于以有意义的方式转义和分隔输入。如果贝壳继续这样下去,那将毫无意义,而且相当可怕。最终全部输入将被评估为零!
所以你需要一个第二次评价。你无法从$(
命令替换中得到它)
中得到它,因为它们不处理输入,他们处理输出。任何副作用$
词扩展您可能会将其等同于更多的生成字事实并非如此——也就是说场地-分裂。场地- 分裂$IFS
和*
通配由 shell 执行后输入字在解析过程中已经被定界。
现在eval
的全部目的是eval
乌阿特作为输入shell 已经接收到的一些字符串作为输入。这是确切地这是正确的工具,但如果您坚持的话,我们也可以采用其他方式。
一种方法是.
获取自己的输出。
string="printf '<%s>\n' 'these are' 'some words' 'i will evaluate again'"
. /dev/fd/0 <<!
$string
!
在该示例中,外壳评估作为输入它自己的输出。有用:
<these are>
<some words>
<i will evaluate again>
您可以通过调用另一个 shell 来评估您的输出作为其输入来完成此操作:
sh -c "$string"
...输出是相同的。你可以延迟通过定义 shell 来输入alias
:
alias evaled="$string"
evaled
...同样的输出。
最多直接的方法是:
eval "$string"
效果与所有其他示例几乎相同,但这次我实际上是使用命令完成的命名的为了我想做的事。
eval
因为人们不明白它的作用或为什么要使用它,所以受到了不好的批评。问题是,如果您不了解这样做的效果,则不应使用它。并且很容易迷失方向:命令被评估两次——这比正常情况要多——而且一对于大多数人来说,时间通常足以浏览此网站。
所以,你应该eval
在这种情况下使用做你想做的这件事,因为你想做的是eval
一个字符串。我不妄下判断为什么你想要eval
字符串,虽然我希望有更好的方法来做到这一点,但是当你想要 shell 字符串的内容时评价的作为 shell 输入,您应该使用eval
.
答案2
由于嵌入引号,您遇到此问题:Bash常见问题解答
这是工作环境:
#!/bin/bash
rsync_path="/usr/bin/rsync -azhrP"
rsync_ssh_cmd=(--rsh='ssh \-o Compression=no')
`$rsync_path /path/to/file $rsync_ssh_cmd root@localhost:/tmp`
[概念证明]
[root@localhost ~]# bash -x /tmp/c.sh
+ rsync_path='/usr/bin/rsync -azhrP'
+ rsync_ssh_cmd=(--rsh='ssh \-o Compression=no')
++ /usr/bin/rsync -azhrP /path/to/file --rsh=ssh '\-o' Compression=no root@localhost:/tmp
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@ WARNING: UNPROTECTED PRIVATE KEY FILE! @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
Permissions 0644 for '/root/.ssh/id_rsa' are too open.
It is required that your private key files are NOT accessible by others.
This private key will be ignored.
bad permissions: ignore key: /root/.ssh/id_rsa
root@localhost's password: