【问题标题】:Generating a JSON map containing shell variables named in a list生成包含在列表中命名的 shell 变量的 JSON 映射
【发布时间】:2020-05-03 19:45:56
【问题描述】:

我的shell-fu处于初级水平以下。我有一个文件,其中包含一些恰好是环境变量名称的行。

例如

ENV_VAR_A
ENV_VAR_B
...

我想要做的是使用这个文件生成一个 JSON 字符串,其中包含使用 jq 的命名变量的名称和当前值,如下所示:

jq -n --arg arg1 "$ENV_VAR_A" --arg arg2 "$ENV_VAR_B" '{ENV_VAR_A:$arg1,ENV_VAR_B:$arg2}'

# if ENV_VAR_A=one and ENV_VAR_B=two then the preceding command would output 
# {"ENV_VAR_A":"one","ENV_VAR_B":"two"}

我正在尝试通过 shell 脚本创建 jq 命令,但我不知道自己在做什么:(

【问题讨论】:

  • @CharlesDuffy 这进一步证明了我在这方面遇到的困难......我知道它不必导出到环境中,但如果它是环境变量,则此语法有效(不确定是否应该做一些不同的事情)。我已经能够运行命令来做我想做的事,但现在我需要动态地做(基于白名单)
  • @CharlesDuffy 是的,但我也希望生成的 JSON 字符串的键也来自文件。

标签: bash shell environment-variables jq


【解决方案1】:

你想要的是一个间接引用。这些可以通过${!varname} 完成。作为一个简单的例子,仅限于两行:

# read arg1_varname and arg2_varname from the first two lines of file.txt
{ read -r arg1_varname; read -r arg2_varname; } <file.txt

# pass the variable named by the contents of arg1_varname as $arg1 in jq
# and the variable named by the contents of arg2_varname as $arg2 in jq
jq -n --arg arg1_name "$arg1_varname" --arg arg1_value "${!arg1_varname}" \
      --arg arg2_name "$arg2_varname" --arg arg2_value "${!arg2_varname}" \
  '{($arg1_name):$arg1_value, ($arg2_name):$arg2_value}'

要支持任意数量的键/值对,请考虑以下内容:

# Transform into NUL-separate key=value pairs (same format as /proc/*/environ)
while IFS= read -r name; do                             # for each variable named in file.txt
  printf '%s=%s\0' "$name" "${!name}"                   # print its name and value, and a NUL
done \
  <file.txt \
  | jq -Rs 'split("\u0000")                             # split on those NULs
            | [.[] | select(.)                          # ignore any empty strings
               | capture("^(?<name>[^=]+)=(?<val>.*)$") # break into k/v pairs
               | {(.name): .val}]                       # make each a JSON map
            | add                                       # combine those maps
  '

【讨论】:

  • 太棒了!我该如何使用 arg1_varname 替换 jq 生成的 JSON 字符串中的 ENV_VAR_A?对于最终的大量道具,无论如何我可以简单地从列表本身执行整个命令而不知道参数的数量(只是从文件中读取行)?
【解决方案2】:

在 bash 中尝试以下脚本:

# array of arguments to pass to jq
jqarg=()
# the script to pass to jq
jqscript=""
# just a number for the arg$num for indexing
# suggestion: just index using variable names...
num=1

# for each variable name from the input
while IFS= read -r varname; do

   # just an assertion - check if the variable is not empty
   # the syntax ${!var} is indirect reference
   # you could do more here, ex. see if such variable exists
   # or if $varname is a valid variable name
   if [[ -z "${!varname}" ]]; then
        echo "ERROR: variable $varname has empty value!" >&2
        exit 50
   fi

   # add the arguments to jqarg array
   jqarg+=(--arg "arg$num" "${!varname}")
   # update jqscript
   # if jqscript is not empty, add a comma on the end
   if [[ -n "$jqscript" ]]; then
      jqscript+=","
   fi
   # add the ENV_VAR_A:$arg<number>
   jqscript+="$varname:\$arg$num"
   # update number - one up!
   num=$((num + 1))

# the syntax of while read loop is that input file is on the end
done < input_file_with_variable_names.txt

# finally execute jq
# note the `{` and `}` in `{$jqscript}` are concious
jq -n "${jqarg[@]}" "{$jqscript}"

希望能帮助您更轻松地开始您的 bash 之旅。

我猜会用xargs做一些不可读的事情,比如:

< input_file_with_variable_names.txt xargs -d$'\n' -n1 bash -c '
   printf %s\\0%s\\0%s\\0 --arg "$1" "${!1}"
' -- |
xargs -0 sh -c 'jq -n "$@" "$0"' "{$(
     sed 's/\(.*\)/\1: $\1 /' input_file_with_variable_names.txt | 
     paste -sd,
)}"

【讨论】:

  • 嘿。我正在研究一些可能有点更优雅的东西,但这实际上是我过去自己在实践中解决这个问题的方式。 :)
  • 您和@CharlesDuffy 都非常乐于助人,非常感谢你们俩。
【解决方案3】:

jq 可以从环境本身中查找值。

$ export A=1
$ export B=2
$ cat tmp.txt
A
B
$ jq -Rn '[inputs] | map({key: ., value: $ENV[.]}) | from_entries' tmp.txt
{
  "A": "1",
  "B": "2"
}

关于其工作原理的几点说明:

  1. -R 读取原始文本,而不是尝试将输入解析为 JSON
  2. -n 阻止 jq 读取输入本身。
  3. inputs 显式读取所有输入,允许构建名称数组。
  4. map 创建一个以keyvalue 为键的对象数组; . 是当前数组输入(变量名),$ENV[.] 是名称为当前数组输入的环境变量的值。
  5. from_entries 最终将所有这些 {"key": ..., "value": ...} 对象合并为一个对象。

【讨论】:

  • 我无话可说,太棒了。
  • ...但是,我有一个进一步的要求,我正在尝试在 docker 容器中运行它,FROM nginx:stable,我已经安装了 jq 并且正在运行 CMD echo $( jq -Rn '[inputs] | map({key: ., value: $ENV[.]}) | from_entries' ./config/env ); 但我得到了@987654336 @....有什么想法吗?
  • 对于$ENV,您需要最新(1.6,在撰写本文时)版本的jq
  • 如果你有jq 1.5,你可以用env[.]代替$ENV[.]
  • 感谢您的提醒,我刚刚查看了我的 Docker 版本,它正在使用 apt-get install jq 安装 1.5,因此 env[.] 可以正常工作!
【解决方案4】:

短而甜(如果你的 jq 1.5 或更高):

 jq -Rn '[inputs | {(.): env[.]}] | add' tmp.txt

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-10-11
    • 2021-11-19
    • 2019-11-06
    • 1970-01-01
    • 1970-01-01
    • 2020-08-10
    • 1970-01-01
    • 2022-08-22
    相关资源
    最近更新 更多