运行存储在变量中的命令(包括管道和重定向)



我正在编写一个脚本,作为一个"说话终端"(你输入命令,它说输出),到目前为止,我的代码是:

#!/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中工作,并给出预期的输出。任何帮助吗?谢谢,马丁

这是一种罕见的情况,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:

#!/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

您是否尝试使用"-r"选项?此选项避免解释特殊字符。此外,您必须删除

行中的外部$()
espeak "$($COMMAND)"

应该是:

espeak "$(COMMAND)"

最新更新