Come eseguire il ciclo sopra le righe di un file?


61

Di 'che ho questo file:

hello
world
hello world

Questo programma

#!/bin/bash

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

uscite

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

Mi piacerebbe avere l' foriterazione su ogni riga ignorando individualmente gli spazi bianchi, cioè le ultime due righe dovrebbero essere sostituite da

tester: hello world

L'uso delle virgolette for i in "$(cat $1)";comporta il'assegnazione dell'intero file in una sola volta. Cosa dovrei cambiare?

Risposte:


69

Con fore IFS :

#!/bin/bash

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

Si noti tuttavia che salterà le righe vuote poiché la nuova riga è un carattere di spazio bianco IFS, le sequenze contano come 1 e quelle iniziali e finali vengono ignorate. Con zshe ksh93(non bash), è possibile modificarlo in modo che IFS=$'\n\n'newline non venga trattato in modo speciale, tuttavia si noti che tutti i caratteri di nuova riga finali (in modo da includere righe vuote finali) verranno sempre rimossi dalla sostituzione del comando.

O conread (non di più cat):

#!/bin/bash

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

Qui vengono mantenute le righe vuote, ma nota che salterebbe l'ultima riga se non fosse delimitata correttamente da un carattere di nuova riga.


5
grazie, non sapevo che si potesse fare <un ciclo completo. Anche se ora ha perfettamente senso, l'ho visto
Tobias Kienzler,

1
Vedo IFS \ read -r line' in second example. Is really IFS = `necessario? Secondo me basta dire:while read -r line; do echo "tester: $line"; done < "$1"
Grzegorz Wierzowiecki,

4
@GrzegorzWierzowiecki IFS=disattiva lo stripping degli spazi bianchi iniziali e finali. Vedi In while IFS= read.., perché IFS non ha alcun effetto?
Gilles 'SO- smetti di essere malvagio' il

0

Per quello che vale, devo farlo abbastanza spesso e non riesco mai a ricordare il modo esatto di utilizzo while IFS= read..., quindi ho definito la seguente funzione nel mio profilo bash:

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

Questa funzione determina innanzitutto il numero di righe nel file, quindi utilizza sedper estrarre riga dopo riga e passa ciascuna riga come singolo argomento stringa a una determinata funzione. Suppongo che questo potrebbe diventare davvero inefficiente con file di grandi dimensioni, ma finora non è stato un problema per me (suggerimenti su come migliorare questo benvenuto ovviamente).

L'utilizzo è abbastanza dolce 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
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.