引用符で囲まれた値を含む文字列を空白に分割する方法

引用符で囲まれた値を含む文字列を空白に分割する方法

次のbashの例を見てください(実際の出力はから来ていることに注意してくださいvboxmanage list vms)。

TEST='"ubuntu server" foo bar'
echo $TEST
"ubuntu server" foo bar

(ループまたは別の変数から)3つの値をどのように分離できますか?私が見つけたすべての解決策はスペース区切りに依存し、最初の値を破壊します。

答え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

長い形式を使用してシステムを一覧表示し、名前を解析します。その後、これらの名前はシェルループから読み取られ、各名前に対して短いメッセージが印刷されます。

名前を配列として読むこともできます。

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最初の質問(例:)のコードブロックに表示される形式の文字列であると仮定すると、既知のデータで十分です。つまり、eval評価のためにシェルコマンドの一部として使用できる引用符専用文字列(シェル特殊文字なし)。

str='"ubuntu server" foo bar'
eval "arr=($str)"

arrこの入力を使用すると、および3つの要素を持つ配列が提供されますubuntu server。一般的な方法で配列を使用できます(たとえば、要素ごとに1つずつ単語リストに展開します)。foobar"${arr[@]}"

ただし、使用法にはeval引用符だけでなく、すべてのシェル構文値を評価することも含まれます。たとえば、文字列にコマンド置換が含まれている場合、$(cmd...)そのコマンドが実行されます。文字列に引用符が含まれていない場合は、);複合代入文が終了し、後続の内容がシェルコマンドで実行されます。

安全を確保するには、まず入力を検証してください。たとえば、引用符と安全な文字のみが含まれていることを確認します。たとえば、文字、数字、スペース、アンダースコア、および両方のタイプの引用符を使用できます。しなければならないご注意ください。入力にまだ一致しない引用符がある可能性があります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

関連情報