Bash:从子 shell 写入全局变量

Bash:从子 shell 写入全局变量

我正在尝试将一些getopts逻辑转移到函数中,这样我就可以多次使用它,从而为用户指定参数的顺序提供更大的灵活性:

print-usage() {
echo "myssh [options] <host> [options] [-- ssh-options...]" >&2
exit 1
}

extra_args=()

parse-args() {
  while getopts ":hvV:" opt; do
    case ${opt} in
      (h)  print-usage ;;
      (v)  extra_args+=('-L 5900:localhost:5900')    ;;
      (V)  extra_args+=("-L $OPTARG:localhost:5900") ;;
      (\?) echo "Invalid option: -$OPTARG" >&2       ;;
      (:)  echo "Invalid option: -$OPTARG requires an argument" >&2 ;;
    esac
  done
  echo $((OPTIND -1))
}

shift $(parse-args $@)

host=$1
shift

shift $(parse-args $@)

ssh $host $extra_args $@

我的问题是这parse-args() { ... extra_args+=(...) }不会影响全局变量extra_args。我知道子 shell 无法写入父级范围的变量,我们通常应该使用 stdout,但我已经使用 stdout 作为移位整数。

通常如何解决这一问题?

答案1

由于您正在使用bash(如标记的),因此您可以使用数组变量。您不能做的是通过诸如 之类的调用影响全局变量shift $(parse_args "$@"),该调用位于parse_args子 shell 中。这是一个替代解决方案,它将值数组传递到函数之外

#!/bin/bash
#
extra_args1=()
extra_args2=()

usage() {
    echo "myssh [options] <host> [options] [-- ssh-options...]" >&2
    exit 1
}
    
parse_args() {
    local OPT OPTIND=1 OPTARG args=()
    while getopts ":hvV:" OPT
    do
        case "$OPT" in
            (h)   usage ;;
            (v)   args+=('-L' '5900:localhost:5900') ;;
            (V)   args+=('-L' "$OPTARG:localhost:5900") ;;
            (\?)  echo "Invalid option: -$OPTARG" >&2 ;;
            (:)   echo "Invalid option: -$OPTARG requires an argument" >&2 ;;
        esac
    done
    echo $((OPTIND -1)) "${args[@]}"
}

# Get number to shift plus set of arguments
extra_args1=($(parse_args "$@"))
shift ${extra_args1[0]}
unset extra_args1[0]

# Position dependent value
host="$1"
shift

# Get number to shift plus set of arguments
extra_args2=($(parse_args "$@"))
unset extra_args2[0]

# "${x[@]}" values are interpolated as quoted and vanish if fully empty
ssh "$host" "${extra_args1[@]}" "${extra_args2[@]}" "$@"

通过双引号,"$@"我们可以使用它的魔力,这意味着不仅单独的值本身被视为双引号,而且如果没有要扩展的值,整个扩展也会消失。我们可以使用相同的功能来处理"${extra_args1[@]}"和的扩展"${extra_args2[@]}"

此方法的一个潜在缺点是它a=($(function))本身会受到通配符的影响,因此如果function返回任何通配符,它​​们将作为 array 赋值的一部分进行扩展a。对于这个担忧,我没有一个好的解决方案。

答案2

使用管道的可能解决方案FIFO。不要extra_args在函数中定义,而是使用函数写入管道并extra_args在调用函数后将其读入。

#!/bin/bash

trap "rm -rf extraargpipe" ERR EXIT
mkfifo extraargpipe
parseargs() {
 echo 1 > extraargpipe &
 echo 2 > extraargpipe &
}
parseargs
extra_args=$(cat extraargpipe)

echo ${extra_args}

由于 FIFO 逻辑而返回2 1(或者实际上是竞争条件,如果我没有记错的话),但该乱序顺序不应该是ssh您定义的选项的问题。如果你想创建类似的东西-o opt1,opt2,opt3,那就是.

使用临时文件也可以完成或多或少相同的操作。

相关内容