ChatGPT解决这个技术问题 Extra ChatGPT

如何执行存储为带有引号和星号的字符串的bash命令[重复]

这个问题在这里已经有了答案:为什么shell会忽略通过变量传递给它的参数中的引号字符? [重复] (3个回答) 6年前关闭。

我尝试执行以下命令:

mysql AMORE -u username -ppassword -h localhost -e "SELECT  host  FROM amoreconfig"

我将它存储在一个字符串中:

cmd="mysql AMORE -u username -ppassword -h localhost -e\"SELECT  host  FROM amoreconfig\""

测试它:

echo $cmd
mysql AMORE -u username -ppassword -h localhost -e"SELECT host FROM amoreconfig"

尝试执行:

$cmd

我得到了 mysql 的帮助页面:

mysql  Ver 14.14 Distrib 5.1.31, for pc-linux-gnu (i686) using readline 5.1
Copyright 2000-2008 MySQL AB, 2008 Sun Microsystems, Inc.
This software comes with ABSOLUTELY NO WARRANTY. This is free software,
and you are welcome to modify and redistribute it under the GPL license
Usage: mysql [OPTIONS] [database]
(...)

我想我对引号做了一些明显的错误,但无法找出问题所在。

我建议您阅读以下内容:mywiki.wooledge.org/BashFAQ/050
@DennisWilliamson - 顶部链接;我特别喜欢这样:“如果你的脑袋离你的屁股太远了,以至于你仍然认为你需要在运行它之前写出你将要运行的每一个命令”——我想知道那个作者将如何解决一个动态构建命令的脚本,并且明确地想要回显它 - 为了提示用户“你想运行这个命令吗?”在它运行之前?...
@sdaau,取决于使用了常见问题解答中给出的哪种方法。对于函数,可以使用 declare -f 打印其文本;对于数组(典型的“动态构造”方法):printf '%q ' "${array[@]}"; echo
顺便说一句,最佳实践方法是将您的命令存储为字符串。如果要动态构造它,请使用数组进行。使用 eval,就像这里的最佳答案一样,会带来巨大的安全风险(如果任何内容被参数化,则会面临 shell 注入攻击)。
@DennisWilliamson - 我喜欢在具有多个阶段的 [big] shell 中为 --dryrun 功能做 if ,用户可能会跳过。说得通??

s
slebetman

你有没有尝试过:

eval $cmd

对于如何转义 * 的后续问题,因为它在裸露或双引号字符串中具有特殊含义:使用单引号。

MYSQL='mysql AMORE -u username -ppassword -h localhost -e'
QUERY="SELECT "'*'" FROM amoreconfig" ;# <-- "double"'single'"double"
eval $MYSQL "'$QUERY'"

奖励:它也读起来不错:eval mysql query ;-)


谢谢,它有效。我将如何选择所有列?我怎样才能逃脱 '*' ?
请参阅 BashFAQ #48 以讨论围绕此用途的安全缺陷:mywiki.wooledge.org/BashFAQ/048
... eval 语句中的文字引号,当它们通过使用 eval 成为语法时,可以通过数据中的任何文字引号取消其效果;因此,它们不能提供有效的安全性。
@joshmcode,...如果我们希望部署的系统运行脚本,这些脚本是根据 ServerFault 的建议构建的,具有注入漏洞。获得正确的细节很重要。我遇到过的最严重的数据丢失事件是,有人在处理“不可能”包含除十六进制数字之外的任何内容的文件名时没有使用足够的引用。直到有一天(由于构建文件的程序中的错误将随机内存内容转储到用作名称的缓冲区中),并且该脚本(负责修剪古老的备份)删除了数月的计费数据。
@joshmcode,(是的,这很重要——eval $cmdeval "$cmd" 不同,它将您的输入拆分为单词,将每个单词评估为一个 glob,然后将它们与空格一起粘贴回来,所以一个带有空格环绕星号的命令可能会导致当前工作目录的文件名中的 shell 扩展被评估)。
C
Charles Duffy

按照 BashFAQ #50 中的指导使用数组,而不是字符串。

使用字符串是非常糟糕的安全做法:考虑用户提供 password(或查询中的 where 子句或任何其他组件)的情况;您不想eval包含 $(rm -rf .) 的密码!

只运行一个本地命令

cmd=( mysql AMORE -u username -ppassword -h localhost -e "SELECT  host  FROM amoreconfig" )
"${cmd[@]}"

明确地打印您的命令

cmd=( mysql AMORE -u username -ppassword -h localhost -e "SELECT  host  FROM amoreconfig" )
printf 'Proposing to run: '
printf '%q ' "${cmd[@]}"
printf '\n'

通过 SSH 运行命令(方法 1:使用标准输入)

cmd=( mysql AMORE -u username -ppassword -h localhost -e "SELECT  host  FROM amoreconfig" )
printf -v cmd_str '%q ' "${cmd[@]}"
ssh other_host 'bash -s' <<<"$cmd_str"

通过 SSH 运行命令(方法 2:命令行)

cmd=( mysql AMORE -u username -ppassword -h localhost -e "SELECT  host  FROM amoreconfig" )
printf -v cmd_str '%q ' "${cmd[@]}"
ssh other_host "bash -c $cmd_str"

考虑查询中没有密码的情况。想想它会有多大用处。
@DavidBeckwith,为什么具有安全漏洞的方法比没有安全漏洞的方法更有用?它增加了什么好处?你仍然可以动态地构造你的数组;您只是获得了不会冒险以不同于预期的方式解析其内容的风险。
...也就是说:可以运行 cmd+=( -e "$query" ) 将这些参数附加到现有数组,并确保 query 将作为单个参数添加到 -e 并传递给 mysql;无需查看它的内容来确定它是否生成子外壳或转义其引号并启动 rootkit 或其他任何东西。
如果您有安全漏洞,那就更令人兴奋了。
我……真的不知道我能说些什么来回应。除了“请不要申请和我一起工作”。或者,也许,“请不要申请在任何地方工作,制作我使用的产品”。
g
ghostdog74

尝试这个

$ cmd='mysql AMORE -u root --password="password" -h localhost -e "select host from amoreconfig"'
$ eval $cmd

谢谢,它有效。我将另一个答案标记为已接受,因为它之前出现过。
就它的工作而言,这很糟糕。需要为 eval "$cmd" 而不是 eval $cmd,以处理任何分词组件可以全局扩展为当前目录中的文件的情况,或者 IFS 中的字符无法无害地替换其他字符的情况。
如果评估字符串的任何输入是用户提供的,则此解决方案存在命令注入安全漏洞的风险。 @CharlesDuffy 提供的解决方案要好得多。
D
David Beckwith

你甚至不需要“eval”。只需在字符串前面放一个美元符号:

cmd="ls"
$cmd

仅适用于极其简单的命令;不适用于 OP 在他们的示例中给出的那个。
阅读 mywiki.wooledge.org/BashFAQ/050 了解原因。
这对我来说适用于 nodejs 工具而不是 `$cmd`
@PauloOliveira,那是因为您之前尝试捕获命令的输出,将该输出分解为单词,然后将这些单词作为另一个命令本身运行。
在 GitBash 中不起作用。
P
Paul Havens

要消除对 cmd 变量的需要,您可以这样做:

eval 'mysql AMORE -u root --password="password" -h localhost -e "select host from amoreconfig"'

如果您只是想要一个硬编码的文字,为什么要使用 eval 而不是只运行 mysql [...]
带有常量文字的 eval 没有意义!