Forループライン - 1つの "for"ステートメントにのみIFSを設定する方法は?

Forループライン - 1つの

以下は、私が達成したい動作の例です。

行リストがあり、各行にスペースで区切られた値が含まれているとします。

lines='John Smith
James Johnson'

ユーザーが求めるプロンプトを押すと、名前や姓をループバックしたいので、IFSを全体的に変更します。

oIFS=$IFS
IFS='
'
for line in $lines; do
    IFS=$oIFS
    read name surname <<< $line
    read -p "Do you want to echo name? surname otherwise "
    if [[ $REPLY == "y" ]]; then
        echo $name
    else
        echo $surname
    fi
done

これはうまくいきますが、このアプローチは私には適していないようです。

  1. IFSを変更しましたが、おそらく復元するのを忘れていました。
  2. ループが繰り返されるたびにIFSを復元します。

while IFS=...ここでは、次のように使用できることがわかりました。

while IFS='
' read line ; do
    echo $line
    read name surname <<< $line
    read -p "Do you want to echo name? surname otherwise "
    if [[ $REPLY == "y" ]]; then
        echo $name
    else
        echo $surname
    fi
done <<< "$lines"

read -pただし、継続的な入力ストリームによってプロンプトが破損するため、これはオプションではありません。

1つの解決策は、for次のように1つのステートメントにのみIFSを設定することです。

IFS='
' for line in $lines; do
    ...
done

しかし、bashはこれを許可しません。

ベストアンサー1

mapfileまず、/を使用して入力ラインを配列として読み取ることができますreadarray

lines='John Smith
James Johnson'
mapfile -t lines <<< "$lines"
for line in "${lines[@]}"; do
    read name surname <<< "$line"
    echo "name: $name surname: $surname"
done

lines出力がコマンドから出力された場合でも、同様にmapfile -t lines < <(somecommand)直接使用できます。プロセスの交換はパイプと少し似ていますが、サブシェルで実行されるパイプ部分の問題を防ぎます。lines最後の行の最後に改行文字がありませんが、1つ<<<を追加しました。mapfile欠けても気にしないでください。ただし、末尾に改行文字がある場合は、追加の項目をlines保持する空の配列項目が取得されます。この問題は、プロセス交換を使用してバイパスできます。


ここでは、

while IFS=... read line ; do
    ...
    read -p "Do you want to echo name? surname otherwise "
done <<< "$lines"

どちらもread同じ入力から読み取られますが(テストされていません)、別のファイル記述子を使用してループにリダイレクトすることでこの問題を解決できると思います。例:

while IFS=... read -u 3 line ; do
    ...
    read -p "Do you want to echo name? surname otherwise "
done 3<<< "$lines"

それともそれが重要なのかわかりませんread <&3read -u

おすすめ記事