GNU/Linux >> Linux Esercitazione >  >> Linux

Capire "ifs=Leggi -r Line"?

Ovviamente capisco che si può aggiungere valore alla variabile del separatore di campo interno. Ad esempio:

$ IFS=blah
$ echo "$IFS"
blah
$ 

Capisco anche che read -r line salverà i dati da stdin alla variabile denominata line :

$ read -r line <<< blah
$ echo "$line"
blah
$ 

Tuttavia, come può un comando assegnare un valore variabile? E prima memorizza i dati da stdin alla variabile line e poi dai il valore di line a IFS ?

Risposta accettata:

Nelle shell POSIX, read , senza alcuna opzione non legge una riga , legge parole da una riga (possibilmente barra rovesciata), dove le parole sono $IFS delimitato e barra rovesciata possono essere utilizzati per evitare i delimitatori (o continuare le righe).

La sintassi generica è:

read word1 word2... remaining_words

read legge lo stdin un byte alla volta¹ finché non trova un carattere di nuova riga senza escape (o fine input), lo divide in base a regole complesse e memorizza il risultato di tale divisione in $word1 , $word2$remaining_words .

Ad esempio su un input come:

  <tab> foo bar baz   blah   blah
whatever whatever

e con il valore predefinito di $IFS , read a b c assegnerebbe:

  • $afoo
  • $bbar baz
  • $cblah blahwhatever whatever

Ora, se viene passato solo un argomento, quello non diventa read line . È ancora read remaining_words . L'elaborazione della barra rovesciata è ancora eseguita, gli spazi bianchi IFS² vengono ancora rimossi dall'inizio e dalla fine.

Il -r l'opzione rimuove l'elaborazione della barra rovesciata. Quindi lo stesso comando sopra con -r assegnerebbe invece

  • $afoo
  • $bbar
  • $cbaz blah blah

Ora, per la parte di divisione, è importante rendersi conto che ci sono due classi di caratteri per $IFS :i caratteri degli spazi bianchi IFS² (inclusi spazio e tabulazione (e newline, anche se qui non importa se non usi -d), che si trovano anche nel valore predefinito di $IFS ) e gli altri. Il trattamento per queste due classi di caratteri è diverso.

Con IFS=: (: non essendo un carattere di spazio vuoto IFS), un input come :foo::bar:: verrebbe suddiviso in "" , "foo" , "" , bar e "" (e un ulteriore "" con alcune implementazioni, tuttavia, non importa tranne che per read -a ). Mentre se sostituiamo quel : con lo spazio, la divisione viene eseguita solo in foo e bar . Cioè quelli iniziali e finali vengono ignorati e le loro sequenze vengono trattate come tali. Esistono regole aggiuntive quando gli spazi bianchi e i caratteri non vuoti vengono combinati in $IFS . Alcune implementazioni possono aggiungere/rimuovere il trattamento speciale raddoppiando i caratteri in IFS (IFS=:: o IFS=' ' ).

Quindi qui, se non vogliamo che gli spazi vuoti iniziali e finali senza caratteri di escape vengano rimossi, dobbiamo rimuovere quei caratteri di spazio vuoto IFS da IFS.

Anche con caratteri IFS diversi da spazi bianchi, se la riga di input contiene uno (e solo uno) di quei caratteri ed è l'ultimo carattere della riga (come IFS=: read -r word su un input come foo: ) con shell POSIX (non zsh né alcuni pdksh versioni), quell'input è considerato come un foo word perché in quelle shell, i caratteri $IFS sono considerati terminatori , quindi word conterrà foo , non foo: .

Quindi, il modo canonico di leggere una riga di input con read integrato è:

IFS= read -r line

(nota che per la maggior parte dei read implementazioni, che funziona solo per le righe di testo poiché il carattere NUL non è supportato tranne che in zsh ).

Correlati:Linux:condividere file tra host Linux e guest Windows?

Usando var=value cmd la sintassi assicura che IFS è impostato in modo diverso solo per la durata di quel cmd comando.

Nota sulla cronologia

Il read builtin è stato introdotto dalla shell Bourne ed era già per leggere parole , non linee. Ci sono alcune differenze importanti con le moderne shell POSIX.

La read della shell Bourne non supportava un -r opzione (che è stata introdotta dalla shell Korn), quindi non c'è modo di disabilitare l'elaborazione della barra rovesciata se non la pre-elaborazione dell'input con qualcosa come sed 's/\/&&/g' lì.

La shell Bourne non aveva quella nozione di due classi di caratteri (che di nuovo è stata introdotta da ksh). Nella shell Bourne tutti i caratteri subiscono lo stesso trattamento dei caratteri degli spazi bianchi IFS in ksh, ovvero IFS=: read a b c su un input come foo::bar assegnerebbe bar a $b , non la stringa vuota.

Nella shell Bourne, con:

var=value cmd

Se cmd è un built-in (come read è), var rimane impostato su value dopo cmd ha finito. Questo è particolarmente critico con $IFS perché nella shell Bourne, $IFS serve per dividere tutto, non solo le espansioni. Inoltre, se rimuovi lo spazio da $IFS nella shell Bourne, "[email protected]" non funziona più.

Nella shell Bourne, il reindirizzamento di un comando composto ne provoca l'esecuzione in una sottoshell (nelle prime versioni, anche cose come read var < file o exec 3< file; read var <&3 non funzionava), quindi era raro nella shell Bourne usare read per qualsiasi cosa tranne l'input dell'utente sul terminale (dove aveva senso la gestione della continuazione della riga)

Alcuni Unice (come HP/UX, ce n'è uno anche in util-linux ) hanno ancora una line comando per leggere una riga di input (che era un comando UNIX standard fino alla specifica UNIX singola versione 2).

È praticamente lo stesso di head -n 1 tranne per il fatto che legge un byte alla volta per assicurarsi che non legga più di una riga. Su quei sistemi, puoi fare:

line=`line`

Ovviamente, ciò significa generare un nuovo processo, eseguire un comando e leggerne l'output attraverso una pipe, quindi molto meno efficiente della IFS= read -r line di ksh , ma ancora molto più intuitivo.


Linux
  1. Capire se?

  2. Casella occupata Leggi file riga per riga?

  3. Leggi riga per riga nello script bash

  4. Come leggere la penultima riga in un file usando Bash?

  5. Shell Script, letto sulla stessa riga dopo aver ripetuto un messaggio

Comprendere i permessi dei file Linux

Bash ha letto Comando

Come leggere un file riga per riga in Bash

Comando Diff in Linux

Comprensione dei processi su Linux

Come leggere gli argomenti della riga di comando negli script della shell?