read
retorna um status de saída diferente de zero se não encontrar um delimitador, o que é sempre o caso quando o delimitador é uma string vazia.
Eu tenho usado o padrão abaixo para imprimir mensagens de várias linhas no terminal em um script bash.
read -d '' message <<- EOF
this is a
mulitline
message
EOF
echo "$message"
Isso tem funcionado - até poucos dias atrás, o padrão simplesmente parou de funcionar. Ao parar de funcionar, quero dizer quando bash encontrou essas expressões heredoc no script - parece não fazer nada - sem saída.
A única coisa que posso pensar sobre isso mudou nos últimos dias é que o ambiente em que os scripts são executados é um Ubuntu live USB 14.04, versus instalações "completas".
Então eu descobri que quando eu movia o heredoc antes da declaração set -o errexit
dos scripts ele começou a funcionar novamente. ou seja, isso não funciona
#!/bin/bash
set -o errexit
read -d '' message <<- EOF
this is a
mulitline
message
EOF
echo "$message"
resultado: (nada)
Mas isso funciona
#!/bin/bash
read -d '' message <<- EOF
this is a
mulitline
message
EOF
echo "$message"
resultado
$ sudo ./script.sh
this is a
mulitline
message
GNU bash, version 4.3.11(1)-release (i686-pc-linux-gnu)
O código de saída do comando de leitura é 1 quando o marcador de fim de arquivo (EOF) é atingido. Isso sempre ocorrerá quando o delimitador -d
for nulo ''
neste caso especial em que o fluxo de origem é um heredoc que não pôde conter um \ 0.
$ read -d '' message <<-_ThisMessageEnds_
> this is a
> multi line
> message
> _ThisMessageEnds_
$ exitval=$?
$ echo "The exit val was $exitval"
The exit val was 1.
Se o valor de saída for um erro (não 0), a saída do script pode ser evitada com uma construção AND / OR:
read -d '' message <<-_ThisMessageEnds_ || echo "$message"
this is a
multi line
message
_ThisMessageEnds_
Isso enviará a mensagem para o console e evitará que saia com errexit
.
Mas, como estamos nesse caminho para reduzir, por que não usar isso diretamente:
cat <<-_ThisMessageEnds_
this is a
mulitline
message
_ThisMessageEnds_
Nenhum comando de leitura executado (mais velocidade), nenhuma variável necessária, nenhum erro do código de saída, menos código para manter.
read -d '' message
lê stdin até o primeiro caractere NUL sem escape (como você não adicionou -r
) ou o final da entrada e armazena os dados após $IFS
e processamento de caracteres de barra invertida em $message
(sem o delimitador).
Se nenhum delimitador sem escape for encontrado na entrada, o status de saída read
será diferente de zero. Apenas retorna 0 (sucesso) se um registro completo e terminado for lido.
É mais útil para lidar com registros delimitados por NUL, como a saída de find -print0
(embora você precise de uma sintaxe IFS= read -rd '' record
).
Aqui, você precisa incluir um delimitador NUL em seu documento aqui para que read
retorne com êxito. Isso, no entanto, não é possível com bash
, que tira os caracteres NUL dos documentos here (que é pelo menos melhor que yash
que retira tudo além do primeiro NUL, ou ksh93, que parece entrar em um loop infinito quando um documento aqui contém um NUL).
zsh
é o único shell que pode ter um NUL em seus documentos aqui ou armazená-lo em suas variáveis ou passar caracteres NUL em argumentos para seus builtins / funções. Em zsh
, você pode fazer:
NUL=$'ONE=$''
IFS= read -d "$ONE" -r var << EOF
1
2
3$ONE
EOF
'
IFS= read -d $NUL -r var << EOF
1
2
3$NUL
EOF
( zsh
também entende read -d ''
como um delimitador NUL como bash
. read -d $'
também funciona em bash
'read
, mas que transmite um argumento vazio para read -d ''
, como em bash
as $NUL
não suporta bytes NUL em sua linha de comando).
(observe que há um caractere extra de nova linha depois disso bash
)
Em read -d
, você pode usar um caractere diferente:
var=$(cat <<EOF
message
here
EOF
)
Mas você também pode fazer:
read -d '' message
Isso ainda não permitirá caracteres NUL. No entanto, esse é o código padrão, portanto, você não precisa confiar no ksh93
do zsh / bash específico. Observe também que ele remove todos os caracteres de nova linha à direita e, exceto em cat
quando o %code% builtin está ativado, isso significa gerar um processo e um comando extra.
Quando você usa set -o errexit
e seu script quebra, significa que há algo errado.
Aqui, é read
, que não consegue ler corretamente sua entrada.
Em bash
, quando você usa read -d ''
, o read
builtin usará o caractere nulo
como terminador de linha. Portanto, quando não houver read
message
em sua entrada, 1
lerá toda a entrada na variável read
e retornará um status de saída diferente de zero para indicar que houve um erro:
$ while read -d '' line; do echo "$line"; done < <(printf '1')
imprime nada enquanto:
$ while read -d '' line; do echo "$line"; done < <(printf '1$ while read -d '' line; do echo "$line"; done < <(printf '1')
')
1
fornece read
.
while
também retornará status diferente de zero quando atingir EOF, mas é usado para indicar que não há mais entrada para ler quando você usa while
com um loop %code% , portanto, o loop %code% pode ser terminado. Não é relevante para o seu problema.
Tags bash