【问题标题】:Run a shell command from a variable in a shell script从 shell 脚本中的变量运行 shell 命令
【发布时间】:2015-04-19 06:06:00
【问题描述】:

我正在尝试从 shell 脚本中的变量运行命令。使用的 shell 是 bash shell。

文件exp 包含:

abcdef

执行以下命令:

sed s/b/\ / exp

...产生输出:

a  cdef

但正在执行:

cmd="sed s/b/\ / exp"
echo $cmd
$cmd

...产生以下错误:

sed s/b/\ / exp
sed: -e expression #1, char 5: unterminated `s' command

我可以看到在执行之前添加eval 是有效的。但我不明白为什么。你能解释一下为什么一种方法有效而另一种方法无效吗?

【问题讨论】:

  • 不要将命令放入字符串中。如您所见,它并不总是有效。这是一些recommended reading
  • 感谢您的回复和链接
  • @tripleee 注意问题最终出在sed 本身的解释上。看我的回答。
  • @Yosha 是否有任何答案解决了您的问题?由于您是新来的,如果您的问题已经解决,请不要忘记将答案标记为已接受。您可以单击答案旁边的复选标记将其从空心切换为绿色。如有任何疑问,请参阅Help Center > Asking

标签: linux bash shell sed


【解决方案1】:

您遇到的问题是 Bash 没有正确解释空间本身。

如果将b 替换为另一个字符,例如X,看看效果如何:

$ cmd="sed s/b/X/ exp"
$ $cmd
aXcdef

所以解决方法是使用十六进制的空格,即20

$ cmd="sed s/b/\x20/ exp"
$ $cmd
a cdef

或者使用eval来执行命令本身:

$ cmd="sed s/b/\ / exp"
$ eval "$cmd"
a cdef

正如 Tom Fenech 所建议的,将命令存储在变量中并不是一个好方法,如 I'm trying to put a command in a variable, but the complex cases always fail! 中所述。它有时可以工作,但在其他情况下会产生不可预测的结果。另一种方法是考虑使用函数。

最后,注意eval 在这种情况下可以派上用场,只是要非常小心存储的内容。一些不错的阅读:Variable as command; eval vs bash -c

【讨论】:

  • 嗯.. 他确实说过“bash”,但作为一项规则,我不会首先向初学者介绍 bash 扩展(\x20),而不会注意到它不是 POSIX。相反,转义的类型和原因将是通常的起点。
【解决方案2】:

看起来像是引用问题:

cmd="sed s/b/\ / exp" 使$cmd 包含一个没有特殊含义的字符序列。所以你的\ 不会逃出你的空间。

eval 将该字符序列视为命令,并将特殊含义重新分配给您的 \

另请参阅:Preserving quotes in bash function parameters

【讨论】:

    【解决方案3】:

    如果您需要变量中的输出,请使用,

    cmd=$(sed 's/b/ /' exp)
    

    就像@thomas 所说,如果您使用变量,则可以使用双引号。

    【讨论】:

    • 这和我发的一样。
    • 和我的帖子一样,
    • 那又怎样?在发布您的答案之前,我开始输入答案!
    • 这个答案没有解决原来的问题,即把命令本身(不是它的输出)存储在一个变量中,然后用这个变量做一些事情来运行命令。
    【解决方案4】:

    如果你引用 sed 参数,脚本更容易阅读,反斜杠只需要转义特殊字符(如反斜杠和双引号):

    cmd=$(sed "s/b/ /" exp)
    

    如果您使用单引号,则可以减少更多转义的需要——但会阻止您使用如下变量:

    xxx=something
    cmd=$(sed "s/b/$xxx/" exp)
    

    【讨论】:

    • 这和我发的一样
    • 我们正在同时编辑 - 所以我指出了一些您尚未(尚未)添加到您的答案中的附加功能。
    • @Jotne 你们都在几秒钟内发布了一个类似的问题。这至少有一些解释。我认为仅仅为此而对他们投反对票是不公平的。
    • 他似乎在我扩大解释时投了反对票 - 现在可以了。
    • 这个答案没有解决原来的问题,即把命令本身(不是它的输出)存储在一个变量中,然后用这个变量做一些事情来运行命令。
    【解决方案5】:

    应该这样做:

    cmd=$(sed "s/b/\ /" exp)
    

    要将数据存储在变量中,请使用var=$(commands)

    【讨论】:

    • 如果你贴出问题的答案意味着那么没有其他人不应该发帖啊?
    • @Karthikeyan.R.S 当然,每个人都可以发布他们喜欢的内容,但不需要发布与其他人 100% 相同的内容。
    • 这个答案没有解决原来的问题,即把命令本身(不是它的输出)存储在一个变量中,然后用这个变量做一些事情来运行命令。