运行 bash shell 脚本的输出作为 shell 命令?

Running the output of a bash shell script as a shell command?

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

#!/usr/bin/env bash

echo "echo 'hello';"

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

echo 'hello';

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

hello

到目前为止一切顺利。
但是我想一步完成,所以我尝试了这个:

$(./test.sh)

但是我现在得到:

'hello';

以防万一,我使用的是默认使用 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;: No such file or directory

无论 test.sh 脚本输出什么,作为 shell 命令行执行的正确语法是什么?

你多了一个echo。这是代码 (test.sh):

#!/bin/bash
echo "hello"

然后运行这样:$(./test.sh)"$(./test.sh)"。它将启动 hello 命令。如果你想要 test.sh 到 运行 脚本,请在 test.sh 中使用 echo "./hello"(使用 ./)。

要在执行前显示命令,运行 this with bash -x test.sh

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

这是您实际想要使用的少数情况之一 eval:

eval "$(./test.sh)"

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

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