Bagaimana cara melingkar di atas baris file?

61

Katakanlah saya punya file ini:

hello
world
hello world

Program ini

#!/bin/bash

for i in $(cat $1); do
    echo "tester: $i"
done

output

tester: hello
tester: world
tester: hello
tester: world

Saya ingin memiliki foriterate atas setiap baris secara individual mengabaikan spasi putih, yaitu dua baris terakhir harus diganti oleh

tester: hello world

Menggunakan kutipan for i in "$(cat $1)";menghasilkan iseluruh file sekaligus. Apa yang harus saya ubah?

Tobias Kienzler
sumber

Jawaban:

69

Dengan fordan IFS :

#!/bin/bash

IFS=$'\n'       # make newlines the only separator
set -f          # disable globbing
for i in $(cat < "$1"); do
  echo "tester: $i"
done

Namun perlu dicatat bahwa itu akan melewati baris kosong sebagai baris baru menjadi karakter spasi-IFS putih, urutannya dihitung sebagai 1 dan yang memimpin dan mengikuti diabaikan. Dengan zshdan ksh93(tidak bash), Anda dapat mengubahnya ke IFS=$'\n\n'untuk baris baru tidak diperlakukan khusus, namun perhatikan bahwa semua mengikuti karakter baris baru (sehingga termasuk tertinggal baris kosong) akan selalu dihapus oleh substitusi perintah.

Atau denganread (tidak lebih cat):

#!/bin/bash

while IFS= read -r line; do
  echo "tester: $line"
done < "$1"

Di sana, baris kosong dipertahankan, tetapi perhatikan bahwa itu akan melewati baris terakhir jika tidak dibatasi dengan benar oleh karakter baris baru.

mengibaskan
sumber
5
terima kasih, saya tidak tahu ada yang bisa <ke seluruh lingkaran. Meskipun masuk akal sekarang saya melihatnya
Tobias Kienzler
1
Saya melihat IFS \ read -r line' in second example. Is really IFS = `diperlukan? IMHO cukup untuk mengatakan:while read -r line; do echo "tester: $line"; done < "$1"
Grzegorz Wierzowiecki
4
@GrzegorzWierzowiecki IFS=mematikan pengupasan spasi putih terkemuka dan tertinggal. Lihat Di while IFS= read.., mengapa IFS tidak berpengaruh?
Gilles 'SO- stop being evil'
0

Untuk apa nilainya, saya perlu melakukan itu cukup sering, dan tidak pernah dapat mengingat cara penggunaan yang tepat while IFS= read..., jadi saya mendefinisikan fungsi berikut di profil bash saya:

# iterate the line of a file and call input function
iterlines() {
    (( $# < 2 )) && { echo "Usage: iterlines <File> <Callback>"; return; }
    local File=$1
    local Func=$2
    n=$(cat "$File" | wc -l)
    for (( i=1; i<=n; i++ )); do
        "$Func" "$(sed "${i}q;d" "$File")"
    done
}

Fungsi ini pertama-tama menentukan jumlah baris dalam file, kemudian digunakan seduntuk mengekstrak baris demi baris, dan meneruskan setiap baris sebagai argumen string tunggal ke fungsi yang diberikan. Saya kira ini mungkin menjadi sangat tidak efisien dengan file besar, tapi itu belum menjadi masalah bagi saya sejauh ini (saran tentang bagaimana meningkatkan sambutan ini tentu saja).

Penggunaannya IMO cukup manis:

>> cat example.txt # note the use of spaces, whitespace, etc.
a/path

This is a sentence.
"wi\th quotes"
$End
>> iterlines example.txt echo # preserves quotes, $ and whitespace
a/path

This is a sentence.
"wi\th quotes"
$End
>> x() { echo "$#"; }; iterlines example.txt x # line always passed as single input string
1
1 
1
1
1
Sheljohn
sumber