Jak zapętlić linie pliku?


61

Powiedz, że mam ten plik:

hello
world
hello world

Ten program

#!/bin/bash

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

wyjścia

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

Chciałbym, aby foriteracja nad każdą linią była indywidualnie ignorowana spacje, tzn. Ostatnie dwie linie powinny zostać zastąpione przez

tester: hello world

Użycie cudzysłowów for i in "$(cat $1)";powoduje iprzypisanie całego pliku na raz. Co powinienem zmienić?

Odpowiedzi:


69

Z fori IFS :

#!/bin/bash

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

Zauważ jednak, że pominie puste linie jako znak nowej linii będący znakiem IFS-białych spacji, jego sekwencje liczą się jako 1, a wiodące i końcowe są ignorowane. Za pomocą zshi ksh93(nie bash) możesz zmienić to na, aby znak nowej IFS=$'\n\n'linii nie był traktowany specjalnie, jednak zwróć uwagę, że wszystkie końcowe znaki nowego wiersza (włączając w to końcowe puste linie) zawsze zostaną usunięte przez podstawienie polecenia.

Lub zread (nie więcej cat):

#!/bin/bash

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

Tam zachowane są puste wiersze, ale należy pamiętać, że pominie on ostatni wiersz, jeśli nie zostanie poprawnie rozdzielony znakiem nowej linii.


5
dzięki, nie wiedziałem, że można <zapętlić całą pętlę. Chociaż teraz ma to sens, teraz to widziałem
Tobias Kienzler,

1
Widzę IFS \ read -r line' in second example. Is really IFS = `potrzebne? IMHO wystarczy powiedzieć:while read -r line; do echo "tester: $line"; done < "$1"
Grzegorz Wierzowiecki

4
@GrzegorzWierzowiecki IFS=wyłącza usuwanie wiodących i końcowych białych znaków. Patrz W while IFS= read.., dlaczego IFS mają żadnego wpływu?
Gilles

0

Aby to było warte, muszę to robić dość często i nigdy nie pamiętam dokładnego sposobu użycia while IFS= read..., dlatego w moim profilu bash zdefiniowałem następującą funkcję:

# 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
}

Ta funkcja określa najpierw liczbę wierszy w pliku, a następnie używa seddo wyodrębnienia linii po linii i przekazuje każdą linię jako argument pojedynczego łańcucha do dowolnej funkcji. Podejrzewam, że może to stać się naprawdę nieefektywne w przypadku dużych plików, ale jak dotąd nie stanowiło to dla mnie problemu (oczywiście sugestie, jak poprawić to powitanie).

Zastosowanie jest całkiem słodkie IMO:

>> 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
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.