首页 > 解决方案 > 将 bash shell 脚本的输出作为 shell 命令运行?

问题描述

我有这个名为 'test.sh' 的 shell 脚本:

#!/usr/bin/env bash

echo "echo 'hello';"

当我运行./test.sh它时,它显然给出了这个输出:

回声“你好”;

现在这个输出本身也是一个有效的 shell 语句。如果我将此行复制并粘贴到外壳上并按回车,它会说

你好

到现在为止还挺好。
但我想一步完成,所以我尝试了这个:

$(./test.sh)

但是我现在得到:

'你好';

万一这很重要,我使用的zsh是默认使用 shell 的 macOS 10.15.6 Catalina。我在 Linux 上使用 bash 进行了同样的尝试,结果相同。

我也尝试过$('./test.sh')$("./test.sh")以防万一,但显然这没有任何区别。

对此可能有一些简单的解释和解决方案,但我看不到。我究竟做错了什么?


(编辑)也许这两个回声令人困惑,假设我的 test.sh 脚本包含以下内容:

echo "ls *.txt;"

如果我现在这样做./test.sh,我会得到

ls *.txt;

如果我将其复制并粘贴到 shell 上,它会按预期显示所有 .txt 文件。

但是,如果我这样做,$(./test.sh)我会得到:

ls: *.txt;: 没有这样的文件或目录

将 test.sh 脚本输出的任何内容作为 shell 命令行执行的正确语法是什么?

标签: bashshellterminalcommandexecution

解决方案


问题是命令替换($( )事情)是在解析命令行的过程中完成的——在它被解析并应用引号、分号和一堆其他东西之后。结果,当那些引号,分号等成为命令行的一部分时,它们做任何有用的事情都为时已晚,它们只是没有特殊含义的普通字符。

这是您真正想要使用的少数情况之一eval

eval "$(./test.sh)"

所做的是eval在其参数上重新运行整个解析过程,因此它们中的任何 shell 语法(引号、分号等)都会得到完全解析。双引号是为了防止在命令替换之后但在结果传递给之前发生的一点点解析eval,这可能会导致奇怪的效果。

顺便说一句,eval通常声誉不佳,因为它可能导致您不希望被视为 shell 语法(如文件名等)的内容被视为 shell 语法。但在这种情况下,这似乎正是您想要和期望的,所以就是这样。


推荐阅读