leggi -a array -d '\ n' <pippo, codice di uscita 1


8

Se provo ad eseguire

read -a fooArr -d '\n' < bar

il codice di uscita è 1 - anche se realizza ciò che voglio; metti ogni riga di barin un elemento dell'array fooArr(usando bash 4.2.37).

Qualcuno può spiegare perché questo sta accadendo


Ho trovato altri modi per risolverlo, come quelli qui sotto, quindi non è quello che sto chiedendo.

for ((i=1;; i++)); do
    read "fooArr$i" || break;
done < bar

o

mapfile -t fooArr < bar

Risposte:


14

Ciò che deve essere spiegato è che il comando sembrava funzionare, non il suo codice di uscita

'\n'è composto da due personaggi: una barra rovesciata \e una lettera n. Quello che pensavi di aver bisogno era $'\n', che è un avanzamento di riga (ma non sarebbe neanche giusto, vedi sotto).

L' -dopzione fa questo:

  -d delim  continue until the first character of DELIM is read, rather
            than newline

Quindi, senza quell'opzione, readleggerebbe una nuova riga, dividere la riga in parole usando i caratteri $IFScome separatori e metterebbe le parole nella matrice. Se specificato -d $'\n', impostando il delimitatore di linea su una nuova riga, farebbe esattamente la stessa cosa . L'impostazione -d '\n'significa che leggerà fino alla prima barra rovesciata (ma, ancora una volta, vedi sotto), che è il primo carattere in delim. Poiché non è presente una barra rovesciata nel file, readverrà terminato alla fine del file e:

Exit Status:
The return code is zero, unless end-of-file is encountered, read times out,
or an invalid file descriptor is supplied as the argument to -u.

Ecco perché il codice di uscita è 1.

Dal fatto che ritieni che il comando abbia funzionato, possiamo concludere che non ci sono spazi nel file, in modo che read, dopo aver letto l'intero file nella futile speranza di trovare una barra rovesciata, lo divida per spazi bianchi (il valore predefinito di $IFS), comprese le newline. Quindi ogni riga (o ogni parola, se una riga contiene più di una parola) viene inserita nell'array.

Il misterioso caso della contraccolpo violenta

Ora, come sapevo che il file non conteneva barre rovesciate? Perché non hai fornito la -rbandiera a read:

  -r                do not allow backslashes to escape any characters

Quindi se avessi delle barre rovesciate nel file, sarebbero state rimosse, a meno che tu non ne avessi due di fila. E, naturalmente, ci sono prove che readavevano un codice di uscita 1, il che dimostra che non ha trovato una barra rovesciata, quindi non ce n'erano neanche due di fila.

Asporto

Bash non farebbe schifo se non ci fossero nascondigli dietro quasi tutti i comandi, e readnon fa eccezione. Eccone un paio:

  1. Se non specificato -r, readinterpreterà le sequenze di escape con barra rovesciata. A meno che non sia effettivamente quello che vuoi (che è occasionalmente, ma solo occasionalmente), dovresti ricordare di specificare -rdi evitare la scomparsa dei caratteri nel raro caso in cui ci siano barre rovesciate nell'input.

  2. Il fatto che readrestituisca un codice di uscita pari a 1 non significa che sia fallito. Potrebbe esserci riuscito, tranne per trovare il terminatore di linea. Quindi fai attenzione con un ciclo come questo: while read -r LINE; do something with LINE; done perché non riuscirà do somethingcon l'ultima riga nel raro caso in cui l'ultima riga non ha una riga alla fine.

  3. read -r LINE conserva le barre rovesciate, ma non conserva gli spazi bianchi iniziali o finali.


Grazie! Pensavo di aver provato l'approccio $ '\ n', ma credo di no: / bello sapere del -r però
RasmusWL

2
"il raro caso in cui l'ultima riga non avesse una nuova riga" non mi sembra un motivo convincente per consigliare "mai usare while read". Altrimenti, risposta fantastica.
Glenn Jackman,

@glennjackman: puoi usarlo while readfintanto che non hai nulla all'interno del loop. Altrimenti, è un insetto in attesa di morderti - e credimi, sono stato morso.
rici,

2
L'ultima riga ha sempre una nuova riga. Ecco come viene definita una linea: termina con una nuova riga. Se un file non vuoto non termina con una nuova riga, non è un file di testo e non è possibile utilizzare strumenti di elaborazione del testo come l'utilità di shell read.
Gilles 'SO- smetti di essere malvagio' il

2
@glennjackman: Io, io iterando attraverso i file in colonne con awk, principalmente. Per iterare intere righe in cui il file non è troppo grande, mapfileè piuttosto interessante. Per un trucco rapido e sporco, userò il ciclo while proibito, ma ho smesso di inserirlo negli script di produzione. YMMV e io abbiamo ammorbidito l'avvertimento nella risposta.
rici,

4

Questo è il comportamento previsto:

Il codice di ritorno è zero, a meno che non venga rilevata la fine del file, [...]

start cmd:> echo a b c | { read -a testarray; echo $?; }
0

start cmd:> echo -n a b c | { read -a testarray; echo $?; }
1
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.