2015-04-17 11 views
18

ho lima a.txt con i seguenti contenutiBash leggere la linea non legge gli spazi iniziali

aaa 
    bbb 

Quando eseguo seguente script:

while read line 
do 
    echo $line 
done <a.txt> b.txt 

generato b.txt contiene i seguenti

aaa 
bbb 

E ' si vede che gli spazi iniziali delle linee sono stati rimossi. Come posso preservare gli spazi principali?

risposta

27

Questo è trattato nella voce Bash FAQ sul reading data line-by-line.

Il comando di lettura modifica ogni riga letta; per impostazione predefinita rimuove tutti i caratteri di spaziatura iniziale e finale (spazi e tabulazioni o eventuali caratteri di spaziatura presenti in IFS). Se questo non è desiderato, la variabile IFS deve essere eliminato:

# Exact lines, no trimming 
while IFS= read -r line; do 
    printf '%s\n' "$line" 
done < "$file" 

Come Charles Duffy fa giustamente notare (e io avevo perso, concentrandosi sul problema IFS); se vuoi vedere gli spazi nel tuo output devi anche citare la variabile quando la usi o la shell, ancora una volta, cancella gli spazi bianchi.

Note su alcune delle altre differenze in questo frammento tra virgolette rispetto al codice originale.

L'utilizzo dell'argomento -r su read è coperto in una singola frase nella parte superiore della pagina precedentemente collegata.

L'opzione -r da leggere impedisce l'interpretazione del backslash (solitamente utilizzata come coppia di backline backslash, per continuare su più righe). Senza questa opzione, le barre retroverse nell'input verranno scartate. Dovresti quasi sempre usare l'opzione -r con read.

Per quanto riguarda utilizzando printf invece di echo là del comportamento di echo è, in qualche modo, purtroppo, non è portabile coerenti in tutti gli ambienti e le differenze possono essere difficile da affrontare. printf d'altra parte è coerente e può essere utilizzato interamente in modo robusto.

+5

Se non si fornisce 'read' alcun argomento da usare per contenere l'input (basandosi sulla variabile di default' REPLY'), non viene eliminato lo spazio vuoto e si può omettere la modifica a 'IFS'. Cioè, 'mentre leggi -r; do printf '% s \ n' "$ RISPONDI"; done <"$ file" ' – chepner

+1

@chepner Interessante. Mi chiedo perché sia ​​così. –

+2

Non ne sono sicuro; non sembra essere documentato per quanto posso dire. Ha un certo senso se ci si pensa come argomenti zero richiedono dividere la linea in campi zero, il che significa che non c'è alcun uso di 'IFS'. (Questo presuppone che tu accetti che spaccare una linea in un campo sia ancora una divisione, anche se degenerata.) In ogni caso, è un 'bash'ism; POSIX 'read' richiede almeno un argomento. – chepner

9

Ci sono diversi problemi qui:

  • A meno IFS viene cancellata, read strisce spazi iniziali e finali.
  • echo $line string-splits e glob-espande il contenuto di $line, suddividendolo in singole parole e passando quelle parole come singoli argomenti al comando echo. Pertanto, anche con IFS cancellato al tempo read, lo echo $line eliminerebbe ancora gli spazi bianchi iniziali e finali e cambierà le corse di spazi bianchi tra le parole in un singolo carattere di spazio ciascuna. Inoltre, una riga contenente solo il carattere * verrà espansa per contenere un elenco di nomi di file.
  • echo "$line" è un miglioramento significativo, ma ancora non gestirà correttamente valori come -n, che considera come argomento eco stesso. printf '%s\n' "$line" risolverebbe completamente questo problema.
  • read senza -r considera le barre rovesciate come caratteri di continuazione anziché contenuto letterale, in modo che non vengano inclusi nei valori prodotti a meno che non siano raddoppiati per sfuggire a se stessi.

Così:

while IFS= read -r line; do 
    printf '%s\n' "$line" 
done 
+0

Un buon consiglio, ma la sequenza di due caratteri '\ n' non _non_ risulta in una _newline_, risulta in _literal' n'_. Al contrario, un '\' -escaped _actual_ newline causa 'read' di leggere anche la riga _following_ e di aggiungerla direttamente a quella corrente (scartando' \ 'e la nuova riga). Un '' prima che qualsiasi altro personaggio venga semplicemente scartato. – mklement0

+2

Un altro modo di descrivere il comportamento di 'read' senza' -r': l'input viene analizzato nello stesso modo in cui una parola bareword con caratteri '\ -escaped individualmente viene analizzata dalla shell (POSIX) stessa (ad esempio, come parte di un elenco di argomenti), come descritto su http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_02_01 ed essenzialmente duplicato nella specifica POSIX di 'read' su http: //pubs.opengroup. org/onlinepubs/9699919799/utilities/read.html. – mklement0

+2

Grazie - Vado a rivedere il materiale sorgente per determinare il modo migliore per rivedere quella parte della mia risposta. –

Problemi correlati