8wDlpd.png
8wDFp9.png
8wDEOx.png
8wDMfH.png
8wDKte.png

如何将命令存储在 shell 脚本的变量中?

Eugene K 1月前

62 0

我想将一个命令存储在一个变量中以便稍后使用(不是命令的输出,而是命令本身)。我有一个简单的脚本如下:command=\'ls\';echo \'C...

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

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

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

b=`$command`;
echo $b; #Output is: public_html REV test... (command worked successfully)

但是,当我尝试更复杂一点的方法时,它就会失败。例如,如果我

command="ls | grep -c '^'";

输出为:

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

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

帖子版权声明 1、本帖标题:如何将命令存储在 shell 脚本的变量中?
    本站网址:http://xjnalaquan.com/
2、本网站的资源部分来源于网络,如有侵权,请联系站长进行删除处理。
3、会员发帖仅代表会员个人观点,并不代表本站赞同其观点和对其真实性负责。
4、本站一律禁止以任何方式发布或转载任何违法的相关信息,访客发现请向站长举报
5、站长邮箱:yeweds@126.com 除非注明,本帖由Eugene K在本站《bash》版块原创发布, 转载请注明出处!
最新回复 (0)
  • 使用评估:

    x="ls | wc"
    eval "$x"
    y=$(eval "$x")
    echo "$y"
    
  • 现在建议使用 $(...) 代替反引号。y=$(eval $x) mywiki.wooledge.org/BashFAQ/082

  • 只有当您信任变量的内容时,eval 才是一种可接受的做法。如果您正在运行 x=\'ls $name | wc\'(甚至 x=\'ls '$name' | wc\'),如果该变量可以由具有较低权限的人设置,则此代码是注入或权限提升漏洞的快速途径。(例如,遍历 /tmp 中的所有子目录?您最好信任系统上的每个用户,不要创建一个名为 $'/tmp/evil-$(rm -rf $HOME)\'$(rm -rf $HOME)\'/' 的子目录)。

  • eval 是一个巨大的错误陷阱,在没有警告意外解析行为风险的情况下,绝不应该推荐使用它(即使没有恶意字符串,如 @CharlesDuffy 的示例)。例如,尝试 x='echo $(( 6 * 7 ))',然后尝试 eval $x。您可能希望打印 \'42\',但可能不会。您能解释为什么它不起作用吗?您能解释为什么我说\'可能\'吗?如果这些问题的答案对您来说不明显,那么您永远不应该碰 eval。

  • @Student,尝试预先运行 set -x 来记录运行的命令,这将使我们更容易地看到正在发生的事情。

  • @Student 我还推荐 shellcheck.net 来指出常见的错误(以及你不应该养成的坏习惯)。

  • 不要 使用 eval 它有引入任意代码执行的重大风险。

    BashFAQ-50 - 我尝试将命令放入变量中,但复杂的情况总是失败。

    将其放入数组中,并用双引号展开所有单词, "${arr[@]}" 以免 IFS 词而导致单词 分裂 .

    cmdArgs=()
    cmdArgs=('date' '+%H:%M:%S')
    

    并查看数组内部的内容。允许 declare -p 您使用单独的索引中的每个命令参数查看数组内部的内容。如果其中一个参数包含空格,则在添加到数组时在内部加上引号将防止它因分词而分裂。

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

    并执行如下命令

    "${cmdArgs[@]}"
    23:15:18
    

    (或)完全使用 bash 函数来运行命令,

    cmd() {
       date '+%H:%M:%S'
    }
    

    并调用如下函数

    cmd
    

    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" "$@"
    }
    

    请注意,此方法只能处理没有重定向的简单命令。它无法处理重定向、管道、for/while 循环、if 语句等

    另一个常见用例是 curl 使用多个标头字段和有效负载运行时。您可以像下面这样定义参数,并 curl 在扩展的数组内容上

    curlArgs=('-H' "keyheader: value" '-H' "2ndkeyheader: 2ndvalue")
    curl "${curlArgs[@]}"
    

    另一个例子,

    payload='{}'
    hostURL='http://google.com'
    authToken='someToken'
    authHeader='Authorization:Bearer "'"$authToken"'"'
    

    现在已经定义了变量,使用数组来存储命令参数

    curlCMD=(-X POST "$hostURL" --data "$payload" -H "Content-Type:application/json" -H "$authHeader")
    

    现在进行适当的引用扩展

    curl "${curlCMD[@]}"
    
  • @Student,如果您的原始字符串包含管道,那么该字符串需要经过 bash 解析器的不安全部分才能作为代码执行。在这种情况下,不要使用字符串;而是使用函数:Command() { echo aaa | grep a; } -- 之后您可以运行 Command、result=$(Command) 或类似的东西。

  • 是的,确实如此。例如,您不能 sendto if true; then echo poo; fi,因为看起来您正在发送 if true,这显然是一个语法错误,并且以下语句与 sendto 调用无关。

  • @tripleee:非常感谢您提供的赏金,我希望可以与您和其他有用的贡献者分享;)

  • var=$(echo "asdf")
    echo $var
    # => asdf
    

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

    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
    

    与反引号相同

    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
    

    使用 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
    

    Using eval, it is evaluated when eval is used

    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
    

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

    stored_date="date -u"
    # ...
    

    对于 Bash 脚本来说,这很少相关,但还有最后一点需要注意。请谨慎使用 eval 。仅 Eval 您控制的字符串,而不要使用来自不受信任的用户或由不受信任的用户输入构建的字符串。

  • @Nate,请注意,当stored_date仅包含日期时,eval $stored_date可能足够好,但eval \'$stored_date\'更可靠。运行str=$'printf \' * %s\\n\' *'; eval \'$str\',带引号和不带引号作为示例。:)

  • @CharlesDuffy 谢谢,我忘了引用了。我敢打赌,如果我费心运行它,我的 linter 肯定会抱怨。

  • 对于 bash,像这样存储你的命令:

    command="ls | grep -c '^'"
    

    像这样运行你的命令:

    echo $command | bash
    
  • 引用 16

    此外,在回显变量时不加引号会破坏变量的内容。如果命令是字符串 cd /tmp && echo *,它将回显当前目录中的文件,而不是 /tmp 中的文件。另请参阅何时将 shell 变量括在引号中

  • 不确定为什么这么多答案使它变得复杂!使用 alias [命令]'要执行的字符串'示例:

    alias dir='ls -l'
    
    ./dir
    [pretty list of files]
    
  • 别名存在许多问题。首先,它们不会在脚本中扩展,如果您正在编写脚本,这显然会令人沮丧(因此您的问题与 Stack Overflow 上的主题相关)。其次,您不能将位置参数传递给别名,只能让它们占用其余的命令行而无法访问它。此外,解析规则很挑剔,因此您无法启用别名扩展并在同一命令行中使用别名。总之,使用函数,就像每个人一直告诉您的那样。

  • 我在使用以下命令时遇到了这个问题:

    awk '{printf "%s[%s]\n", $1, $3}' "input.txt"

    我需要动态构建这个命令:

    目标文件名 input.txt 是动态的,可能包含空格。

    括号 awk 内的脚本 {} printf "%s[%s]\n", $1, $3 动态的。

    挑战:

    1. " 有很多引号转义逻辑,请避免使用大量引号转义逻辑 awk
    2. 字段变量 $ 进行参数扩展

    命令和关联性 eval 的解决方案 arrays 不起作用。由于 bash 变量扩展和引用。

    解决方案:

    动态 bash 构建 bash 扩展,使用 printf 模板。

     # dynamic variables, values change at runtime.
     input="input file 1.txt"
     awk_script='printf "%s[%s]\n" ,$1 ,$3'
    
     # static command template, preventing double-quote escapes and avoid variable  expansions.
     awk_command=$(printf "awk '{%s}' \"%s\"\n" "$awk_script" "$input")
     echo "awk_command=$awk_command"
    
     awk_command=awk '{printf "%s[%s]\n" ,$1 ,$3}' "input file 1.txt"
    

    执行变量命令:

    bash -c "$awk_command"
    

    也可以使用的替代方案

    bash << $awk_command
    
  • ed22 1月前 0 只看Ta
    引用 20

    我尝试了各种不同的方法:

    printexec() {
      printf -- "\033[1;37m$\033[0m"
      printf -- " %q" "$@"
      printf -- "\n"
      eval -- "$@"
      eval -- "$*"
      "$@"
      "$*"
    }
    

    输出:

    $ printexec echo  -e "foo\n" bar
    $ echo -e foo\\n bar
    foon bar
    foon bar
    foo
     bar
    bash: echo -e foo\n bar: command not found
    

    如您所见,只有第三个 "$@" 给出了正确的结果。

返回
作者最近主题: