Bashシェルスクリプトで”while read”ループ内で”read”コマンドを使う方法

2023-03-27Bash,CentOS,Linux,Ubuntu

はじめに

決して多くないケースですが、 while read ループ中で read コマンドを利用したいという場合に起きる問題と、その対処法について紹介します。

検証環境

$ uname -moi
aarch64 aarch64 GNU/Linux

$ head -n 3 /etc/os-release
PRETTY_NAME="Ubuntu Kinetic Kudu (development branch)"
NAME="Ubuntu"
VERSION_ID="22.10"

$ bash -version | head -n 1
GNU bash, バージョン 5.2.0(1)-release (aarch64-unknown-linux-gnu)

while read ループ中で read コマンドを利用するとどうなるか?

while read ループ中で read コマンドを利用するとどうなるか。
具体例をもとに見ていきます。

例えば、簡単な英単語の暗記スクリプトを作ってみます。

仕様は単純で、単語を1つ表示しては ENTER キーの入力を待つ、を繰り返します。

$ cat <<EOF >words.txt
one
two
three
EOF

$ cat words.txt \
  | while read -r WORD; do
    echo "WORD: $WORD"
    echo "---"
    read -r -p "次に進むには ENTER を押してね >"
    echo "---"
  done

実行結果は以下のようになります。

$ cat words.txt \
  | while read -r WORD; do
    echo "WORD: $WORD"
    echo "---"
    read -r -p "次に進むには ENTER を押してね >"
    echo "---"
  done
WORD: one
---
---
WORD: three
---
---

ENTER キーの入力を求められないどころか、 two という単語が表示されていません。

cat words.txt | ... のようにパイプを使わず、リダイレクトで読み込ませても結果は変わりません。

$ while read -r NUM; do
  echo "NUM: $NUM"
  echo "---"
  read -r -p "Press enter to continue"
  echo "---"
done <words.txt
NUM: one
---
---
NUM: three
---
---

これはループ内の read が パイプあるいはリダイレクトで渡された words.txt の内容を読み込んでしまうことが原因です。

対処法 1 : while read ループを避け for ループを使用する

while read を避けられるのなら for ループに書き換えます。

ちゃんと、3 回 ENTER キーの入力を求められます。

$ for NUM in $(cat words.txt); do
  echo "NUM: $NUM"
  echo "---"
  read -r -p "Press enter to continue"
  echo "---"
done
NUM: one
---
Press enter to continue
---
NUM: two
---
Press enter to continue
---
NUM: three
---
Press enter to continue
---

対処法 2 : /dev/tty から read するように明示する

どうしても while read を使いたい場合には、 ループ内の read に対して TTY からの入力を待ち受けるように明示します。

ちゃんと、3 回 ENTER キーの入力を求められます。

$ while read -r NUM; do
  echo "NUM: $NUM"
  echo "---"
  read -r -p "Press enter to continue" </dev/tty
  echo "---"
done <words.txt
NUM: one
---
Press enter to continue
---
NUM: two
---
Press enter to continue
---
NUM: three
---
Press enter to continue
---

ひとこと

たまたまインタラクティブな操作を求めるループ処理を書いたときに遭遇した問題でした。

2023-03-27Bash,CentOS,Linux,Ubuntu