テキストファイルの各行を Bash で反復するにはどうすればよいですか。
このスクリプトでは:
echo "Start!"
for p in (peptides.txt)
do
echo "${p}"
done
この出力は画面に表示されます。
Start!
./runPep.sh: line 3: syntax error near unexpected token `('
./runPep.sh: line 3: `for p in (peptides.txt)'
(後で画面に出力するだけではなく、$p
を使ってもっと複雑なことをしたいと思います。)
環境変数 Shell は(envから)です。
Shell=/bin/bash
/bin/bash --version
の出力:
GNU bash, version 3.1.17(1)-release (x86_64-suse-linux-gnu)
Copyright (C) 2005 Free Software Foundation, Inc.
cat /proc/version
の出力:
Linux version 2.6.18.2-34-default (geeko@buildhost) (gcc version 4.1.2 20061115 (prerelease) (SUSE Linux)) #1 SMP Mon Nov 27 11:46:27 UTC 2006
Peptide.txtファイルには、次のものが含まれています。
RKEKNVQ
IPKKLLQK
QYFHQLEKMNVK
IPKKLLQK
GDLSTALEVAIDCYEK
QYFHQLEKMNVKIPENIYR
RKEKNVQ
VLAKHGKLQDAIN
ILGFMK
LEDVALQILL
それをする一つの方法は:
while read p; do
echo "$p"
done <peptides.txt
コメントで指摘されているように、これは先頭の空白を削除し、バックスラッシュシーケンスを解釈し、終端の改行がない場合は末尾の行をスキップするという副作用があります。これらが懸念であるならば、あなたはすることができます:
while IFS="" read -r p || [ -n "$p" ]
do
printf '%s\n' "$p"
done < peptides.txt
例外として、 ループ本体が標準入力 から読み取られる場合は、別のファイル記述子を使用してファイルを開くことができます。
while read -u 10 p; do
...
done 10<peptides.txt
ここで、10は任意の数です(0、1、2とは異なります)。
cat peptides.txt | while read line
do
# do something with $line here
done
オプション1a: Whileループ:一度に1行ずつ:入力リダイレクト
#!/bin/bash
filename='peptides.txt'
echo Start
while read p; do
echo $p
done < $filename
オプション1b: Whileループ:一度に1行ずつ:
ファイルを開き、ファイルディスクリプタ(この場合はファイルディスクリプタ#4)から読み込みます。
#!/bin/bash
filename='peptides.txt'
exec 4<$filename
echo Start
while read -u4 p ; do
echo $p
done
オプション2: forループ:ファイルを単一の変数に読み込み、解析します。
この構文は、トークン間の空白に基づいて「行」を解析します。与えられた入力ファイルの行がシングルワードトークンであるため、これはまだうまくいきます。 1行に複数のトークンがあると、この方法は機能しません。また、ファイル全体を単一の変数に読み込むことは、大きなファイルには適していません。
#!/bin/bash
filename='peptides.txt'
filelines=`cat $filename`
echo Start
for line in $filelines ; do
echo $line
done
これは他の答えよりも優れているわけではありませんが、スペースなしのファイルでジョブを完成させるもう1つの方法です(コメントを参照)。別のスクリプトファイルを使用するという余分な手順を踏まずに、テキストファイル内のリストを掘り下げるためのワンライナーが必要になることがよくあります。
for Word in $(cat peptides.txt); do echo $Word; done
このフォーマットは私がそれを一つのコマンドラインに入れることを可能にします。 「echo $ Word」の部分を必要なものに変更すると、セミコロンで区切って複数のコマンドを発行できます。次の例では、ファイルの内容を他の2つのスクリプトへの引数として使用します。
for Word in $(cat peptides.txt); do cmd_a.sh $Word; cmd_b.py $Word; done
あるいは、これをストリームエディタのように使うつもりなら(learn sed)、次のように出力を別のファイルにダンプすることができます。
for Word in $(cat peptides.txt); do cmd_a.sh $Word; cmd_b.py $Word; done > outfile.txt
1行に1つのWordで作成したテキストファイルを使用したので、上記のように使用しました。あなたがあなたの単語/行を分割したくないスペースがあるならば、それは少しより上品になります、しかし同じコマンドはまだ以下のように働きます:
OLDIFS=$IFS; IFS=$'\n'; for line in $(cat peptides.txt); do cmd_a.sh $line; cmd_b.py $line; done > outfile.txt; IFS=$OLDIFS
これは、シェルにスペースではなく改行だけで分割するように指示し、その後環境を以前の状態に戻します。ただし、この時点では、すべてを1行にまとめるのではなく、すべてをShellスクリプトにまとめることを検討してください。
頑張って!
他の答えではカバーされていないことがいくつかあります。
# ':' is the delimiter here, and there are three fields on each line in the file
# IFS set below is restricted to the context of `read`, it doesn't affect any other code
while IFS=: read -r field1 field2 field3; do
# process the fields
# if the line has less than three fields, the missing fields will be set to an empty string
# if the line has more than three fields, `field3` will get all the values, including the third field plus the delimiter(s)
done < input.txt
while read -r line; do
# process the line
done < <(command ...)
ここでのwhileループは、後者の場合のようにサブシェルではなく現在のシェルで実行されるため、このアプローチはcommand ... | while read -r line; do ...
よりも優れています。関連する記事を参照してください whileループ内で変更された変数は記憶されません 。
find ... -print0
のように、ヌルで区切られた入力から読み取るwhile read -r -d '' line; do
# logic
# use a second 'read ... <<< "$line"' if we need to tokenize the line
done < <(find /path/to/dir -print0)
関連記事: BashFAQ/020 - 改行、スペース、またはその両方を含むファイル名を見つけて安全に処理する方法を教えてください。
while read -u 3 -r line1 && read -u 4 -r line2; do
# process the lines
# note that the loop will end when we reach EOF on either of the files, because of the `&&`
done 3< input1.txt 4< input2.txt
@ chepnerの answer に基づいて :
-u
はbashの拡張です。 POSIXとの互換性のために、各呼び出しはread -r X <&3
のようになります。
while read -r line; do
my_array+=("$line")
done < my_file
ファイルが不完全な行で終わっている(最後に改行がない)場合、次のようになります。
while read -r line || [[ $line ]]; do
my_array+=("$line")
done < my_file
readarray -t my_array < my_file
または
mapfile -t my_array < my_file
その後
for line in "${my_array[@]}"; do
# process the lines
done
関連記事:
次のようにwhileループを使います。
while IFS= read -r line; do
echo "$line"
done <file
ノート:
IFS
を正しく設定しないと、字下げを失います。
改行文字で読みが壊れたくない場合は、 - を使用してください。
#!/bin/bash
while IFS='' read -r line || [[ -n "$line" ]]; do
echo "$line"
done < "$1"
次に、ファイル名をパラメータとしてスクリプトを実行します。
このファイルがあるとします。
$ cat /tmp/test.txt
Line 1
Line 2 has leading space
Line 3 followed by blank line
Line 5 (follows a blank line) and has trailing space
Line 6 has no ending CR
多くのBashソリューションによって読み取られるファイル出力の意味を変える4つの要素があります。
空白行とCRを含まない終了行を含むテキストファイルを1行ずつ表示したい場合は、whileループを使用し、最後の行に別のテストを行う必要があります。
ファイルを変更する可能性があるメソッドは次のとおりです(cat
が返すものとの比較で)。
1)最後の行と前後のスペースをなくします。
$ while read -r p; do printf "%s\n" "'$p'"; done </tmp/test.txt
'Line 1'
'Line 2 has leading space'
'Line 3 followed by blank line'
''
'Line 5 (follows a blank line) and has trailing space'
(代わりにwhile IFS= read -r p; do printf "%s\n" "'$p'"; done </tmp/test.txt
を実行した場合、先頭と末尾のスペースは保持されますが、最後の行がCRで終わっていない場合でも最後の行は失われます)
2)cat
でプロセス置換を使用すると、ファイル全体が1回の読み取りで読み込まれ、個々の行の意味が失われます。
$ for p in "$(cat /tmp/test.txt)"; do printf "%s\n" "'$p'"; done
'Line 1
Line 2 has leading space
Line 3 followed by blank line
Line 5 (follows a blank line) and has trailing space
Line 6 has no ending CR'
($(cat /tmp/test.txt)
から"
を削除すると、1つの単語ではなくWord単位でファイルを読むことになります。意図したものではないかもしれません...)
ファイルを1行ずつ読み取り、すべてのスペースを保持するための最も堅牢で最も簡単な方法は、次のとおりです。
$ while IFS= read -r line || [[ -n $line ]]; do printf "'%s'\n" "$line"; done </tmp/test.txt
'Line 1'
' Line 2 has leading space'
'Line 3 followed by blank line'
''
'Line 5 (follows a blank line) and has trailing space '
'Line 6 has no ending CR'
先行スペースと取引スペースを削除したい場合は、IFS=
部分を削除します。
$ while read -r line || [[ -n $line ]]; do printf "'%s'\n" "$line"; done </tmp/test.txt
'Line 1'
'Line 2 has leading space'
'Line 3 followed by blank line'
''
'Line 5 (follows a blank line) and has trailing space'
'Line 6 has no ending CR'
(終端の\n
のないテキストファイルは、かなり一般的ですが、POSIXでは壊れていると見なされます。末尾の\n
を信頼できる場合は、while
ループで|| [[ -n $line ]]
は必要ありません。)
BASH FAQ にもっと
#!/bin/bash
#
# Change the file name from "test" to desired input file
# (The comments in bash are prefixed with #'s)
for x in $(cat test.txt)
do
echo $x
done
これは私の実生活の例で、別のプログラム出力の行をループし、部分文字列をチェックし、変数から二重引用符を削除し、ループの外側でその変数を使用する方法です。遅かれ早かれこれらの質問をしている人は多いと思います。
##Parse FPS from first video stream, drop quotes from fps variable
## streams.stream.0.codec_type="video"
## streams.stream.0.r_frame_rate="24000/1001"
## streams.stream.0.avg_frame_rate="24000/1001"
FPS=unknown
while read -r line; do
if [[ $FPS == "unknown" ]] && [[ $line == *".codec_type=\"video\""* ]]; then
echo ParseFPS $line
FPS=parse
fi
if [[ $FPS == "parse" ]] && [[ $line == *".r_frame_rate="* ]]; then
echo ParseFPS $line
FPS=${line##*=}
FPS="${FPS%\"}"
FPS="${FPS#\"}"
fi
done <<< "$(ffprobe -v quiet -print_format flat -show_format -show_streams -i "$input")"
if [ "$FPS" == "unknown" ] || [ "$FPS" == "parse" ]; then
echo ParseFPS Unknown frame rate
fi
echo Found $FPS
ループの外側で変数を宣言し、値を設定し、それをループの外側で使用するには done <<< "$(...)" という構文が必要です。アプリケーションは現在のコンソールのコンテキスト内で実行する必要があります。コマンドを囲む引用符は出力ストリームの改行を保持します。
次に部分文字列のループマッチは name = value pairを読み取り、最後の = 文字の右側部分を分割し、最初の引用符を削除し、最後の引用符を削除します。
@ピーター:これはあなたのためにうまくいくかもしれない -
echo "Start!";for p in $(cat ./pep); do
echo $p
done
これは出力を返します
Start!
RKEKNVQ
IPKKLLQK
QYFHQLEKMNVK
IPKKLLQK
GDLSTALEVAIDCYEK
QYFHQLEKMNVKIPENIYR
RKEKNVQ
VLAKHGKLQDAIN
ILGFMK
LEDVALQILL
これはかなり遅いですが、誰かを助けるかもしれないという考えで、答えを追加しています。また、これは最良の方法ではないかもしれません。 head
コマンドは、-n
引数とともに使用して、n行をファイルの先頭から読み取り、同様に、tail
コマンドを使用して下から読み取ることができます。ここで、ファイルからnth行を取得するために、n行に進みます。パイプされたデータから1行だけテールにデータをパイプします。
TOTAL_LINES=`wc -l $USER_FILE | cut -d " " -f1 `
echo $TOTAL_LINES # To validate total lines in the file
for (( i=1 ; i <= $TOTAL_LINES; i++ ))
do
LINE=`head -n$i $USER_FILE | tail -n1`
echo $LINE
done