仅当变量非空时才引用

仅当变量非空时才引用

我有一个古老的 bash 脚本,我想改进它。这个脚本使用了许多变量,在某些情况下,一些变量被引用但为空或未设置。这通常不是问题,因为脚本从一开始就是这样设计的。

然而,我已经开始使用外壳检查它经常抱怨缺少引号。所以我添加了引号,但遇到了一个麻烦,而我没有很好的解决方案。

有很多行看起来像这样:

cmd $param1 $param2 $param3 $param4

如果其中一个参数为空或未设置,它仍可工作,并且 cmd 获取的参数数量会减少。但如果我开始添加引号

cmd "$param1" "$param2" "$param3" "$param4"

cmd 总是会得到 4 个参数,无论其中任何一个是否为空。

为什么会这样,这非常清楚。解决方法也很清楚(在使用参数之前检查是否为空),但它很丑陋,并且需要大量额外的代码。

因此,我正在寻找一个巧妙的解决方案,要么 a)如果参数为空,则省略该参数(包括引号!) b)仅为非空变量添加引号

答案1

您使用什么 shell 来编写此脚本?我假设它是 /bin/sh

POSIX shell 中唯一类似数组的实体是位置参数。你可以这样做:

set --  # clear the positional parameters
for param in "$param1" "$param2" "$param3" "$param4"; do
    if [ -n "$param" ]; then
        # append this param to the positional params
        set -- "$@" "$param"
    fi
done
# invoke the command with the positional params, properly quoted
cmd "$@"

如果您想要更聪明地做到这一点,请将 cmd 的调用包装在一个函数中:

invoke_cmd() {
  n="$#"  # remember the original number of params
  # append to the args list only if non-empty                    
  for arg; do [ -n "$arg" ] && set -- "$@" "$arg"; done
  # discard the original params (including the empty ones)
  shift "$n"
  # and invoke the cmd
  cmd "$@"
}
invoke_cmd "$param1" "$param2" "$param3" "$param4"

对于实际的 bash,简化

invoke_cmd() {
  local args=()
  for arg; do [[ "$arg" ]] && args+=("$arg"); done
  cmd "${args[@]}"
}
invoke_cmd "$param1" "$param2" "$param3" "$param4"

答案2

另一个选项(除了@glennjackman 描述的之外)是使用条件扩展,:+仅当变量被设置并且非空时才包含一个(正确引用的)变量:

cmd ${param1:+"$param1"} ${param2:+"$param2"} ${param3:+"$param3"} ${param4:+"$param4"}

(注:条件扩展在posix 规范,在第 2.6.2 节中;但我不完全确定所有 posix shell 都会正确尊重替代值中的双引号。)

如果您愿意对脚本进行更广泛的更改,并将其限制为 bash(即如果您使用 a#!/bin/bash#!/usr/bin/env bashshebang),那么在数组中积累参数通常更清晰:

cmd_parameters=() # start with an empty array
if [ something ]; then
    cmd_parameters+=("$param1") # Add an element -- note that the () are critical here!
fi
if [ something_else ]; then
    cmd_parameters+=("$param2")
fi
...etc
cmd "${cmd_parameters[@]}"

请注意,完全可以混合使用这些方法,包括使用:+技巧有条件地添加数组元素:

cmd_parameters+=(${param3:+"$param3"})

和/或将数组和条件参数混合到同一个命令中:

cmd "${cmd_parameters[@]}" ${param4:+"$param4"}

此外,根据@glennjackman 对包装函数的建议,可以编写一个通用的包装函数:

conditional_args() {
    # invoke a command ($1) with any empty arguments omitted
    args=()
    for arg; do
        args+=(${arg:+"$arg"})
    done
    "${args[@]}" # Note that the first "arg" is actually the command itself
}

conditional_args cmd "$param1" "$param2" "$param3" "$param4"

相关内容