shibatch's journey

日々考えていることをつらつら書くだけです

bashスクリプトではテキストを1行ずつ読むループ処理ではパイプを使うことは避ける

先日久しぶりにbashスクリプトを書いたときにレビューで指摘いただいて初めて知ったことなので書き留めておく。

bash スクリプトで1行ずつ読み込んでwhile read lineを使うときの構文。

今までこのように、while read lineの前に変数やテキストファイルを展開して読ませていました。

$ cat front.sh
#/bin/bash

LIST="aaaa
bbbb
cccc
dddd"

echo "${LIST}" | while read line     👈ここに注目!
do
  echo "====LOOP====="
  echo "$line"
  DEFINE_IN_LOOP="TEST"
done

echo "$DEFINE_IN_LOOP"

さてこれを実行すると以下の通りになります。

====LOOP=====
aaaa
====LOOP=====
bbbb
====LOOP=====
cccc
====LOOP=====
dddd

echo "$DEFINE_IN_LOOP" の結果が出てきません。これはパイプでLISTの内容を渡しているのでサブシェルでループが走り、ループを抜けると中で定義された変数はなかったことにされるからですね。

なのでパイプを使わずにこう書くのがよさそう。

$ cat back.sh
#/bin/bash

LIST="aaaa
bbbb
cccc
dddd"

while read line      👈パイプを使わないように変えた
do
  echo "====LOOP====="
  echo "$line"
  DEFINE_IN_LOOP="TEST"
done < <(echo "${LIST}")

echo "$DEFINE_IN_LOOP"

これであればループの中で定義した変数をループから抜けても取り出せます。

====LOOP=====
aaaa
====LOOP=====
bbbb
====LOOP=====
cccc
====LOOP=====
dddd
TEST

この記法はbashじゃないとできないことに注意(shではできない)

 sh back.sh
back.sh: 行 13: 予期しないトークン `<' 周辺に構文エラーがあります
back.sh: 行 13: `done < <(echo "${LIST}")'

★追記 コメントいただきました🙏

というわけで確かめてみます。 ループ処理の中にsleepを入れてps で確認。

こちらがパイプを使っているほう

vagrant   6345  0.0  0.2 157044  2616 ?        S    12月27   0:00      \_ sshd: vagrant@pts/0
vagrant   6346  0.0  0.2 115688  2160 pts/0    Ss   12月27   0:00          \_ -bash
vagrant  24285  0.0  0.1 113280  1360 pts/0    S    01:20   0:00              \_ bash front.sh
vagrant  24287  0.0  0.0 113284   644 pts/0    S    01:20   0:00              |   \_ bash front.sh
vagrant  24288  0.0  0.0 108052   360 pts/0    S    01:20   0:00              |       \_ sleep 5
vagrant  24293  0.0  0.1 155600  2012 pts/0    R+   01:20   0:00              \_ ps auxfww

こちらがパイプを使わないほう

vagrant   6345  0.0  0.2 157044  2616 ?        S    12月27   0:00      \_ sshd: vagrant@pts/0
vagrant   6346  0.0  0.2 115688  2172 pts/0    Ss   12月27   0:00          \_ -bash
vagrant  24362  0.0  0.1 113284  1404 pts/0    S    01:22   0:00              \_ bash back.sh
vagrant  24371  0.0  0.0 108052   360 pts/0    S    01:22   0:00              |   \_ sleep 5
vagrant  24372  0.0  0.1 155600  2012 pts/0    R+   01:22   0:00              \_ ps auxfww

確かに、パイプを使わないほうはサブシェルが起動されていないことがわかります。