想象一下我有这个
$ ARGS='"a b" c'
$ for arg in "$ARGS"; do echo "$arg"; done
"a b" c
$ for arg in $ARGS; do echo "$arg"; done
"a
b"
c
我希望得到的结果是
$ <???>
a b
c
我该怎么做?谢谢。
答案1
这将提供您要求的输出:
eval "for arg in $ARGS; do echo \"\$arg\";done"
但 eval 只能作为最后的手段。你的问题很难回答,是因为你的 ARGS 变量的格式设计得很糟糕。
一系列由空格分隔的单词,单词内的空格受引号机制保护,这是 shell 命令的基本格式。由于您必须学习这种格式才能编写 shell 脚本,因此很容易将其用于由 shell 脚本管理的数据结构,因此您只需采用类似 shell 的构造"a b" c
并将其填充到 shell 变量中即可。
这通常不是一个好主意。shell 脚本中的字符串没有理由看起来像 shell 命令行,而操作包含空格分隔或引号单词的字符串的唯一方法是使用原始字符串操作构建自己的解析器,或者让 shell 使用 eval 来执行此操作。
但是当您使用 eval 时,您不仅可以获得方便的单词分割器和引号删除器,还可以获得 shell 解析器的所有其他功能:命令替换、参数替换、输入/输出重定向以及其他您可能不想要的东西。
使用数组。此基于数组的脚本适用于 zsh、ksh 和 bash:
ARGS=("a b" c)
for arg in "${ARGS[@]}"; do
echo "$arg"
done
它比 eval 简洁得多。如果您正在为没有 zsh、ksh 或 bash 的古老系统编程,请考虑学习 awk。对于复杂到需要数组的脚本来说,最小的 POSIX shell 并不是一门好语言。
对 ormaaj 的回答进行一些详细说明:POSIX sh 有一个数组变量:$@
它是“位置参数”(命令行参数)的数组。您可以访问(读取,而不是写入)其各个元素,如、、$1
等。您可以使用命令替换整个数组。对于您的示例脚本,此功能勉强够用。这适用于 Bourne 系列的任何 shell:$2
$3
set
set -- "a b" c
for arg; do
echo "$arg"
done
我在那里添加了一个额外的功能演示:for arg
没有就in
相当于for arg in "$@"
循环遍历数组$@
。
答案2
由于您不关心引号,因此只需在遍历字符串之前使用 sed 将其删除:
for arg in `echo $ARGS | sed -e 's/"//g'`; do
echo $arg
done
除非...你的例子是错误的,你想看到这样的情况:
a b
c
可能有很多方法可以做到这一点,但我会使用 awk(假设是 GNU awk,因此我们可以使用长度超过一个字符的字段分隔符):
echo $ARGS | \
awk -F'" ' '{ for (field = 1; field <= NF; field++) print $field; }' | \
sed -e 's/^"//' -e 's/"$//'
最后一个 sed 删除了 $ARGS 末尾的所有开始引号和最终的结束引号。当然,如果有嵌套引号,整个过程就会崩溃。