bash 变量作为命令:在执行前回显命令并将结果保存到变量

bash variable as a command: echo the command before execution and save the result to a variable

我正在执行一系列 curl 命令:

  1. 我需要在执行前回显命令。
  2. 执行命令并将结果保存到bash变量。
  3. 从执行结果中获取值并使用该值执行下一个 curl。

是这样的:

#  -----> step 1 <-----
URL="https://web.example.com:8444/hello.html"
CMD="curl \
        --insecure \
        --dump-header - \
        \"$URL\""

echo $CMD && eval $CMD
OUT="<result of the curl command???>"

# Set-Cookie: JSESSIONID=5D5B29689EFE6987B6B17630E1F228AD; Path=/; Secure; HttpOnly
JSESSIONID=$(echo $OUT | grep JSESSIONID | awk '{ s = ""; for (i = 2; i <= NF; i++) s = s $i " "; print s }' | xargs)

# Location: https://web.example.com:8444/oauth2/authorization/openam
URL=$(echo $OUT | grep Location | awk '{print }')

#  -----> step 2 <-----
CMD="curl \
        --insecure \
        --dump-header - \
        --cookie \"$JSESSIONID\" \
        \"$URL\""
echo $CMD && eval $CMD
OUT="<result of the curl command???>"
...

#  -----> step 3 <-----
...

我只有 step 2 有问题:将 curl 命令的完整结果保存到一个变量中,以便我可以解析它。

我试过很多不同的方法,但没有一个有效:

我错过了什么?

我想 OUT=$(eval $CMD) 会如你所愿。

简易模式:使用set -x

Bash 有一个内置的特性,xtrace,它告诉它将每个命令记录到变量 BASH_XTRACEFD 中命名的文件描述符(默认情况下,文件描述符 2 , 标准错误).

#!/bin/bash
set -x
url="https://web.example.com:8444/hello.html"
output=$(curl \
  --insecure \
  --dump-header - \
  "$url")

echo "Output of curl follows:"
echo "$output"

...将提供以下形式的日志:

+ url=https://web.example.com:8444/hello.html
++ curl --insecure --dump-header - https://web.example.com:8444/hello.html
+ output=Whatever
+ echo 'Output of curl follows:'
+ echo Whatever

...其中+是基于变量PS4的内容,可以对其进行修改以获得更多信息。 (我经常使用并建议 PS4=':${BASH_SOURCE}:$LINENO+' 将源文件名和行号放在每个记录的行中)。

手工制作

如果不行,你可以写一个函数。

log_and_run() {
  { printf '%q ' "$@"; echo; } >&2
  "$@"
}

output=$(log_and_run curl --insecure --dump-header - "$url")

...会将您的 curl 命令行写入 stderr,然后再将其输出存储在 $output 中。请注意,在编写该输出时需要使用引号:echo "$output",而不是 echo $output.

对于非常基本的命令,OUT=$($CMD) 应该可以。问题在于,存储在变量中的字符串与直接输入的字符串的处理方式不同。例如,echo "a" 打印 a,但 var='"a"'; echo $a 打印 "a"(注意引号)。由于这个和其他原因,您不应将命令存储在变量中。

在bash中,您可以改用数组。顺便说一句:常规变量的命名约定不是全部大写,因为这样的名称可能会意外地与特殊变量发生冲突。此外,您可能可以大大简化您的 grep | awk | xargs.

url="https://web.example.com:8444/hello.html"
cmd=(curl --insecure --dump-header - "$url")
printf '%q ' "${cmd[@]}"; echo
out=$("${cmd[@]}")
# Set-Cookie: JSESSIONID=5D5B29689EFE6987B6B17630E1F228AD; Path=/; Secure; HttpOnly
jsessionid=$(awk '{=""; printf "%s%s", d, substr([=10=],2); d=FS}' <<< "$out")
# Location: https://web.example.com:8444/oauth2/authorization/openam
url=$(awk '/Location/ {print }' <<< "$out")

#  -----> step 2 <-----
cmd=(curl --insecure --dump-header - --cookie "$jsessionid" "$url")
printf '%q ' "${cmd[@]}"; echo
out=$("${cmd[@]}")

#  -----> step 3 <-----
...

如果您的步骤比这更多,请将重复部分包装成