gpt4 book ai didi

shell - shell 脚本中的命令替换,无需通配符

转载 作者:行者123 更新时间:2023-12-04 19:57:53 25 4
gpt4 key购买 nike

考虑这个小shell脚本。

# Save the first command line argument
cmd="$1"

# Execute the command specified in the first command line argument
out=$($cmd)

# Do something with the output of the specified command
# Here we do a silly thing, like make the output all uppercase
echo "$out" | tr -s "a-z" "A-Z"

该脚本执行指定为第一个参数的命令,转换从该命令获得的输出并将其打印到标准输出。可以以这种方式执行该脚本。
sh foo.sh "echo select * from table"

这不会做我想要的。它可能会打印如下内容,
$ sh foo.sh "echo select * from table"
SELECT FILEA FILEB FILEC FROM TABLE

如果当前目录中存在 fileA、fileB 和 fileC。

从用户的角度来看,这个命令是合理的。用户已引用 *在命令行参数中,所以用户不期望 *被全局化。但是我的脚本在命令替换中使用这个参数让用户感到惊讶,这会导致 * 的通配符。如上面的输出所示。

我希望输出如下。
SELECT * FROM TABLE

全文在 cmd实际上来自脚本的命令行参数,所以我想保留任何 *参数中存在的符号,而无需对它们进行通配。

我正在寻找适用于任何 POSIX shell 的解决方案。

我想出的一个解决方案是使用 set -o noglob 禁用通配符。就在命令替换之前。这是完整的代码。
# Save the first command line argument
cmd="$1"

# Execute the command specified in the first command line argument
set -o noglob
out=$($cmd)

# Do something with the output of the specified command
# Here we do a silly thing, like make the output all uppercase
echo "$out" | tr -s "a-z" "A-Z"

这符合我的期望。
$ sh foo.sh "echo select * from table"
SELECT * FROM TABLE

除此之外,是否有任何其他概念或技巧(例如引用机制)我需要注意以仅在命令替换中禁用通配符而不必使用 set -o noglob .

我不反对 set -o noglob .我只是想知道是否有其他方法。您知道,可以通过引用普通命令行参数来禁用通配符,所以我想知道命令替换是否有类似的东西。

最佳答案

如果我理解正确,您希望用户提供一个 shell 命令作为命令行参数,该命令将由脚本执行,并预计生成一个 SQL 字符串,该字符串将被处理(大写)并回显到标准输出。

首先要说的是,让用户提供脚本只是盲目执行的 shell 命令是没有意义的。如果脚本在执行之前对命令进行了某种修改/预处理,那么它可能有意义,但如果没有,那么用户也可以自己执行命令并将输出作为命令行传递给脚本参数,或通过标准输入。

但话虽如此,如果你真的想这样做,那么有两件事需要说。首先,这是使用的正确形式:

out=$(eval "$cmd");

要完全理解使用上述语法的基本原理,需要对 shell 语法和扩展规则有相当深入的了解,但基本上执行 $cmd并执行 eval "$cmd"呈现 $cmd 的细微差别形式不适合执行给定的 shell 命令字符串。

为了提供一些希望澄清上述观点的细节,shell 在处理输入时按以下顺序执行七种扩展:(1) 大括号扩展,(2) 波浪号扩展,(3) 参数和变量扩展、(4) 算术扩展、(5) 命令替换、(6) 分词和(7) 路径名扩展。请注意,变量扩展发生在该序列的中间,因此变量扩展的 shell 命令(由用户提供)将不会获得先前扩展类型的好处。其他问题是在 $cmd 下无法正确执行前导变量赋值、管道和命令列表标记。形式,因为它们也在变量扩展之前(实际上在所有扩展之前)被解析和处理。

通过运行命令 eval ,正确地双引号,您确保完整的 shell 解析/处理/执行算法将应用于脚本用户提供的 shell 命令字符串。

要说的第二件事是:如果您在脚本中尝试上述正确形式,您会发现它并没有解决您的问题。你仍然会得到 SELECT FILEA FILEB FILEC FROM TABLE作为输出。

原因是:既然您已经决定要接受来自脚本用户的任意 shell 命令,那么现在用户有责任正确引用可能嵌入该代码段的所有元字符。接受 shell 命令作为命令行参数是没有意义的,但以某种方式更改 shell 命令的处理规则,以便在执行给定的 shell 命令时某些元字符将不再是元字符。实际上,你可以做类似的事情,也许使用 set -o noglob正如您所发现的那样,但这必须成为脚本与脚本用户之间的契约(Contract);用户必须清楚地知道执行命令时的精确处理规则是什么,以便他可以正确使用脚本。

在这种设计下,用户可以按如下方式调用脚本(注意 shell 命令字符串评估的额外引用层;也可以反斜杠转义星号):
$ sh foo.sh "echo 'select * from table'";

我想回到我之前关于整体设计的评论;这样做真的没有意义。使用文本到进程本身更有意义,而不是预期生成文本到进程的 shell 命令。

这是如何做到的:
## take the text-to-process via a command-line argument
sql="$1";

## process and echo it
echo "$sql"| tr a-z A-Z;

(我还删除了 -str 选项,这在这里真的没有意义。)

注意现在脚本更简单了,用法也更简单了:
$ sh foo.sh 'select * from table';

关于shell - shell 脚本中的命令替换,无需通配符,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28627922/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com