如何用空格分割字符串,其中包含带引号的值

如何用空格分割字符串,其中包含带引号的值

以下面的 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 serverfoo和 的数组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

相关内容