Erros de tubulação para minha função fazem com que o comando seja ignorado após 1 falha

Erros de tubulação para minha função fazem com que o comando seja ignorado após 1 falha

Contexto: tenho um script Bash que copia arquivos

function log () {
    read IN
    if [ "$IN" == "" ]; then
        :
    else

        echo "$datetime"$'\t'"$IN" | tee -a logfile
    fi
}

function copy () {
    command cp -L --parents $@
}

...
copy -R /etc . 2>&1 | log
...

Problema: Quando cp -L --parents -R /etc 2>&1é executado manualmente, recebo cerca de 10 falhas (symlinks quebrados, esperado) e todo o /etc foi copiado.

Mas quando o script é executado, apenas 1 falha é relatada e /etc é copiado apenas até o ponto onde ocorreu a 1 falha.

Na tentativa de solucionar o problema, tudo que fiz foi remover 2>&1o script e a cópia ocorreu conforme o esperado.

Pergunta: Minha logfunção está causando o problema ou é algum problema sintático (embora não seja uma quebra de script) na forma como o script é escrito?

Responder1

Sua logfunção é a culpada. O que ele faz é: ler uma linha¹ e, se a linha não estiver vazia, imprimir um carimbo de data/hora e depois o conteúdo da linha. Isso é tudo que faz: depois de processada uma linha, ela retorna.

Ao cpemitir uma primeira mensagem de erro, a logfunção a lê e a processa. Como a logfunção retorna, o processo no lado direito do tubo sai, o que faz com que a extremidade de leitura do tubo feche. Quando cpemite uma segunda mensagem de erro, ele tenta escrever em um pipe fechado, o que faz com que ele morra devido a um erro.Sinal SIGPIPE. O erro padrão é armazenado em buffer de linha (por padrão, e cpnão tenta mudar isso), portanto, o buffer não entra em ação.

Para processar todas as linhas de entrada, você precisa readde um loop.

log () {
    while IFS= read -r IN; do
        echo "$datetime"$'\t'"$IN"
    done | tee -a logfile >&2
}

Também consertei a readligação paraIFS= read -rpara realmente ler uma linha. Removi o tratamento especial para linhas vazias, o que é inútil (não haveria linhas vazias na entrada). Presumo que você o tenha colocado para lidar com o caso quando a entrada está vazia (zero linhas de entrada), mas a maneira correta de lidar com isso é verificar o status de retorno do readcomando. Também corrigi loga impressão com seu erro padrão, já que é usado para processar mensagens de erro.

VerAnexando um carimbo de data/hora a cada linha de saída de um comandopara outras maneiras de fazer isso.

Esteja ciente de que colocar o comando no lado esquerdo de um tubo tem uma grande desvantagem:faz com que o status de saída seja ignorado. Portanto, se cpfalhar, seu script continuará feliz. Os erros serão registrados em algum lugar, mas os comandos subsequentes serão executados normalmente e nada irá alertá-lo sobre o fato de que você deve ler os logs. No bash, ksh ou zsh, você pode definir opipefailopçãoalém de set -eque seu script saia com status de erro assim que qualquer comando falhar, mesmo no lado esquerdo de um pipeline.

set -o errexit -o pipefail
copy … |& log

Alternativamente, usesubstituição de processoem vez de um canal para canalizar a saída de erro através de outro processo. A substituição de processos tem advertências ligeiramente diferentes das de tubos; erros logsão efetivamente ignorados e o comando pode retornar antes de logser concluído (no bash, logexecutado em segundo plano).

set -e
copy … 2> >(log)

¹ Quase, você IFS= read -r INnunca precisaria ler mais e possivelmente não estragaria a linha.

informação relacionada