如何在 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
我希望这对像我这样的未来旁观者有所帮助。
使用 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
我希望这对像我这样的未来旁观者有所帮助。