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 / 474232
Accepted
agc
agc
Asked: 2018-10-10 04:08:44 +0800 CST2018-10-10 04:08:44 +0800 CST 2018-10-10 04:08:44 +0800 CST

Por que `colar` não pode imprimir stdin ao lado de stderr?

  • 772

Geralmente pasteimprime dois arquivos nomeados (ou equivalentes) em colunas adjacentes como esta:

paste <(printf '%s\n' a b) <(seq 2)

Resultado:

a   1
b   2

Mas quando os dois arquivos são /dev/stdine /dev/stderr, não parece funcionar da mesma maneira.

Suponha que temos um programa b falta b box que gera duas linhas na saída padrão e duas linhas no erro padrão . Para fins de ilustração, isso pode ser simulado com uma função:

bb() { seq 2 | tee >(sed 's/^/e/' > /dev/stderr) ; }

Agora execute annotate-output, (no pacote devscripts no Debian/Ubuntu/etc. ), para mostrar que funciona:

annotate-output bash -c 'bb() { seq 2 | tee >(sed 's/^/e/' > /dev/stderr) ; }; bb'
22:06:17 I: Started bash -c bb() { seq 2 | tee >(sed s/^/e/ > /dev/stderr) ; }; bb
22:06:17 O: 1
22:06:17 E: e1
22:06:17 O: 2
22:06:17 E: e2
22:06:17 I: Finished with exitcode 0

Então funciona. Alimentar bbpara paste:

bb | paste /dev/stdin /dev/stderr

Resultado:

1   e1
e2
^C

Ele trava - ^Csignifica pressionar Control-C para sair.

Alterar o |para a ;também não funciona:

bb ; paste /dev/stdin /dev/stderr

Resultado:

1
2
e1
e2
^C

Também trava -- ^Csignifica pressionar Control-C para sair.

Saída desejada:

1    e1
2    e2

Pode ser feito usando paste? Se não, por que não?

shell io-redirection
  • 3 3 respostas
  • 1034 Views

3 respostas

  • Voted
  1. Best Answer
    JigglyNaga
    2018-10-10T09:30:59+08:002018-10-10T09:30:59+08:00

    Por que você não pode usar /dev/stderr como pipeline

    O problema não é com paste, e nem com /dev/stdin. É com /dev/stderr.

    Todos os comandos são criados com um descritor de entrada aberto (0: entrada padrão) e duas saídas (1: saída padrão e 2: erro padrão). Eles normalmente podem ser acessados ​​com os nomes /dev/stdin, /dev/stdoute /dev/stderrrespectivamente, mas veja Quão portáteis são /dev/stdin, /dev/stdout e /dev/stderr? . Muitos comandos, incluindo paste, também interpretarão o nome do arquivo -como STDIN.

    Quando você executa bbsozinho, STDOUT e STDERR são o console, onde a saída do comando geralmente aparece. As linhas passam por diferentes descritores (como mostrado por seu annotate-output), mas acabam no mesmo lugar.

    Quando você adiciona um |e um segundo comando, criando um pipeline...

    bb | paste /dev/stdin /dev/stderr
    

    o |diz ao shell para conectar a saída de bbà entrada de paste. pasteprimeiro tenta ler de /dev/stdin, que (através de alguns links simbólicos) resolve para seu próprio descritor de entrada padrão (que o shell acabou de conectar) para que a linha 1passe.

    Mas o shell/pipeline não faz nada para STDERR. bbainda envia isso ( e1 e2etc.) para o console. Enquanto isso, pastetenta ler a partir do mesmo console, que trava (até você digitar algo).

    Seu link Por que não consigo ler /dev/stdout com um editor de texto? ainda é relevante aqui porque essas mesmas restrições se aplicam ao /dev/stderr.

    Como fazer um segundo pipeline

    Você tem um comando que produz tanto a saída padrão quanto o erro padrão e deseja pasteessas duas linhas próximas uma da outra. Isso significa dois tubos simultâneos, um para cada coluna. O pipeline do shell ... | ...fornece um desses, e você precisará criar o segundo você mesmo e redirecionar o STDERR para ele usando 2>filename.

    mkfifo RHS
    bb 2>RHS | paste /dev/stdin RHS
    

    Se for para uso em um script, você pode preferir fazer esse FIFO em um diretório temporário e removê-lo após o uso.

    • 4
  2. mosvy
    2018-10-11T14:31:47+08:002018-10-11T14:31:47+08:00

    annotate-outputé capaz de fazer isso porque está fazendo algo especial (ou seja, redireciona o stderr de um comando para um fifo), algo que não pastetem absolutamente nenhuma maneira de fazer - simplesmente porque nãopaste está executando o comando (s) do qual obtém sua entrada , e não tem como redirecionar sua entrada ou sua saída.

    Mas você pode escrever um wrapper que use exatamente o mesmo truque que a saída de anotação está usando:

    pasteout(){
      f=$(mktemp -u) || return
      mkfifo -m 600 -- "$f" || return
      "$@" 2>"$f" | paste -- - "$f"
      rm -f -- "$f"
    }
    pasteout bb
    

    Observe, no entanto, que é propenso a bloqueios. Se, por exemplo, bbproduzir mais saída padrão do que pode caber no tubo mais a quantidade extra inicialmente lida, pastemas não produzir nenhuma saída de erro, pasteserá bloqueado aguardando entrada no fifo e não esvaziará o tubo que bbestá alimentando seu stdout, fazendo com que bbos write()s do pipe também travem.

    • 3
  3. user232326
    2018-10-11T17:03:53+08:002018-10-11T17:03:53+08:00

    Existem alguns problemas com toda a linha que precisamos analisar, ou seja:

    seq 2 | tee >(sed 's/^/e/' > /dev/stderr) | paste /dev/stdin /dev/stderr
    

    stderr

    Primeiro, o último comando. Apenas stdout pode passar por um pipe:

    $ seq2 | paste -
    1
    2
    
    $ seq2 | paste - -
    1 2
    

    Não há nada para ler stderr:

    $ seq 2 | paste - /dev/stderr 
    1   ^C
    

    Você precisa ^Cdele porque ele bloqueia, não há nada para ler stderr.
    Mesmo se você criar alguma saída para stderrela, ela não percorre um tubo:

    $ { seq 2; seq 3 4 >/dev/stderr; } | paste - /dev/stderr
    1   3
    4
    

    Exatamente como antes, o 1é impresso e os pasteblocos esperando por stderr.
    Os outros 2 números foram diretamente para o console e foram impressos (independentemente).

    Você poderia dar alguma entrada stderrno último comando do pipe:

    $ { seq 2; seq 3 4 >/dev/stderr; } | paste - /dev/stderr 2</dev/null
    1
    2
    3
    4
    

    Que é exatamente o mesmo que 2>/dev/null, aliás, para evitar bloquear o segundo descritor de arquivo usado no pastecomando. Mas os valores impressos vêm diretamente do seq 3 4redirecionado para o console, não do paste. Isso faz o mesmo:

    $ { seq 2; seq 3 4 >/dev/tty; } | paste - /dev/stderr 2</dev/null
    1   
    2   
    3
    4
    

    E isso não bloqueia:

    $ seq 2 | tee >(sed 's/^/e/' > /dev/stderr) | 
      paste /dev/stdin /dev/stderr 2</dev/null
    1   
    2   
    e1
    e2
    

    ordem

    Segundo, a saída de teenão precisa estar "em ordem". Ordem de substituição do processo `tee` e `bash`

    E, de fato: A saída de uma substituição de processo não precisa estar "em ordem": A saída de substituição de processo está fora de ordem

    $ echo one; echo two > >(cat); echo three;
    one
    three
    two
    

    De fato, em alguns exemplos, se você tentar várias vezes, poderá obter pedidos diferentes. saída não determinística de processos independentes executados simultaneamente por substituição de processo

    $ printf '%s\n' {0..1000} | tee >(head -n2) >(sort -grk1,1 | head -n3) >/dev/null
    1000
    999
    998
    0
    1
    

    Então, não, não poderia ser feito com substituição de processo e pasta.
    Você precisa dar alguma ordem para a execução:

    $ seq 2 | { while read a; do printf "%s %s\n" "$a" "e$a" ; done; }
    1 e1
    2 e2
    

    bb

    Então, sua função bb, que (basicamente) contém:

    | tee >(sed 's/^/e/')
    

    Que poderia ser testado com:

    $ printf '%s\n' {0..1000} | tee >(sort -grk1,1 | head -n3 >&2) | head -n 2
    0
    1
    291
    290
    289
    

    Deve imprimir 0, 1, 1000, 999, 998, nessa ordem, mas muitas vezes não.
    Ou seja: é intrinsecamente instável.

    Solução real estável.

    A única solução segura para bb é evitar qualquer substituição de processo.
    E, aproveitando que {…}captura tanto stdout quanto stderr, exemplo:

    $ bash -c '{ echo test-str >/dev/stderr; }' 2>/dev/null
    

    Sem saída, remova o 2 para confirmar.

    Isso funcionará para bb:

    $ bb() { seq 5 | tee /dev/stderr | sed 's/^/e/'; }
    

    E use um fifo para colar:

    $ mkfifo out2
    $ bb 2>out2  | paste out2 -
    1   e1
    2   e2
    3   e3
    4   e4
    5   e5
    

    Você precisará definir um trap para remover o arquivo fifo e testar se o arquivo fifo existe antes de criá-lo.

    Parece funcionar de forma portátil em todos os shells (compatíveis com a sintaxe Almquist) que testei. Não totalmente testado, peça confirmação de outros usuários, pode haver algumas surpresas ainda desconhecidas.

    • 1

relate perguntas

  • Como funciona este comando? mkfifo /tmp/f; cat /tmp/f | /bin/sh -i 2>&1 | nc -l 1234 > /tmp/f

  • FreeBSD's sh: funções de lista

  • Existe uma maneira de fazer ls mostrar arquivos ocultos apenas para determinados diretórios?

  • o que grep -v grep faz

  • Como salvar um caminho com ~ em uma variável?

Sidebar

Stats

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

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

    • 4 respostas
  • Marko Smith

    ssh Não é possível negociar: "nenhuma cifra correspondente encontrada", está rejeitando o cbc

    • 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

    Como descarregar o módulo do kernel 'nvidia-drm'?

    • 13 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
    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
    Wong Jia Hau ssh-add retorna com: "Erro ao conectar ao agente: nenhum arquivo ou diretório" 2018-08-24 23:28:13 +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
  • Martin Hope
    Bagas Sanjaya Por que o Linux usa LF como caractere de nova linha? 2017-12-20 05:48:21 +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