解説
readしたら空白やバックスラッシュが消える?BashのIFS=と-rで一行を保とう
入力の前後の空白とバックスラッシュを、四つのreadで比較。IFSによる分割と-rの役割を分け、終了時の注意も小さな作例で確認。

入力した一行を変数へ入れたのに、前後の空白やバックスラッシュがなくなっていた。Bashのreadは、そのまま一行を保存するだけの動きとは限らないよ。
このページでは、空白とバックスラッシュを含む架空の文字を使い、読み取り方を比べる。Bashのターミナルが必要だよ。PowerShellやWindowsの通常のコマンドプロンプトへ、そのまま貼り付けないでね。
readは文字を変数へ受け取る
readは、入力から一行を読み、指定した変数へ値を入れる組み込みコマンド。今回の例はキーボード入力を待たず、<<<で練習用の文字列を渡すよ。これはBashの書き方で、末尾に改行を加えて渡す。
前後に空白が二つずつあり、途中に一つのバックスラッシュがある値を用意しよう。
blue\path
ここでは空白・タブ・改行を区切りにする通常のIFSを前提にする。IFSは、入力を単語へ分けるときの区切り文字の設定だよ。
空白とバックスラッシュは、別の条件で変わる
普通のreadでは、区切りの空白の扱いに加え、バックスラッシュが次の文字を保護するための印として働く。今回のblue\pathでは、バックスラッシュがそのままの文字として残らないんだ。
| 読み方 | 前後の空白 | バックスラッシュ |
|---|---|---|
| read | 残らない | 残らない |
| read -r | 残らない | 残る |
| IFS= read | 残る | 残らない |
| IFS= read -r | 残る | 残る |
-rは、バックスラッシュをエスケープの印として扱わない指定。IFS=は区切り文字を空にする指定だよ。どちらか一方だけで、両方の性質が変わるわけではない。
四通りを一緒に試す
次の一まとまりをBashで実行する。丸括弧の中だけで練習用の変数を使うため、終わると親のシェルへその変数は残らない。ファイルや設定の保存はしないよ。
(
practice_value=' blue\path '
read ordinary <<< "$practice_value"
read -r slash <<< "$practice_value"
IFS= read spaces <<< "$practice_value"
IFS= read -r preserved <<< "$practice_value"
printf 'ordinary:<%s>\n' "$ordinary"
printf 'slash:<%s>\n' "$slash"
printf 'spaces:<%s>\n' "$spaces"
printf 'preserved:<%s>\n' "$preserved"
)
期待する表示は次の四行。<と>を付けると、値の前後に空白が残っているか見やすいよ。
ordinary:<bluepath>
slash:<blue\path>
spaces:< bluepath >
preserved:< blue\path >
IFS=はreadの呼び出しへ付けた設定で、今回の書き方では親シェル全体のIFSを書き換える手順にしていないよ。表示側も"$preserved"のように二重引用符で囲み、読み取った後の分割を避けている。
一行の終わりと、データの終わりも違う
通常のreadは、行を区切る改行そのものを値へ含めない。今回の「保つ」は、改行以外の一行の内容を保つという意味だよ。バイナリーファイルを一字も変えずにコピーするための道具ではないんだ。
また、改行のない最終行では、値が入ってもreadの終了状態が成功にならないことがある。ファイルを全行処理するループを書くときは、EOF、つまり入力の終わりで最後の値を落とさない設計も別に確認しよう。今回の<<<は改行を加えるので、そのケースを再現していないよ。

みんなのコメント 0
感想や、試してみたことを聞かせてね。コメント・返信は承認後に公開されます。
最初のコメントをどうぞ。