AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / unix / Perguntas / 672318
Accepted
Binarus
Binarus
Asked: 2021-10-08 09:29:33 +0800 CST2021-10-08 09:29:33 +0800 CST 2021-10-08 09:29:33 +0800 CST

Podemos usar $PIPESTATUS com o comando tee (ou pee)?

  • 772

Em meus scripts bash, costumo usar pipes e gostaria de saber qual estágio do pipe estava causando o problema em caso de erros. A estrutura básica de tais snippets é:

#!/bin/bash

ProduceCommand 2>/dev/null | ConsumeCommand >/dev/null 2>&1
PipeErrors=("${PIPESTATUS[@]}")
[[ "${PipeErrors[0]}" -eq '0' ]] || { HandleErrorInProduceCommand; }
[[ "${PipeErrors[1]}" -eq '0' ]] || { HandleErrorInConsumeCommand; }

Agora (curiosamente pela primeira vez) estou em uma situação em que seria ótimo se eu pudesse usar teeou pee. Mas o que acontece ao $PIPESTATUSusar esses comandos? Por exemplo:

#!/bin/bash

ProduceCommand 2>/dev/null | tee >(ConsumeCommand1) >(ConsumeCommand2) >/dev/null 2>&1
PipeErrors=("${PIPESTATUS[@]}")

ou

#!/bin/bash

ProduceCommand 2>/dev/null | pee ConsumeCommand1 ConsumeCommand2 2>/dev/null
PipeErrors=("${PIPESTATUS[@]}")

Acredito que em ambos os casos ${PipeErrors[0]}reflita o status de erro do ProduceCommand. Além disso, seria lógico supor que ${PipeErrors[1]}reflete o status de erro de teeou de peesi mesmo, respectivamente.

Mas isso me leva a pelo menos dois problemas de compreensão:

  1. Qual é o status de erro (valor de retorno) de teeou pee? Não encontrei declarações precisas sobre isso nas páginas de manual. Eles retornam um status de erro codificado se um dos comandos de consumo falhar ou eles retransmitem o status de erro dos comandos de consumo de alguma forma (como ssh, por exemplo)? Se o primeiro for o caso, como podemos descobrir qual dos comandos de consumo é o culpado? Se o último for o caso, qual status de erro é retransmitido? É simplesmente o comando que falha primeiro?

  2. AFAIK, bash ou o próprio comando teeor pee, respectivamente, usam pipes (fifos) internamente para obter ProduceCommanda saída de 's para os comandos de consumo. Isso significa que temos um pipe cujo (primeiro e neste caso, único) lado receptor é o próprio pipe. Isso não deve influenciar $PipeErrorsno código de exemplo acima, mas não tenho certeza.

Alguém poderia dar uma luz sobre isso?

bash pipe
  • 2 2 respostas
  • 432 Views

2 respostas

  • Voted
  1. Best Answer
    Uncle Billy
    2021-10-08T16:15:56+08:002021-10-08T16:15:56+08:00

    Qual é o status de erro (valor de retorno) detee

    É 0 quando conseguiu copiar todos os dados para todos os arquivos de saída e >0 se não. Veja a especificação . A implementação do GNU coreutilstee tem opções extras para ignorar erros ao escrever em pipes (como aqueles usados ​​para implementar >(...)):

    $ seq 1024 | tee >(false) >/dev/null; echo $?
    141
    $ seq 1024 | tee -p >(false) >/dev/null; echo $?
    0
    

    Não há opções para saber qual das saídas falhou, se houver [1].


    Mas sua pergunta parece ser mais sobre se o status de saída dos comandos executados em >(..)substituições de processo é refletido de alguma forma em PIPESTATUS, ou se poderia ser refletido de alguma forma em PIPESTATUS.

    A resposta para isso é não .

    Em primeiro lugar, observe que >(...)são mais como comandos em ... &segundo plano do que comandos de ...|...pipeline. Em um trecho como:

    ... | tee >(cmd ...) | ...; echo ${PIPESTATUS[@]}
    

    não há garantia de que cmdtenha terminado no momento em que você executa echo ${PIPESTATUS[@]}.

    Mas eles não são exatamente iguais ...&, pois você não waitpode obtê-los e não pode obter seu status de $!-- exceto em alguns casos limitados que não incluem seu uso com teeou outros comandos externos:

    $ bash -c 'echo 1 | tee >(sleep 2; sed s/1/2/); wait; echo DONE'
    1
    DONE
    $
    <after two seconds>
    2
    

    Como você vê, ambos teee o shell principal terminaram muito antes do comando do >(...).

    [1] Um comando como peeo que está executando os "subcomandos" de saída (e está esperando que eles terminem) pode ser mais inteligente e refletir em seu status de saída quais deles falharam (por exemplo, definindo o bit 1 para o primeiro, bit 2 para o segundo, e assim por diante, até 8 subcomandos), mas também não faz isso.

    • 1
  2. Stéphane Chazelas
    2021-10-08T22:26:33+08:002021-10-08T22:26:33+08:00

    Você sempre pode fazer:

    {
      {
        ProduceCommand 2>/dev/null 3>&- ||
          HandleErrorInProduceCommand >&3 3>&-
      } |
        tee >(
          ConsumeCommand1 3>&- ||
            HandleErrorInConsumer1 >&3 3>&-
        ) >(
          ConsumeCommand2 3>&- ||
            HandleErrorInConsumer2 >&3 3>&-
        ) > /dev/null
    } 3>&1
    

    Isso lida com o erro em cada um dos subshells em que o produtor e os consumidores são iniciados.

    Duplicamos stdout em fd 3, para que possamos restaurar o stdout original para os manipuladores de erros, pois não queremos que sua saída, se houver, passe pelos tubos.

    Se você preferir que os manipuladores de erros sejam executados no processo do shell principal (para que ele possa sair, por exemplo), você pode fazer com que esses subshells passem o status de saída para o shell pai por meio de algum canal de substituição de comando:

     producer_status=-1
    consumer1_status=-1
    consumer2_status=-1
    {
      eval "$(
        {
          {
            ProduceCommand 2>/dev/null 4>&-
            echo "producer_status=$?" >&4
          } | tee >(
            ConsumeCommand1 4>&-
            echo "consumer1_status=$?" >&4
          ) >(
            ConsumeCommand2 4>&-
            echo "consumer2_status=$?" >&4
          )
        } 4>&1 >&3 3>&-
      )"
    } 3>&1
    
    [ "$producer_status"  -eq 0 ] || HandleErrorInProduceCommand
    [ "$consumer1_status" -eq 0 ] || HandleErrorInConsumer1
    [ "$consumer2_status" -eq 0 ] || HandleErrorInConsumer2
    

    Isso evita o $PIPESTATUSbashism ou você pode evitar o >(...)kshism e substituí-los por um pipeline normal:

    {
      ProduceCommand 2>/dev/null |
        {
          tee /dev/fd/4 |
            ConsumeCommand1 4>&-
        } 4>&1 >&3 3>&- |
          ConsumeCommand2 3>&-
    } 3>&1
     producer_status=${PIPESTATUS[0]}
    consumer1_status=${PIPESTATUS[1]}
    consumer2_status=${PIPESTATUS[2]}
    
    [ "$producer_status"  -eq 0 ] || HandleErrorInProduceCommand
    [ "$consumer1_status" -eq 0 ] || HandleErrorInConsumer1
    [ "$consumer2_status" -eq 0 ] || HandleErrorInConsumer2
    

    Ou combine as duas abordagens e, em seguida, obtenha a shsintaxe padrão e também tenha acesso ao teestatus de saída do 's como bônus.

     producer_status=-1
          tee_status=-1
    consumer1_status=-1
    consumer2_status=-1
    
    {
      eval "$(
        {
          {
            ProduceCommand 2>/dev/null 4>&-
            echo "producer_status=$?" >&4
          } 3>&- |
            {
              {
                tee /dev/fd/5 4>&-
                echo "tee_status=$?" >&4
              } |
                ConsumeCommand1 4>&-
              echo "consumer1_status=$?" >&4
            } 5>&1 >&3 3>&- |
            ConsumeCommand2 >&3 3>&- 4>&- 
            echo "consumer2_status=$?" >&4
        } 4>&1
      )"
    } 3>&1
    
    [ "$producer_status"  -eq 0 ] || HandleErrorInProduceCommand
    [ "$tee_status"       -eq 0 ] || HandleErrorInTee
    [ "$consumer1_status" -eq 0 ] || HandleErrorInConsumer1
    [ "$consumer2_status" -eq 0 ] || HandleErrorInConsumer2
    

    Note que teepode morrer de um SIGPIPE se um dos processos sair sem ler toda a entrada, o que significa que o outro processo também pode perder alguma entrada. Portanto, pode ser importante verificar também seu status de saída.

    Como já observado por @UncleBilly, com a implementação GNU de tee, isso pode ser contornado com a -popção (onde teeignora os sinais SIGPIPE e simplesmente para de tentar gravar mais dados em um pipe quando ele está quebrado).

    Com outras implementações, você pode substituir tee ...por (trap '' PIPE; exec tee ...)para obter um comportamento semelhante (embora possivelmente algumas mensagens de erro sobre os tubos quebrados, mesmo que teenão sejam abortadas).

    • 1

relate perguntas

  • Problema estranho ao passar variáveis ​​do arquivo de texto

  • Enquanto a linha lê mantendo os espaços de escape?

  • ordem de substituição de processos `te` e `bash`

  • Execute um script muito lento até que seja bem-sucedido

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    Possível firmware ausente /lib/firmware/i915/* para o módulo i915

    • 3 respostas
  • Marko Smith

    Falha ao buscar o repositório de backports jessie

    • 4 respostas
  • Marko Smith

    Como exportar uma chave privada GPG e uma chave pública para um arquivo

    • 4 respostas
  • Marko Smith

    Como podemos executar um comando armazenado em uma variável?

    • 5 respostas
  • Marko Smith

    Como configurar o systemd-resolved e o systemd-networkd para usar o servidor DNS local para resolver domínios locais e o servidor DNS remoto para domínios remotos?

    • 3 respostas
  • Marko Smith

    apt-get update error no Kali Linux após a atualização do dist [duplicado]

    • 2 respostas
  • Marko Smith

    Como ver as últimas linhas x do log de serviço systemctl

    • 5 respostas
  • Marko Smith

    Nano - pule para o final do arquivo

    • 8 respostas
  • Marko Smith

    erro grub: você precisa carregar o kernel primeiro

    • 4 respostas
  • Marko Smith

    Como baixar o pacote não instalá-lo com o comando apt-get?

    • 7 respostas
  • Martin Hope
    user12345 Falha ao buscar o repositório de backports jessie 2019-03-27 04:39:28 +0800 CST
  • Martin Hope
    Carl Por que a maioria dos exemplos do systemd contém WantedBy=multi-user.target? 2019-03-15 11:49:25 +0800 CST
  • Martin Hope
    rocky Como exportar uma chave privada GPG e uma chave pública para um arquivo 2018-11-16 05:36:15 +0800 CST
  • Martin Hope
    Evan Carroll status systemctl mostra: "Estado: degradado" 2018-06-03 18:48:17 +0800 CST
  • Martin Hope
    Tim Como podemos executar um comando armazenado em uma variável? 2018-05-21 04:46:29 +0800 CST
  • Martin Hope
    Ankur S Por que /dev/null é um arquivo? Por que sua função não é implementada como um programa simples? 2018-04-17 07:28:04 +0800 CST
  • Martin Hope
    user3191334 Como ver as últimas linhas x do log de serviço systemctl 2018-02-07 00:14:16 +0800 CST
  • Martin Hope
    Marko Pacak Nano - pule para o final do arquivo 2018-02-01 01:53:03 +0800 CST
  • Martin Hope
    Kidburla Por que verdadeiro e falso são tão grandes? 2018-01-26 12:14:47 +0800 CST
  • Martin Hope
    Christos Baziotis Substitua a string em um arquivo de texto enorme (70 GB), uma linha 2017-12-30 06:58:33 +0800 CST

Hot tag

linux bash debian shell-script text-processing ubuntu centos shell awk ssh

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve