以下面的 bash 为例(我应该提到我的真实输出来自vboxmanage list vms
)
TEST='"ubuntu server" foo bar'
echo $TEST
"ubuntu server" foo bar
如何将三个值分开(在循环或单独的变量中)?我发现的每个解决方案都依赖于空格分隔符,并且会破坏第一个值。
答案1
您可以迭代 VirtualBox 机器的名称:
vboxmanage list -l vms | sed -n 's/^Name:[[:blank:]]*//p' |
while IFS= read -r vm_name; do
printf 'There is one VM called "%s"\n' "$vm_name"
done
这会列出使用长格式的机器并解析出名称。然后在 shell 循环中读取这些名称,并为每个名称打印一条短消息。
您还可以将名称读入数组:
readarray -t vm_names < <(vboxmanage list -l vms | sed -n 's/^Name:[[:blank:]]*//p')
printf 'There is one VM called "%s"\n' "${vm_names[@]}"
这里,名称列表被红色放入数组中vm_names
。你可以像这样循环这个数组:
for vm_name in "${vm_names[@]}"; do
# Use "$vm_name" here
done
也可以使用 UUID:
vboxmanage list -l vms |
sed -n \
-e '/^Name:[[:blank:]]*/{ s///; h; }' \
-e '/^UUID:[[:blank:]]*/{ s///; G; y/\n/@/; p; }' |
while IFS=@ read -r vm_uuid vm_name; do
printf 'VM "%s" has UUID "%s"\n' "$vm_name" "$vm_uuid"
done
答案2
假设您将其作为字符串,采用初始问题中代码块中显示的格式(即"ubuntu server" foo bar
),并且已知数据足够好,即仅带引号的字符串(无 shell 特殊字符),您可以使用eval
它作为 shell 命令的一部分来评估它:
str='"ubuntu server" foo bar'
eval "arr=($str)"
arr
通过该输入,这将给出包含三个元素ubuntu server
、foo
和 的数组bar
。您可以按照通常的方式使用数组,例如"${arr[@]}"
扩展为单词列表,每个元素一个。
但请注意,使用eval
涉及评估所有 shell 语法的值,而不仅仅是引号。例如,如果字符串包含命令替换$(cmd...)
,则该命令将运行。如果字符串包含不带引号的);
,它将结束复合赋值语句,后面的内容将作为 shell 命令运行。
为了安全起见,请首先验证输入,例如检查它仅包含引号和安全字符。例如,这将允许字母、数字、空格、下划线和两种类型的引号,所有这些应该请注意安全。输入仍然可能有不匹配的引号,这会导致eval
.
re='[^[:alnum:][:blank:]_'\''"]'
if [[ "$str" =~ $re ]]; then
echo "unsafe input" >&2
else
eval "arr=($str)";
if [[ $? -ne 0 ]]; then
echo "error in assignment" >&2
fi
fi
答案3
您可以使用 set 命令进行分词。这是一个笨拙的尝试,但你也许可以改进它。
首先将分隔符设置为双引号:
IFS='"'
然后
set -- $TEST
echo $1
echo $2
ubuntu server
echo $3
foo bar
第一个(2 个字)变量位于 $2 中,其余变量位于 $3 中
a=$2
b=$3
现在对变量 b 进行另一个操作,但这次使用空格分隔符
IFS=' '
set -- $b
echo $1
foo
echo $2
bar