如何在 Bourne shell 的变量中存储带引号的字符串列表? (没有 Bash 数组)

How can I store a list of quoted strings in a variable in Bourne shell? (without Bash arrays)

使用 Bash's arrays,我可以将列表存储到数组变量中并迭代元素,同时将包含空格或特殊字符的引用字符串保持在一起:

LIST=(--foo --bar --baz "test 1 2 3")
for item in "${LIST[@]}"; do
    echo "$item"
done

输出为:

--foo
--bar
--baz
test 1 2 3

我有一个使用此功能的脚本,不幸的是需要移植它以使用 Busybox 的 ash(不支持数组)。我正在尝试找出一种存储列表的好方法,其中某些项目可能有空格,同时仍保留列表中正确数量的元素。

这不起作用,例如(错误地将 test 1 2 3 拆分为单独的项目):

LIST='--foo --bar --baz "test 1 2 3"'
for item in $LIST; do
    echo "$item"
done

输出为:

--foo
--bar
--baz
"test
1
2
3"

一个idea I found on the Busybox mailing list是用set --代替位置参数:

set -- --foo --bar --baz "test 1 2 3"
for item in "$@"; do
    echo "$item"
done

输出正确:

--foo
--bar
--baz
test 1 2 3

但是,此构造破坏了位置参数列表 ($@),此脚本 使用。

有什么合理的方法可以让我既吃蛋糕又吃蛋糕,并在非Bash sh变体中模拟多个任意数组?

您可以在其中声明一个带有 \n 的变量:

list='--foo\n--bar\n--baz\n"test 1 2 3"'

# then iterate it using
echo -e "$list" | while read -r line; do echo "line=[$line]"; done

输出:

line=[--foo]
line=[--bar]
line=[--baz]
line=["test 1 2 3"]

大多数 shell 提供某种数组的原因是您无法使用扁平字符串安全地模拟它们。如果您可以将需要数组的代码与脚本的其余部分隔离开来,则可以在子 shell 中执行它,以便在子 shell 退出后可以恢复位置参数。

( # Nothing in here can rely on the "real" positional
  # parameters, unless you can fsave them to a fixed set of indiviual
  # parameters first.
  first_arg=  # etc
  set -- --foo --bar --baz "test 1 2 3"
  for item in "$@"; do
      echo "$item"
  done
  # Any other parameters you set or update will likewise revert
  # to their former values now.
)

xargs + 子外壳

派对晚了几年,但是......我确实找到了一种方法,没有破坏 $@,而且 甚至可以处理恶意输入.

输入:

SSH_ORIGINAL_COMMAND='echo "hello world" foo '"'"'bar'"'"'; sudo ls -lah /; say -v Ting-Ting "evil cackle"'

(我最初在那里有一个 rm -rf,但后来我意识到在测试脚本的变体时这将是一场灾难)

已完美转换为安全参数:

# Note: DO NOT put IFS= on its own line (that would overwrite it: bad)
IFS=$'\r\n' GLOBIGNORE='*' args=($(echo "$SSH_ORIGINAL_COMMAND" | \
  xargs bash -c 'for arg in "$@"; do echo "$arg"; done'))

这给了你一个很好的 ${args[@]},你可以像 $@:

for arg in "${args[@]}"
do
  echo "$arg"
done

输出:

hello world
foo
bar;
sudo
rm
-rf
/;
say
-v
Ting-Ting
evil cackle

我希望这对像我这样的未来旁观者有所帮助。