it-swarm-id.com

Bagaimana cara mengulangi baris-baris file?

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 for iterate pada setiap baris secara individual mengabaikan spasi putih, yaitu dua baris terakhir harus diganti oleh

tester: hello world

Menggunakan tanda kutip for i in "$(cat $1)"; menghasilkan i ditugaskan seluruh file sekaligus. Apa yang harus saya ubah?

63
Tobias Kienzler

Dengan for dan 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 ruang-putih IFS, urutannya dihitung sebagai 1 dan yang memimpin dan mengikuti adalah diabaikan. Dengan zsh dan ksh93 (bukan bash), Anda dapat mengubahnya ke IFS=$'\n\n' agar baris baru tidak diperlakukan secara khusus, namun perlu diketahui bahwa semua trailing karakter baris baru (sehingga menyertakan trailing baris kosong) akan selalu dihapus oleh perintah pengganti.

Atau dengan read (tidak ada lagi 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.

76
wag

Untuk apa nilainya, saya perlu melakukan itu cukup sering, dan tidak pernah bisa mengingat cara tepatnya menggunakan 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 menggunakan sed untuk 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
1
Jonathan H