Bash迭代数组,检测是否为空

Bash迭代数组,检测是否为空

我正在尝试迭代一个可能是null字符串或字符串数​​组的变量。

ZEIT_DEPLOYMENT_ALIASES=null或者ZEIT_DEPLOYMENT_ALIASES=['domain.sh]

我是 bash 的初学者,我读过bash 迭代文件列表,除非为空但我不明白。

我尝试了两种不同的方法。

的值ZEIT_DEPLOYMENT_ALIASES实际上来自jq读取 JSON 的库。

ZEIT_DEPLOYMENT_ALIASES=$(cat now.$CUSTOMER_REF_TO_DEPLOY.staging.json | jq --raw-output '.alias')

方法一

  ZEIT_DEPLOYMENT_ALIASES=['test.sh']

  # Check if there are no aliases configured
  if [ -z "$ZEIT_DEPLOYMENT_ALIASES" ]
  then
    ZEIT_DEPLOYMENT_ALIASES_COUNT=${#ZEIT_DEPLOYMENT_ALIASES[@]}
    echo "$ZEIT_DEPLOYMENT_ALIASES_COUNT alias(es) found. Aliasing them now..."

    # For each alias configured, then alias it to the deployed domain
    for DEPLOYMENT_ALIAS in "${ZEIT_DEPLOYMENT_ALIASES_COUNT[@]}"
    do
      echo "npx now alias "$ZEIT_DEPLOYMENT_URL $DEPLOYMENT_ALIAS
      npx now alias $ZEIT_DEPLOYMENT_URL $DEPLOYMENT_ALIAS --token $ZEIT_TOKEN || echo "Aliasing failed for '$DEPLOYMENT_ALIAS', but the build will continue regardless."
    done
  else
    # $ZEIT_DEPLOYMENT_ALIASES is null, this happens when it was not defined in the now.json file
    echo "There are no more aliases to configure. You can add more aliases from your now.json 'alias' property. See https://vercel.com/docs/configuration?query=alias%20domain#project/alias"
    echo "$ZEIT_DEPLOYMENT_ALIASES"
  fi

但有了这个,即使ZEIT_DEPLOYMENT_ALIASES=['something']它没有进入then条款。

方法2

ZEIT_DEPLOYMENT_ALIASES=['test.sh']
echo "Alias(es) for current project:" $ZEIT_DEPLOYMENT_ALIASES

for DEPLOYMENT_ALIAS in $ZEIT_DEPLOYMENT_ALIASES; do
  [ -z "$DEPLOYMENT_ALIAS" ] || continue
  echo "npx now alias "$ZEIT_DEPLOYMENT_URL $DEPLOYMENT_ALIAS
  npx now alias $ZEIT_DEPLOYMENT_URL $DEPLOYMENT_ALIAS --token $ZEIT_TOKEN || echo "Aliasing failed for '$DEPLOYMENT_ALIAS', but the build will continue regardless."
done

同样,似乎[ -z "$DEPLOYMENT_ALIAS" ]总是评估为true

如果您愿意,这里有一个游乐场:

  1. https://www.jdoodle.com/iembed/v0/3bs
  2. https://www.jdoodle.com/iembed/v0/3bo

答案1

给定这个文件:

$ cat test.json
{"alias": ["foo", "bar"], "whatever": "xyzzy"}

至少我的版本jq给出了这个输出jq --raw-output '.alias[]' < test.json

$ jq --raw-output '.alias[]' < test.json
foo
bar

即条目位于不同的行上,这很重要,因为我们可以使用它来将它们彼此分开。例如,通过将它们读取到带有readarray. (<(...)是一个流程替代,它使命令的输出像文件一样可用,因此< <(...)使其在标准输入中可用。实际上,有点像管道,只不过管道运行子 shell,因此在管道之后读取的值将不可用。)

#!/bin/bash
readarray -t entries < <(jq  '.alias[]' < test.json)
if [ "${#entries[@]}" = 0 ]; then
    echo empty array...
fi

# this will not do anything if the array is empty
for entry in "${entries[@]}"; do
    echo "processing entry $entry..."
done

要处理可能缺失的alias字段,请使用.alias[]?injq代替。但请注意,这会将非数组字符串值(例如{"alias": "foo"})处理为空,因此如果有可能,我们需要做其他事情。

另请注意,如果条目包含换行符,--raw-output将按原样打印它们,因此包含换行符的条目将显示为拆分为多行,就好像它们是多个不同的条目一样。


或者,无需进程替换,因此这应该适用于标准 shell,而不仅仅是 Bash。

#!/bin/sh
jq --raw-output '.alias[]' < json.txt | 
(
any=
while IFS= read -r line; do 
    echo "doing something with '$line'..."
    any=1
done
if [ "$any" != 1 ]; then
    echo "empty input..."
fi
)

为什么我的变量在一个“while read”循环中是本地变量,但在另一个看似相似的循环中却不是?至于为什么括号是必要的。


现在,关于你的代码:

ZEIT_DEPLOYMENT_ALIASES=['test.sh']

这会将字符串分配[test.sh]给变量。这["test.sh"]与像从 中获取的那样分配字符串不同jq,因为在这里,shell 会处理您提供的引号。它们不会根据命令替换的输出进行类似的处理。这也是单个标量变量,而不是数组。

if [ -z "$ZEIT_DEPLOYMENT_ALIASES" ]

这测试字符串是否为空字符串,这可能不是您的意思。不管怎样,jq's.alias可以给细绳 null,这与空字符串不同。

${#ZEIT_DEPLOYMENT_ALIASES[@]}

这将永远是1,因为它不是一个数组。出于同样的原因, for循环不会执行您想要的操作。

请注意,Bash 本身并不处理 JSON,如果它从命令替换中获取字符串["foo", "bar"],那么它只是一个细绳。您需要自己将其拆分为各个条目......

答案2

这是尝试 #1 的解决方案

由于其null价值,这很棘手,这与empty我最初的想法并无不同。

最难的事情是jq使用以下方法将 JSON 数组转换为 bash 数组:

readarray -t ZEIT_DEPLOYMENT_ALIASES < <(jq --raw-output '.alias[]' < now.$CUSTOMER_REF_TO_DEPLOY.staging.json)

非常感谢https://unix.stackexchange.com/a/615717/60329

  ZEIT_DEPLOYMENT_ALIASES_JSON=$(cat now.$CUSTOMER_REF_TO_DEPLOY.staging.json | jq --raw-output '.alias')
  echo "Custom aliases: " $ZEIT_DEPLOYMENT_ALIASES_JSON

  # Convert the JSON array into a bash array - See https://unix.stackexchange.com/a/615717/60329
  readarray -t ZEIT_DEPLOYMENT_ALIASES < <(jq --raw-output '.alias[]' < now.$CUSTOMER_REF_TO_DEPLOY.staging.json)

  # Check if there are no aliases configured (it will return "null" in such case, which is not the same as bash "empty")
  if [ "$ZEIT_DEPLOYMENT_ALIASES" != null ]
  then
    ZEIT_DEPLOYMENT_ALIASES_COUNT=${#ZEIT_DEPLOYMENT_ALIASES[@]}
    echo "$ZEIT_DEPLOYMENT_ALIASES_COUNT alias(es) found. Aliasing them now..."

    # For each alias configured, then assign it to the deployed domain
    for DEPLOYMENT_ALIAS in "${ZEIT_DEPLOYMENT_ALIASES[@]}"; do
      echo "npx now alias "$ZEIT_DEPLOYMENT_URL $DEPLOYMENT_ALIAS
      npx now alias $ZEIT_DEPLOYMENT_URL $DEPLOYMENT_ALIAS --token $ZEIT_TOKEN || echo "Aliasing failed for '$DEPLOYMENT_ALIAS', but the build will continue regardless."
    done
  else
    # $ZEIT_DEPLOYMENT_ALIASES is null, this happens when it was not defined in the now.json file
    echo "There are no more aliases to configure. You can add more aliases from your now.json 'alias' property. See https://vercel.com/docs/configuration?query=alias%20domain#project/alias"
    echo "$ZEIT_DEPLOYMENT_ALIASES"
  fi

谢谢你!

相关内容