繁体   English   中英

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

[英]How can I store a command in a variable in a shell script?

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

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

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 '^'";

output 是:

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

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

使用评估:

x="ls | wc"
eval "$x"
y=$(eval "$x")
echo "$y"

不要使用eval 它具有引入任意代码执行的主要风险。

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

把它放在一个数组中,并用双引号"${arr[@]}"展开所有单词,以免IFS由于Word Splitting拆分单词

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没有数组,所以最接近的是在位置参数中建立一个元素列表。 这是运行邮件程序的 POSIX sh方式

# 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[@]}"
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

使用eval,在使用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

在上面的例子中,如果你需要运行一个带参数的命令,把它们放在你存储的字符串中

stored_date="date -u"
# ...

对于 bash 脚本,这很少相关,但最后一个注意事项。 小心eval 仅评估您控制的字符串,而不是来自不受信任的用户或根据不受信任的用户输入构建的字符串。

  • 感谢@CharlesDuffy 提醒我引用命令!

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

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

像这样运行你的命令:

echo $command | bash

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

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

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

不知道为什么这么多答案使它变得复杂! 使用 alia [command] 'string to execute' 示例:

alias dir='ls -l'

./dir
[pretty list of files]

我用以下命令遇到了这个问题:

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

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

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

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

挑战:

  1. 如果awk脚本中有很多" ,请避免大量引用 escaping 逻辑。
  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

由于您没有指定任何脚本语言,因此我会推荐tcl ,用于这种目的的工具命令语言

然后在第一行,添加适当的 shebang:

#!/usr/local/bin/tclsh

在适当的位置,您可以使用which tclsh检索。

tcl脚本中,您可以使用exec调用操作系统命令。

使用以下命令小心注册订单: X=$(Command)

即使在被调用之前,这个仍然被执行。 要检查并确认这一点,您可以执行以下操作:

echo test;
X=$(for ((c=0; c<=5; c++)); do
sleep 2;
done);
echo note the 5 seconds elapsed

首先,有这方面的功能。 但是如果你更喜欢 vars 那么你的任务可以这样完成:

$ cmd=ls

$ $cmd # works
file  file2  test

$ cmd='ls | grep file'

$ $cmd # not works
ls: cannot access '|': No such file or directory
ls: cannot access 'grep': No such file or directory
 file

$ bash -c $cmd # works
file  file2  test

$ bash -c "$cmd" # also works
file
file2

$ bash <<< $cmd
file
file2

$ bash <<< "$cmd"
file
file2

或者通过 tmp 文件

$ tmp=$(mktemp)
$ echo "$cmd" > "$tmp"
$ chmod +x "$tmp"
$ "$tmp"
file
file2

$ rm "$tmp"
#!/bin/bash
#Note: this script works only when u use Bash. So, don't remove the first line.

TUNECOUNT=$(ifconfig |grep -c -o tune0) #Some command with "Grep".
echo $TUNECOUNT                         #This will return 0 
                                    #if you don't have tune0 interface.
                                    #Or count of installed tune0 interfaces.

即使您以后需要使用它,也没有必要将命令存储在变量中。 只需按正常方式执行即可。 如果您存储在变量中,您将需要某种eval语句或调用一些不必要的 shell 进程来“执行您的变量”。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM