如何在Linux中的变量中存储命令?

Ben*_*min 101 linux variables bash command

我想存储一个命令,以便稍后在变量中使用(不是命令的输出,而是命令本身)

我有一个简单的脚本如下:

command="ls";
echo "Command: $command"; #Output is: Command: ls

b=`$command`;
echo $b; #Output is: public_html REV test... (command worked successfully)
Run Code Online (Sandbox Code Playgroud)

但是,当我尝试更复杂的东西时,它会失败.例如,如果我做

command="ls | grep -c '^'";
Run Code Online (Sandbox Code Playgroud)

输出是:

Command: ls | grep -c '^'
ls: cannot access |: No such file or directory
ls: cannot access grep: No such file or directory
ls: cannot access '^': No such file or directory
Run Code Online (Sandbox Code Playgroud)

知道如何在变量中存储这样的命令(使用管道/多个命令)供以后使用吗?

Eri*_*rik 136

使用eval:

x="ls | wc"
eval "$x"
y=$(eval "$x")
echo "$y"
Run Code Online (Sandbox Code Playgroud)

  • 现在建议使用$(...)而不是`backticks`.y = $(eval $ x)http://mywiki.wooledge.org/BashFAQ/082 (25认同)
  • 如果您信任变量的内容,那么'eval`是一种可接受的做法****.如果你正在运行,比如说,`x ="ls $ name | wc"`(甚至`x ="ls'$ name'| wc"`),那么这段代码就是注入或特权升级漏洞的快速通道如果该变量可由具有较少特权的人设置.(例如,对`/ tmp`中的所有子目录进行迭代?您最好相信系统中的每个用户都不要生成一个名为`$'/ tmp/evil - $(rm -rf $ HOME)\'$( rm -rf $ HOME)\'/'`). (13认同)
  • `eval`是一个巨大的bug磁体,在没有警告意外解析行为的风险的情况下永远不应该被推荐(即使没有恶意字符串,如@ CharlesDuffy的例子).例如,尝试`x ='echo $((6*7))'`然后`eval $ x`.您可能希望打印"42",但可能不会.你能解释一下为什么它不起作用吗?你能解释我说"可能"的原因吗?如果这些问题的答案对你来说并不明显,那么你就不应该触摸"eval". (9认同)
  • @Student,尝试事先运行“set -x”来记录运行的命令,这将使您更容易看到发生了什么。 (2认同)
  • @Student我还推荐[shellcheck.net](https://www.shellcheck.net)来指出常见错误(以及你不应该养成的坏习惯)。 (2认同)

Ini*_*ian 36

千万不能使用eval!它具有引入任意代码执行的主要风险.

BashFAQ-50 - 我试图将命令放在一个变量中,但复杂的情况总是失败.

把它放在一个阵列,并展开所有用双引号的话"${arr[@]}"没有IFS分裂由于单词分词.

cmdArgs=()
cmdArgs=('date' '+%H:%M:%S')
Run Code Online (Sandbox Code Playgroud)

并查看里面数组的内容.的declare -p允许你看到里面与在单独的索引每个命令参数的数组的内容.如果一个这样的参数包含空格,那么在添加到数组时引用内部将阻止它因为Word-Splitting而被拆分.

declare -p cmdArgs
declare -a cmdArgs='([0]="date" [1]="+%H:%M:%S")'
Run Code Online (Sandbox Code Playgroud)

并执行命令

"${cmdArgs[@]}"
23:15:18
Run Code Online (Sandbox Code Playgroud)

(或)共同使用一个bash函数来运行命令,

cmd() {
   date '+%H:%M:%S'
}
Run Code Online (Sandbox Code Playgroud)

并将该函数称为just

cmd
Run Code Online (Sandbox Code Playgroud)

POSIX sh没有数组,因此最接近的是在位置参数中建立元素列表.这是一种sh运行邮件程序的POSIX 方式

# POSIX sh
# Usage: sendto subject address [address ...]
sendto() {
    subject=$1
    shift
    first=1
    for addr; do
        if [ "$first" = 1 ]; then set --; first=0; fi
        set -- "$@" --recipient="$addr"
    done
    if [ "$first" = 1 ]; then
        echo "usage: sendto subject address [address ...]"
        return 1
    fi
    MailTool --subject="$subject" "$@"
}
Run Code Online (Sandbox Code Playgroud)

  • @Student,如果您的原始字符串包含管道,则该字符串需要通过 bash 解析器的不安全部分才能作为代码执行。在这种情况下不要使用字符串;改用函数:`Command() { echo aaa | grep a; }` -- 之后你可以只运行`Command`,或`result=$(Command)`,或类似的。 (2认同)
  • @Student:我在最后添加了一个注释,提到它在某些条件下不起作用 (2认同)
  • 对,就是这样。例如,您不能 `sendto if true; 然后回声便便;fi` 因为看起来您正在发送 `if true`,这显然是一个语法错误,并且以下语句与 `sendto` 调用无关。 (2认同)
  • @tripleee:对提供赏金感到有点荣幸,希望我能与您和其他有用的贡献者分享;) (2认同)

Nat*_*ate 24

var=$(echo "asdf")
echo $var
# => asdf
Run Code Online (Sandbox Code Playgroud)

使用此方法,立即评估命令并存储其返回值.

stored_date=$(date)
echo $stored_date
# => Thu Jan 15 10:57:16 EST 2015
# (wait a few seconds)
echo $stored_date
# => Thu Jan 15 10:57:16 EST 2015
Run Code Online (Sandbox Code Playgroud)

与反引号相同

stored_date=`date`
echo $stored_date
# => Thu Jan 15 11:02:19 EST 2015
# (wait a few seconds)
echo $stored_date
# => Thu Jan 15 11:02:19 EST 2015
Run Code Online (Sandbox Code Playgroud)

使用中的eval $(...)不会在以后进行评估

stored_date=$(eval "date")
echo $stored_date
# => Thu Jan 15 11:05:30 EST 2015
# (wait a few seconds)
echo $stored_date
# => Thu Jan 15 11:05:30 EST 2015
Run Code Online (Sandbox Code Playgroud)

使用eval,可以在eval使用时进行评估

stored_date="date" # < storing the command itself
echo $(eval "$stored_date")
# => Thu Jan 15 11:07:05 EST 2015
# (wait a few seconds)
echo $(eval "$stored_date")
# => Thu Jan 15 11:07:16 EST 2015
#                     ^^ Time changed
Run Code Online (Sandbox Code Playgroud)

在上面的示例中,如果需要运行带参数的命令,请将它们放在要存储的字符串中

stored_date="date -u"
# ...
Run Code Online (Sandbox Code Playgroud)

对于bash脚本,这很少相关,但最后一个注释.小心eval.仅Eval您控制的字符串,从不来自不受信任的用户的字符串或从不受信任的用户输入构建的字符串.


Der*_*ell 6

对于 bash,请像这样存储命令:

command="ls | grep -c '^'"
Run Code Online (Sandbox Code Playgroud)

像这样运行你的命令:

echo $command | bash
Run Code Online (Sandbox Code Playgroud)

  • 不确定,但也许这种运行命令的方式与使用“eval”具有相同的风险。 (3认同)
  • 此外,当您“回显”变量时不引用它,就会破坏变量的内容。如果“command”是字符串“cd /tmp &amp;&amp; echo *”,它将回显当前目录中的文件,而不是“/tmp”中的文件。另请参阅[何时在 shell 变量周围加上引号](/sf/ask/704708651/) (2认同)