【问题标题】:Running a command that is stored in a variable (including pipes and redirects)运行存储在变量中的命令(包括管道和重定向)
【发布时间】:2015-12-25 19:29:25
【问题描述】:

我正在编写一个充当“会说话的终端”的脚本(您输入命令并显示输出),到目前为止我的代码是:

#!/bin/bash
while [ 1=1 ]; do
echo -n "~>"
read COMMAND
espeak "$($COMMAND)"
done

它适用于简单的命令:

bash$ ./talkingterminal.sh
~> ls
# espeak says "talkingterminal.sh"

但是当我使用管道等时:

bash$ ./talkingterminal.sh
~>ip addr | grep inet
Command "|" is unknown, try "ip addr help".
~>

并且该命令在 bash 中工作并给出预期的输出。 有什么帮助吗? 谢谢, 马丁

【问题讨论】:

  • 不是一个答案,但为了解释,我会说这是因为命令分离发生在初始分词阶段,所以扩展命令不能再将一个命令变成两个管道。参考:How to execute command stored in a variable? 因此eval。

标签: bash shell espeak


【解决方案1】:

这是eval 合适的罕见情况;您只需在已经可以无限制访问命令行的内容周围放置一个薄包装器。更重要的是,您不会以任何意想不到的方式修改COMMAND。用户输入的内容将被执行。

#!/bin/bash
while : ; do
    IFS= read -rp "~> " COMMAND
    espeak "$(eval "$COMMAND")"
done

一些注意事项:

  1. bash 中的内置函数read 可以使用-p 选项显示自定义提示。
  2. 使用总是成功的: 或true 命令设置无限循环。
  3. 使用-r 选项和IFS 的空值确保COMMAND 设置为准确 用户键入的行。
  4. 引用$COMMAND 以便将整个字符串传递给eval 而无需任何先前的shell 解析。
  5. 请注意,这仍然无法以与 shell 本身相同的方式处理多行输入。 (例如,for x in 1 2 3; do 之类的行不会提示您输入循环体。)

【讨论】:

  • 反对者:这就是eval 实际上的意图。 OP 已经想要执行任意代码,而不是假设 eval 只会执行它可以执行的预期代码子集。
【解决方案2】:

一个简单的解决方案是使用eval:

#!/bin/bash
while [ 1=1 ]; do
echo -n "~>"
read COMMAND
espeak "$(eval $COMMAND)"
done

然后您也可以轻松地将输出打印到 shell:

#!/bin/bash
while [ 1=1 ]; do
echo -n "~>"
read COMMAND
OUTPUT="$(eval $COMMAND)"
echo $OUTPUT
espeak "$($OUTPUT)"
done

【讨论】:

    【解决方案3】:

    您是否尝试过使用“-r”选项?此选项可避免解释特殊字符。此外,您必须删除该行中的外部 $():

    espeak "$($COMMAND)"
    

    应该是:

    espeak "$(COMMAND)"
    

    【讨论】:

    • read 不是问题;问题是像这样扩展$COMMAND 将| 作为参数传递给ipaddr,而不是将整个字符串解析为管道。
    • $(COMMAND) 会尝试运行文字命令“COMMAND”,而不是 COMMAND 参数的值。
    猜你喜欢
    • 2012-11-19
    • 2018-03-20
    • 1970-01-01
    • 2016-08-11
    • 1970-01-01
    • 1970-01-01
    • 2019-11-06
    • 1970-01-01
    • 2017-04-23
    相关资源
    最近更新 更多