Q1. Divisione del campo.
La divisione dei campi è uguale alla divisione delle parole?
Sì, entrambi indicano la stessa idea.
L'impostazione è IFS=''
uguale a null, uguale anche a una stringa vuota?
Sì, tutti e tre significano lo stesso: non deve essere eseguita alcuna divisione di campi / parole. Inoltre, ciò influisce sui campi di stampa (come nel caso di echo "$*"
) tutti i campi verranno concatenati insieme senza spazio.
Q3: (parte a) Disinserire IFS.
Nella specifica POSIX, ho letto quanto segue :
Se IFS non è impostato, la shell si comporterà come se il valore di IFS fosse <space><tab> <newline> .
Che è esattamente equivalente a:
Con un unset IFS
, la shell si comporterà come se IFS fosse di default.
Ciò significa che la "suddivisione del campo" sarà esattamente la stessa con un valore IFS predefinito o non impostata.
Ciò NON significa che IFS funzionerà allo stesso modo in tutte le condizioni. Essendo più specifico, l'esecuzione OldIFS=$IFS
imposterà var OldIFS
su null , non sul valore predefinito. E provare a ripristinare IFS, in questo modo, IFS=OldIFS
renderà IFS su null, non lo manterrà come prima. Attento !!.
Q3: (parte b) Ripristina IFS.
Come posso ripristinare il valore di IFS sul valore predefinito. Supponiamo di voler ripristinare il valore predefinito di IFS. Come lo faccio? (più specificamente, come faccio a fare riferimento a <tab> e <newline> ?)
Per zsh, ksh e bash (AFAIK), IFS potrebbe essere impostato sul valore predefinito come:
IFS=$' \t\n' # works with zsh, ksh, bash.
Fatto, non devi leggere nient'altro.
Ma se è necessario reimpostare IFS per sh, potrebbe diventare complesso.
Diamo un'occhiata dal più semplice da completare senza inconvenienti (tranne la complessità).
1.- Disinserire IFS.
Potremmo solo unset IFS
(Leggi Q3 parte a, sopra.).
2.- Scambia caratteri.
Per ovviare a questo problema, scambiare il valore di tab e newline rende più semplice impostare il valore di IFS e quindi funziona in modo equivalente.
Impostare IFS su <spazio><newline> <tab> :
sh -c 'IFS=$(echo " \n\t"); printf "%s" "$IFS"|xxd' # Works.
3.- Un semplice? soluzione:
Se ci sono script figlio che richiedono che IFS sia impostato correttamente, puoi sempre scrivere manualmente:
IFS ='
'
Dove era la sequenza digitata manualmente:, IFS=
'spacetabnewline'sequenza che è stata effettivamente digitata correttamente sopra (Se è necessario confermare, modificare questa risposta). Ma una copia / incolla dal tuo browser si interromperà perché il browser comprime / nasconde lo spazio bianco. Rende difficile condividere il codice come scritto sopra.
4.- Soluzione completa.
Scrivere un codice che può essere copiato in modo sicuro di solito comporta inequivocabili escape stampabili.
Abbiamo bisogno di un codice che "produca" il valore atteso. Ma, anche se concettualmente corretto, questo codice NON imposta un trailing \n
:
sh -c 'IFS=$(echo " \t\n"); printf "%s" "$IFS"|xxd' # wrong.
Ciò accade perché, sotto la maggior parte delle shell, tutte le nuove righe finali $(...)
o le `...`
sostituzioni di comandi vengono rimosse durante l'espansione.
Dobbiamo usare un trucco per sh:
sh -c 'IFS="$(printf " \t\nx")"; IFS="${IFS%x}"; printf "$IFS"|xxd' # Correct.
Un modo alternativo potrebbe essere quello di impostare IFS come valore di ambiente da bash (ad esempio) e quindi chiamare sh (le versioni di esso che accettano IFS da impostare tramite l'ambiente), in quanto:
env IFS=$' \t\n' sh -c 'printf "%s" "$IFS"|xxd'
In breve, sh rende il ripristino di IFS predefinito un'avventura piuttosto strana.
Q4: nel codice attuale:
Infine, come sarebbe questo codice:
while IFS= read -r line
do
echo $line
done < /path_to_text_file
comportarsi se cambiamo la prima riga in
while read -r line # Use the default IFS value
o a:
while IFS=' ' read -r line
Primo: non so se il echo $line
(con il var NON citato) sia presente su Porpouse o no. Introduce un secondo livello di "divisione del campo" che la lettura non ha. Quindi risponderò ad entrambi. :)
Con questo codice (in modo da poter confermare). Avrai bisogno dell'utile xxd :
#!/bin/ksh
# Correctly set IFS as described above.
defIFS="$(printf " \t\nx")"; defIFS="${defIFS%x}";
IFS="$defIFS"
printf "IFS value: "
printf "%s" "$IFS"| xxd -p
a=' bar baz quz '; l="${#a}"
printf "var value : %${l}s-" "$a" ; printf "%s\n" "$a" | xxd -p
printf "%s\n" "$a" | while IFS='x' read -r line; do
printf "IFS --x-- : %${l}s-" "$line" ;
printf "%s" "$line" |xxd -p; done;
printf 'Values quoted :\n' "" # With values quoted:
printf "%s\n" "$a" | while IFS='' read -r line; do
printf "IFS null quoted : %${l}s-" "$line" ;
printf "%s" "$line" |xxd -p; done;
printf "%s\n" "$a" | while IFS="$defIFS" read -r line; do
printf "IFS default quoted : %${l}s-" "$line" ;
printf "%s" "$line" |xxd -p; done;
unset IFS; printf "%s\n" "$a" | while read -r line; do
printf "IFS unset quoted : %${l}s-" "$line" ;
printf "%s" "$line" |xxd -p; done;
IFS="$defIFS" # set IFS back to default.
printf "%s\n" "$a" | while IFS=' ' read -r line; do
printf "IFS space quoted : %${l}s-" "$line" ;
printf "%s" "$line" |xxd -p; done;
printf '%s\n' "Values unquoted :" # Now with values unquoted:
printf "%s\n" "$a" | while IFS='x' read -r line; do
printf "IFS --x-- unquoted : "
printf "%s, " $line; printf "%s," $line |xxd -p; done
printf "%s\n" "$a" | while IFS='' read -r line; do
printf "IFS null unquoted : ";
printf "%s, " $line; printf "%s," $line |xxd -p; done
printf "%s\n" "$a" | while IFS="$defIFS" read -r line; do
printf "IFS defau unquoted : ";
printf "%s, " $line; printf "%s," $line |xxd -p; done
unset IFS; printf "%s\n" "$a" | while read -r line; do
printf "IFS unset unquoted : ";
printf "%s, " $line; printf "%s," $line |xxd -p; done
IFS="$defIFS" # set IFS back to default.
printf "%s\n" "$a" | while IFS=' ' read -r line; do
printf "IFS space unquoted : ";
printf "%s, " $line; printf "%s," $line |xxd -p; done
Ottengo:
$ ./stackexchange-Understanding-IFS.sh
IFS value: 20090a
var value : bar baz quz -20202062617220202062617a20202071757a2020200a
IFS --x-- : bar baz quz -20202062617220202062617a20202071757a202020
Values quoted :
IFS null quoted : bar baz quz -20202062617220202062617a20202071757a202020
IFS default quoted : bar baz quz-62617220202062617a20202071757a
IFS unset quoted : bar baz quz-62617220202062617a20202071757a
IFS space quoted : bar baz quz-62617220202062617a20202071757a
Values unquoted :
IFS --x-- unquoted : bar, baz, quz, 6261722c62617a2c71757a2c
IFS null unquoted : bar, baz, quz, 6261722c62617a2c71757a2c
IFS defau unquoted : bar, baz, quz, 6261722c62617a2c71757a2c
IFS unset unquoted : bar, baz, quz, 6261722c62617a2c71757a2c
IFS space unquoted : bar, baz, quz, 6261722c62617a2c71757a2c
Il primo valore è solo il valore corretto di IFS=
'spacetabnewline'
La riga successiva è rappresentata da tutti i valori esadecimali di var $a
e una nuova riga '0a' alla fine in quanto verrà data a ciascun comando di lettura.
La riga successiva, per la quale IFS è nulla, non esegue alcuna "divisione del campo", ma la nuova riga viene rimossa (come previsto).
Le tre righe successive, poiché IFS contiene uno spazio, rimuovono gli spazi iniziali e impostano la var line sul saldo rimanente.
Le ultime quattro righe mostrano cosa farà una variabile non quotata. I valori verranno suddivisi sui (diversi) spazi e verranno stampati come:bar,baz,qux,
IFS
e uno non impostatoIFS
sono molto diversi. La risposta a Q4 è in parte errata: i separatori interni non vengono toccati qui, ma solo quelli iniziali e finali.