Alcune persone hanno quell'idea errata che readè il comando di leggere una riga. Non è.
readlegge le parole da una riga (possibilmente con barra rovesciata), in cui le parole sono $IFSdelimitate e la barra rovesciata può essere utilizzata per sfuggire ai delimitatori (o per le linee continue).
La sintassi generica è:
read word1 word2... remaining_words
readlegge stdin un byte alla volta finché non trova un carattere di nuova riga escape (o fine input), spaccature che secondo regole complesse e memorizza il risultato di tale frazionamento in $word1, $word2... $remaining_words.
Ad esempio su un input come:
<tab> foo bar\ baz bl\ah blah\
whatever whatever
e con il valore predefinito di $IFS, read a b cassegnerebbe:
$a ⇐ foo
$b ⇐ bar baz
$c ⇐ blah blahwhatever whatever
Ora se viene passato solo un argomento, questo non diventa read line. È ancora read remaining_words. L'elaborazione della barra rovesciata viene ancora eseguita, i caratteri degli spazi bianchi IFS vengono comunque rimossi dall'inizio e dalla fine.
L' -ropzione rimuove l'elaborazione della barra rovesciata. Quindi lo stesso comando sopra con -rinvece avrebbe assegnato
$a ⇐ foo
$b ⇐ bar\
$c ⇐ baz bl\ah 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 (ovvero spazio e tabulazione (e newline, anche se qui non importa a meno che non si usi -d), che si verificano anche essere nel valore predefinito di $IFS) e gli altri. Il trattamento per queste due classi di personaggi è diverso.
Con IFS=:( :essendo non un IFS spazi di carattere), un ingresso come :foo::bar::sarebbe diviso in "", "foo", "", bare ""(e un extra ""con alcune implementazioni anche se questo non importa tranne read -a). Mentre se lo sostituiamo :con lo spazio, la divisione viene eseguita in solo fooe bar. Ciò significa che quelli iniziali e finali vengono ignorati e le loro sequenze vengono trattate come una sola. Ci sono regole aggiuntive quando si combinano caratteri bianchi e non bianchi $IFS. Alcune implementazioni possono aggiungere / rimuovere il trattamento speciale raddoppiando i caratteri in IFS ( IFS=::o IFS=' ').
Quindi, qui, se non vogliamo che vengano eliminati i caratteri di spazi bianchi senza escape iniziali e finali, dobbiamo rimuovere quei caratteri di spazi bianchi IFS da IFS.
Anche con caratteri IFS non di 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 wordsu un input come foo:) con shell POSIX (non zshné alcune pdkshversioni), quell'input è considerato come una sola fooparola perché in quelle shell, i caratteri $IFSsono considerati come terminatori , quindi wordconterranno foo, non foo:.
Quindi, il modo canonico di leggere una riga di input con l' readintegrato è:
IFS= read -r line
(nota che per la maggior parte delle readimplementazioni, che funziona solo per le righe di testo in quanto il carattere NUL non è supportato tranne in zsh).
L'uso della var=value cmdsintassi assicura che IFSsia impostato diversamente solo per la durata di quel cmdcomando.
Nota storica
Il readbuiltin fu introdotto dalla shell Bourne e doveva già leggere le parole , non le righe. Ci sono alcune differenze importanti con le moderne shell POSIX.
La shell Bourne readnon supportava -run'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 di simile sed 's/\\/&&/g'lì.
La shell Bourne non aveva quella nozione di due classi di personaggi (che di nuovo fu introdotta da ksh). Nella Bourne shell tutti i caratteri subiscono lo stesso trattamento IFS spazi caratteri fare a ksh, che è IFS=: read a b csu un ingresso come foo::barassegnerebbe bara $b, non la stringa vuota.
Nella shell Bourne, con:
var=value cmd
Se cmdè un built-in (come readè), varrimane impostato su valuedopo che cmdè terminato. Questo è particolarmente critico $IFSperché nella shell Bourne $IFSviene utilizzato per dividere tutto, non solo le espansioni. Inoltre, se si rimuove il carattere spazio dalla $IFSshell Bourne, "$@"non funziona più.
Nella shell Bourne, il reindirizzamento di un comando composto provoca l'esecuzione in una subshell (nelle prime versioni, anche cose come read var < fileo exec 3< file; read var <&3non funzionavano), quindi era raro nella shell Bourne utilizzare readper qualsiasi cosa tranne l'input dell'utente sul terminale (dove aveva senso la gestione della continuazione di linea)
Alcuni Unices (come HP / UX, ce n'è anche uno in util-linux) hanno ancora un linecomando per leggere una riga di input (che era un comando UNIX standard fino alla specifica Single UNIX versione 2 ).
Questo è fondamentalmente lo stesso head -n 1tranne 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 leggere il suo output attraverso una pipe, quindi molto meno efficiente di quello di Ksh IFS= read -r line, ma ancora molto più intuitivo.