Baca file berorientasi baris yang mungkin tidak diakhiri dengan baris baru

11

Saya memiliki file dengan nama /tmp/urlFiletempat setiap baris mewakili url. Saya mencoba membaca dari file sebagai berikut:

cat "/tmp/urlFile" | while read url
do
    echo $url
done

Jika baris terakhir tidak berakhir dengan karakter baris baru, baris itu tidak akan dibaca. Saya bertanya-tanya mengapa?

Apakah mungkin untuk membaca semua baris, terlepas dari apakah itu diakhiri dengan baris baru atau tidak?

Tim
sumber
8
Ini dibahas di Mengapa menggunakan shell loop untuk memproses teks yang dianggap praktik buruk? (dengan beberapa cara untuk melakukannya)
Stéphane Chazelas
2
Hah @ Stéphane Saya suka TBD di sana ;-).
Stephen Kitt
2
Cara lain untuk menambahkan baris tambahan jika tidak ada; awk 1 /tmp/urlFile.. soawk 1 /tmp/urlFile | while ...
muru
@uru, itu jawaban yang lebih baik daripada yang lain di sini.
Wildcard
1
Karena Anda bertanya mengapa tidak dibaca: stackoverflow.com/a/729795/1968
Konrad Rudolph

Jawaban:

13

Anda akan melakukannya:

while IFS= read -r url || [ -n "$url" ]; do
  printf '%s\n' "$url"
done < url.list

(secara efektif, loop itu menambahkan kembali baris baru yang hilang pada baris terakhir (non-)).

Lihat juga:

Stéphane Chazelas
sumber
Terima kasih. Saya membaca artikel yang ditautkan, dan mungkin saya melewatkan sesuatu, mengapa "loop itu menambahkan kembali baris baru yang hilang pada baris terakhir (non-)"?
Tim
1
@Tim Apa yang tampaknya Stephane maksudkan adalah bahwa ia menambah kembali baris baru yang hilang dalam output karena semua printfpanggilan di sini \n.
Sergiy Kolodyazhnyy
6

Ini tampaknya diselesaikan sebagian dengan readarray -t:

readarray -t urls "/tmp/urlFile"
for url in "${urls[@]}"; do
    printf '%s\n' "$url"
done

Namun perlu dicatat bahwa sementara ini bekerja untuk file berukuran cukup, solusi ini memperkenalkan potensi masalah baru dengan file yang sangat besar - pertama kali membaca file ke dalam array yang kemudian harus diulangi. Untuk file yang sangat besar ini bisa memakan waktu dan memori, berpotensi sampai pada titik kegagalan.

DopeGhoti
sumber
Terima kasih. Bagian mana yang dipecahkan dan mana yang tidak?
Tim
Ini memecahkan masalah dengan tidak adanya baris baru, tetapi memperkenalkan potensi masalah baru dengan file yang sangat besar, karena pertama kali membaca file ke dalam array yang kemudian harus diulangi.
DopeGhoti
1
@DopeGhoti Itu informasi yang bagus - dapatkah saya menyarankan Anda menambahkannya langsung ke dalam jawabannya?
RJHunter
Jawabannya telah diubah.
DopeGhoti
5

Menurut definisi , file teks terdiri dari urutan garis. Sebuah garis diakhiri dengan karakter baris baru. Dengan demikian file teks berakhir dengan karakter baris baru, kecuali jika kosong.

The readbuiltin hanya dimaksudkan untuk membaca file teks. Anda tidak melewatkan file teks, jadi Anda tidak bisa berharap itu berfungsi dengan mulus. Shell membaca semua baris - apa yang dilompati adalah karakter tambahan setelah baris terakhir.

Jika Anda memiliki file input yang berpotensi cacat yang mungkin hilang pada baris terakhir, Anda dapat menambahkan baris baru untuknya, hanya untuk memastikan.

{ cat "/tmp/urlFile"; echo; } | 

File yang seharusnya berupa file teks tetapi tidak ada baris terakhir akhir sering diproduksi oleh editor Windows. Ini biasanya berjalan dalam kombinasi dengan ujung garis Windows, yang CR LF, yang bertentangan dengan LF Unix. Karakter CR jarang berguna di mana saja, dan tidak dapat muncul dalam URL dalam hal apa pun, jadi Anda harus menghapusnya.

{ <"/tmp/urlFile" tr -d '\r'; echo; } | 

Jika file input terbentuk dengan baik dan diakhiri dengan baris baru, baris echotambahan kosong ditambahkan. Karena URL tidak boleh kosong, abaikan saja baris kosong.

Perhatikan juga bahwa readtidak membaca garis secara langsung. Itu mengabaikan spasi putih terkemuka, dan yang untuk URL mungkin diinginkan. Ini memperlakukan backslash pada akhir baris sebagai karakter pelarian, menyebabkan baris berikutnya bergabung dengan yang pertama minus urutan backslash-newline, yang jelas tidak diinginkan. Jadi, Anda harus meneruskan -ropsi ke read. Sangat, sangat jarang untuk readmenjadi hal yang benar daripada read -r.

{ <"/tmp/urlFile" tr -d '\r'; echo; } | while read -r url
do
  if [ -z "$url" ]; then continue; fi
  
done
Gilles 'SANGAT berhenti menjadi jahat'
sumber
3

Nah, readmengembalikan nilai palsu jika memenuhi akhir file sebelum baris baru, tetapi bahkan jika itu, nilai itu masih menetapkan nilai yang dibacanya. Jadi, kita dapat memeriksa apakah panggilan akhir readmengembalikan sesuatu selain dari baris kosong, dan memprosesnya seperti biasa. Jadi, hanya keluar dari loop setelah readmengembalikan false dan baris kosong:

#!/bin/sh
while IFS= read -r line || [ "$line" ]; do 
    echo "line: $line"
done

$ printf 'foo\nbar' | sh ./read.sh 
line: foo
line: bar
$ printf 'foo\nbar\n' | sh ./read.sh 
line: foo
line: bar
ilkkachu
sumber
1

Cara lain akan seperti ini:

Ketika membaca mencapai end-of-file, bukan end-of-line, itu membaca dalam data dan menetapkannya ke variabel, tetapi keluar dengan status bukan nol. Jika loop Anda dikonstruksi "saat membaca; lakukan hal-hal; dilakukan

Jadi, alih-alih menguji status keluar baca secara langsung, uji bendera, dan minta perintah baca mengatur bendera itu dari dalam badan loop. Dengan cara itu terlepas dari status keluar baca, seluruh badan loop berjalan, karena membaca hanyalah salah satu dari daftar perintah dalam loop seperti yang lain, bukan faktor penentu apakah loop akan dijalankan sama sekali.

DONE=false
until $DONE ;do
read || DONE=true
echo $REPLY 
done < /tmp/urlFile

Dirujuk dari sini .

Pemburu. Thompson
sumber
1
cat "/ tmp / urlFile" | saat membaca url
melakukan
    echo $ url
selesai

Ini adalah Penggunaan yang Tidak Berguna daricat .

Ironisnya, Anda dapat mengganti catproses di sini dengan sesuatu yang benar-benar berguna: alat yang sistem POSIX miliki untuk menambahkan baris baru yang hilang, dan menjadikan file tersebut menjadi file teks POSIX yang tepat.

sed -e '$ a \' "/ tmp / urlFile" | saat membaca -r url
melakukan
    printf "% s \ n" "$ {url}"
selesai

Bacaan lebih lanjut

JdeBP
sumber
1
Perilaku sed tidak ditentukan oleh POSIX ketika input tidak berakhir pada karakter baris baru; juga ketika ada garis yang lebih besar dari LINE_MAX, sementara perilaku readditentukan dalam kasus tersebut.
Stéphane Chazelas