it-swarm.it

Come eseguire il ciclo sopra le righe di un file?

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

Vorrei che for ripetesse su ogni riga ignorando individualmente gli spazi bianchi, ovvero le ultime due righe dovrebbero essere sostituite da

tester: hello world

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

63
Tobias Kienzler

Con for e 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é newline essendo un carattere di spazio bianco IFS, le sequenze contano come 1 e quelle iniziali e finali vengono ignorate. Con zsh e ksh93 (non bash), puoi cambiarlo in IFS=$'\n\n' per newline da non trattare in modo speciale, tuttavia si noti che tutti i caratteri trailing newline (in modo da includere righe vuote finali) verranno sempre rimossi dalla sostituzione del comando.

Oppure con read (non 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.

76
wag

Per quello che vale, devo farlo abbastanza spesso e non riesco mai a ricordare il modo esatto di usare 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 sed per estrarre riga dopo riga e passa ciascuna riga come argomento di una singola 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
1
Jonathan H