read -a array -d '\ n' foo, código de saída 1

8

Se eu tentar executar

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

o código de saída é 1 - mesmo que ele realize o que eu quero; coloque cada linha de bar em um elemento da matriz fooArr (usando bash 4.2.37).

Alguém pode explicar por que isso está acontecendo

Eu encontrei outras maneiras de resolver isso, como as que estão abaixo, então não é isso que estou pedindo.

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

ou

mapfile -t fooArr < bar
    
por RasmusWriedtLarsen 19.06.2013 / 22:25

2 respostas

13

O que precisa ser explicado é que o comando pareceu funcionar, não o seu código de saída

'\n' é dois caracteres: uma barra invertida \ e uma letra n . O que você achou que precisava era $'\n' , que é um avanço de linha (mas isso também não seria correto, veja abaixo).

A opção -d faz isso:

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

Portanto, sem essa opção, read leria uma nova linha, dividiria a linha em palavras usando os caracteres em $IFS como separadores e colocaria as palavras na matriz. Se você especificou -d $'\n' , definindo o delimitador de linha para uma nova linha, ele faria exatamente a mesma coisa . A configuração -d '\n' significa que ele será lido até a primeira barra invertida (mas, mais uma vez, veja abaixo), que é o primeiro caractere em delim . Como não há barra invertida em seu arquivo, o read terminará no final do arquivo 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.

Então é por isso que o código de saída é 1.

Do fato de você acreditar que o comando funcionou, podemos concluir que não há espaços no arquivo, de modo que read , depois de ler o arquivo inteiro na esperança inútil de encontrar uma barra invertida, o dividirá por espaço em branco (o valor padrão de $IFS ), incluindo novas linhas. Assim, cada linha (ou cada palavra, se uma linha contiver mais de uma palavra) é colocada no array.

O caso misterioso da barra invertida

Agora, como eu sabia que o arquivo não continha barras invertidas? Porque você não forneceu o -r flag para read :

  -r                do not allow backslashes to escape any characters

Então, se você tivesse alguma barra invertida no arquivo, eles seriam removidos, a menos que você tivesse dois deles seguidos. E, claro, há a evidência de que read tinha um código de saída igual a 1, o que demonstra que ele não encontrou uma barra invertida, portanto não havia duas delas seguidas.

Takeaways

Bash não seria nada se não houvesse pegadinhas se escondendo atrás de quase todos os comandos, e read não é exceção. Aqui está um casal:

  1. A menos que você especifique -r , read interpretará seqüências de escape de barra invertida. A menos que seja realmente o que você quer (o que ocasionalmente é, mas apenas ocasionalmente), você deve se lembrar de especificar -r para evitar que os caracteres desapareçam no caso raro de haver barras invertidas na entrada.

  2. O fato de que read retorna um código de saída de 1 não significa que ele falhou. Pode ter sido bem sucedido, exceto por encontrar o terminador de linha. Portanto, tenha cuidado com um loop como este: while read -r LINE; do something with LINE; done porque falhará em do something com a última linha no caso raro de a última linha não ter uma nova linha no final.

  3. read -r LINE preserva barras invertidas, mas não preserva espaços em branco iniciais ou finais.

por 19.06.2013 / 23:34
2

Esse é o comportamento esperado:

The return code is zero, unless end-of-file is encountered, [...]

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

start cmd:> echo -n a b c | { read -a testarray; echo $?; }
1
    
por 19.06.2013 / 23:33