Pertanyaan Looping melalui isi file di Bash


Bagaimana cara melakukan iterasi melalui setiap baris file teks dengan Pesta?

Dengan skrip ini:

echo "Start!"
for p in (peptides.txt)
do
    echo "${p}"
done

Saya mendapatkan output ini di layar:

Start!
./runPep.sh: line 3: syntax error near unexpected token `('
./runPep.sh: line 3: `for p in (peptides.txt)'

(Kemudian saya ingin melakukan sesuatu yang lebih rumit $p dari sekadar keluaran ke layar.)


Variabel lingkungan KULIT adalah (dari env):

SHELL=/bin/bash

/bin/bash --version keluaran:

GNU bash, version 3.1.17(1)-release (x86_64-suse-linux-gnu)
Copyright (C) 2005 Free Software Foundation, Inc.

cat /proc/version keluaran:

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

File peptides.txt mengandung:

RKEKNVQ
IPKKLLQK
QYFHQLEKMNVK
IPKKLLQK
GDLSTALEVAIDCYEK
QYFHQLEKMNVKIPENIYR
RKEKNVQ
VLAKHGKLQDAIN
ILGFMK
LEDVALQILL

937
2017-10-05 17:52


asal


Jawaban:


Salah satu cara untuk melakukannya adalah:

while read p; do
  echo $p
done <peptides.txt

Luar biasa, jika tubuh loop dapat membaca dari input standar, Anda dapat membuka file menggunakan deskriptor file yang berbeda:

while read -u 10 p; do
  ...
done 10<peptides.txt

Di sini, 10 hanyalah angka acak (berbeda dari 0, 1, 2).


1514
2017-10-05 18:00



cat peptides.txt | while read line
do
   # do something with $line here
done

293
2017-10-05 17:54



Opsi 1a: Sedangkan loop: Single line pada satu waktu: Input redirection

#!/bin/bash
filename='peptides.txt'
echo Start
while read p; do 
    echo $p
done < $filename

Opsi 1b: Saat loop: Satu baris pada satu waktu:
Buka file, baca dari deskriptor file (dalam hal ini file descriptor # 4).

#!/bin/bash
filename='peptides.txt'
exec 4<$filename
echo Start
while read -u4 p ; do
    echo $p
done

Pilihan 2: Untuk loop: Baca file ke dalam variabel tunggal dan parse.
Sintaks ini akan mengurai "garis" berdasarkan ruang putih di antara token. Ini masih berfungsi karena baris file input yang diberikan adalah token satu kata. Jika ada lebih dari satu token per baris, maka metode ini tidak akan berfungsi. Juga, membaca file lengkap menjadi variabel tunggal bukanlah strategi yang baik untuk file besar.

#!/bin/bash
filename='peptides.txt'
filelines=`cat $filename`
echo Start
for line in $filelines ; do
    echo $line
done

107
2017-10-05 18:18



Ini tidak lebih baik daripada jawaban lainnya, tetapi merupakan salah satu cara lain untuk menyelesaikan pekerjaan dalam file tanpa spasi (lihat komentar). Saya menemukan bahwa saya sering membutuhkan satu baris untuk menggali daftar dalam file teks tanpa langkah ekstra menggunakan file skrip terpisah.

for word in $(cat peptides.txt); do echo $word; done

Format ini memungkinkan saya untuk menempatkan semuanya dalam satu baris perintah. Ubah bagian "echo $ word" ke apa pun yang Anda inginkan dan Anda dapat mengeluarkan beberapa perintah yang dipisahkan oleh titik koma. Contoh berikut menggunakan isi file sebagai argumen ke dalam dua skrip lain yang mungkin telah Anda tulis.

for word in $(cat peptides.txt); do cmd_a.sh $word; cmd_b.py $word; done

Atau jika Anda ingin menggunakan ini seperti editor aliran (pelajari sed), Anda dapat membuang output ke file lain sebagai berikut.

for word in $(cat peptides.txt); do cmd_a.sh $word; cmd_b.py $word; done > outfile.txt

Saya telah menggunakan ini sebagaimana ditulis di atas karena saya telah menggunakan file teks di mana saya telah membuatnya dengan satu kata per baris. (Lihat komentar) Jika Anda memiliki spasi yang Anda tidak ingin memisahkan kata-kata / garis Anda, itu menjadi sedikit lebih jelek, tetapi perintah yang sama masih berfungsi sebagai berikut:

OLDIFS=$IFS; IFS=$'\n'; for line in $(cat peptides.txt); do cmd_a.sh $line; cmd_b.py $line; done > outfile.txt; IFS=$OLDIFS

Ini hanya memberitahu shell untuk membelah pada baris baru saja, bukan spasi, lalu mengembalikan lingkungan kembali ke apa yang sebelumnya. Pada titik ini, Anda mungkin ingin mempertimbangkan untuk memasukkan semuanya ke dalam skrip shell daripada memeras semuanya ke dalam satu baris.

Semoga berhasil!


60
2017-10-04 13:30



Gunakan loop sementara, seperti ini:

while IFS= read -r line; do
   echo "$line"
done <file

Catatan:

  1. Jika Anda tidak mengatur IFS dengan benar, Anda akan kehilangan indentasi.

  2. Anda harus selalu menggunakan opsi -r dengan membaca.

  3. Jangan membaca garis dengan for


36
2018-06-09 15:09



Beberapa hal lagi yang tidak tercakup oleh jawaban lain:

Membaca dari file yang dibatasi

# ':' 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

Membaca dari output dari perintah lain, menggunakan substitusi proses

while read -r line; do
  # process the line
done < <(command ...)

Pendekatan ini lebih baik daripada command ... | while read -r line; do ... karena loop sementara di sini berjalan di shell saat ini daripada subkulit seperti dalam kasus yang terakhir. Lihat pos terkait Sebuah variabel yang dimodifikasi di dalam loop sementara tidak diingat.

Membaca dari input terbatas null, misalnya 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)

Bacaan terkait: BashFAQ / 020 - Bagaimana saya dapat menemukan dan menangani dengan aman nama file yang berisi baris baru, spasi atau keduanya?

Membaca dari lebih dari satu file dalam satu waktu

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

Berdasarkan @ chepner menjawab sini:

-u adalah ekstensi bash. Untuk kompatibilitas POSIX, setiap panggilan akan terlihat seperti itu read -r X <&3.

Membaca seluruh file ke dalam sebuah array (versi Bash sebelumnya ke 4)

while read -r line; do
    my_array+=("$line")
done < my_file

Jika file berakhir dengan garis yang tidak lengkap (baris baru hilang di bagian akhir), maka:

while read -r line || [[ $line ]]; do
    my_array+=("$line")
done < my_file

Membaca seluruh file ke dalam array (Bash versi 4x dan yang lebih baru)

readarray -t my_array < my_file

atau

mapfile -t my_array < my_file

Lalu

for line in "${my_array[@]}"; do
  # process the lines
done

Pos terkait:


27
2018-01-14 03:30



Jika Anda tidak ingin bacaan Anda dipatahkan oleh karakter baris baru, gunakan -

#!/bin/bash
while IFS='' read -r line || [[ -n "$line" ]]; do
    echo "$line"
done < "$1"

Kemudian jalankan skrip dengan nama file sebagai parameter.


8
2018-03-08 16:10



Misalkan Anda memiliki file ini:

$ 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

Ada empat elemen yang akan mengubah arti dari output file yang dibaca oleh banyak solusi Bash:

  1. Baris kosong 4;
  2. Ruang leading atau trailing pada dua garis;
  3. Mempertahankan arti dari masing-masing baris (yaitu, setiap baris adalah catatan);
  4. Garis 6 tidak diakhiri dengan CR.

Jika Anda ingin file teks baris demi baris termasuk garis kosong dan mengakhiri garis tanpa CR, Anda harus menggunakan loop sementara dan Anda harus memiliki tes alternatif untuk baris terakhir.

Berikut adalah metode yang dapat mengubah file (dibandingkan dengan apa cat kembali):

1) Kehilangan baris terakhir dan spasi utama dan tambahan:

$ 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'

(Jika kamu melakukan while IFS= read -r p; do printf "%s\n" "'$p'"; done </tmp/test.txt sebagai gantinya, Anda melestarikan ruang di depan dan di belakang, tetapi tetap kehilangan baris terakhir jika tidak dihentikan dengan CR)

2) Menggunakan substitusi proses dengan cat akan membaca seluruh file dalam satu tegukan dan kehilangan arti dari masing-masing baris:

$ 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'

(Jika Anda menghapus " dari $(cat /tmp/test.txt) Anda membaca file kata demi kata daripada satu tegukan. Juga mungkin bukan apa yang dimaksud ...)


Cara paling kuat dan paling sederhana untuk membaca file baris demi baris dan melestarikan semua spasi adalah:

$ 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'

Jika Anda ingin menghapus ruang terdepan dan perdagangan, hapus IFS= bagian:

$ 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'

(File teks tanpa penghentian \n, sementara cukup umum, dianggap rusak di bawah POSIX. Jika Anda dapat mengandalkan trailing \n Anda tidak perlu || [[ -n $line ]] dalam while lingkaran.)

Lebih lanjut di FAQ BASH


6
2018-02-03 19:15



#!/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

4
2017-11-14 14:23