GNU/Linux >> Linux の 問題 >  >> Linux

シェル スクリプトの for ループで単語の代わりに行を繰り返す

用途

for l in $() IFS に基づいて単語分割を実行します:

$ for l in $(printf %b 'a b\nc'); do echo "$l"; done
a
b
c
$ IFS=$'\n'; for l in $(printf %b 'a b\nc'); do echo "$l"; done
a b
c

後で使用しない場合、IFS を元に戻す必要はありません。

for l in $() パス名展開も実行します:

$ printf %b 'a\n*\n' > file.txt
$ IFS=$'\n'
$ for l in $(<file.txt); do echo "$l"; done
a
file.txt
$ set -f; for l in $(<file.txt); do echo "$l"; done; set +f
a
*

IFS=$'\n' の場合 、改行は取り除かれ、折りたたまれます:

$ printf %b '\n\na\n\nb\n\n' > file.txt
$ IFS=$'\n'; for l in $(<file.txt); do echo "$l"; done
a
b

$(cat file.txt) (または $(<file.txt) ) また、ファイル全体をメモリに読み込みます。

読み取りの使用

-r を使用しない場合、バックスラッシュは行の継続に使用され、他の文字の前に削除されます:

$ cat file.txt
\1\\2\
3
$ cat file.txt | while read l; do echo "$l"; done
1\23
$ cat file.txt | while read -r l; do echo "$l"; done
\1\\2\
3

IFS の文字は行頭と行末から削除されますが、折りたたまれません:

$ printf %b '1  2 \n\t3\n' | while read -r l; do echo "$l"; done
1  2
3
$ printf %b ' 1  2 \n\t3\n' | while IFS= read -r l; do echo "$l"; done
 1  2 
    3

最後の行が改行で終わっていない場合、read はそれに l を割り当てますが、ループの本体の前に終了します:

$ printf 'x\ny' | while read l; do echo $l; done
x
$ printf 'x\ny' | while read l || [[ $l ]]; do echo $l; done
x
y

while ループがパイプラインにある場合、それはサブシェルにもあるため、変数はその外側では見えません:

$ x=0; seq 3 | while read l; do let x+=l; done; echo $x
0
$ x=0; while read l; do let x+=l; done < <(seq 3); echo $x
6
$ x=0; x=8 | x=9; echo $x
0

for loop は、「行」をループするようには設計されていません。代わりに、「単語」をループします。

短い用語:「行」は、改行で区切られたものです。 「単語」とは、スペース (および改行など) で区切られたものです。 bash 用語では「単語」は「フィールド」と呼ばれます。

行をループする慣用的な方法は、 while を使用することです read と組み合わせたループ .

ioscan -m dsf | while read -r line
do
  printf '%s\n' "$line"
done

while ループは、パイプのためにサブシェルにあることに注意してください。これにより、変数のスコープで混乱が生じる可能性があります。 bash では、プロセス置換を使用してこれを回避できます。

while read -r line
do
  printf '%s\n' "$line"
done < <(ioscan -m dsf)

http://mywiki.wooledge.org/BashFAQ/024 も参照してください

for ループは、$IFS 内の文字を使用して、ループする対象を分割します。 セパレーターとしての変数。 IFS は、Internal Field Separator の略です。通常 $IFS スペース、タブ、および改行が含まれています。つまり、for loop は、行ではなく「単語」をループします。

行をループするために for ループを使用することを主張する場合は、 $IFS の値を変更する必要があります 改行のみに。ただし、これを行う場合は、古い値の $IFS を保存する必要があります 他の多くのことも $IFS に依存しているため、ループの後にそれを復元します .

OLDIFS="$IFS"
IFS=$'\n' # bash specific
for line in $(ioscan -m dsf)
do
  printf '%s\n' "$line"
done
IFS="$OLDIFS"

ANSI-C クォーティング ($'\n') を持たない POSIX シェルでは、 )、次のようにできます:

IFS='
'

つまり、引用符の間に実際の改行を入れます。

または、サブシェルを使用して $IFS への変更を含めることができます :

(
  # changes to variables in the subshell stay in the subshell
  IFS=$'\n'
  for line in $(ioscan -m dsf)
  do
    printf '%s\n' "$line"
  done
)
# $IFS is not changed outside of the subshell

ただし、ループ内のコマンド自体が $IFS の適切な設定に依存している可能性があることに注意してください。 .次に、$IFS を復元する必要があります コマンドを実行する前に、次のループなどの前に再度設定してください。 $IFS をいじることはお勧めしません . $IFS の適切な値に依存するコマンドが多すぎます そしてそれを変更することは、あいまいなバグハンティングの果てしない悪夢です.

こちらもご覧ください:

  • http://wiki.bash-hackers.org/syntax/ccmd/classic_for
  • http://wiki.bash-hackers.org/commands/builtin/read
  • http://mywiki.wooledge.org/IFS
  • http://mywiki.wooledge.org/SubShell
  • http://mywiki.wooledge.org/ProcessSubstitution

Linux
  1. Linux シェル スクリプト用の 12 の Bash for ループの例

  2. Linux シェル スクリプトまたは変数の日付と時刻の書式設定

  3. シェルスクリプトとメイクファイルの命名規則

  1. Bash スクリプト:セッションをログに記録するための bash スクリプトからのスクリプト コマンドの使用

  2. データベース バックアップ用の Linux シェル スクリプト

  3. 2 番目の要素から for ループを開始する - シェル スクリプト

  1. Linux シェルの音訳スクリプト

  2. シェル スクリプト while ループ:[ パイプラインの周りに `]' がありません

  3. ネストされた for ループ