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 / 793927
Accepted
Alex Martian
Alex Martian
Asked: 2025-04-19 12:33:30 +0800 CST2025-04-19 12:33:30 +0800 CST 2025-04-19 12:33:30 +0800 CST

Como (internamente) fd3>&fd1 após { fd1>&fd3 } coloca de volta (ou não) o fd original em fd1? ("descritor de arquivo inválido")

  • 772

`Estou lendo uma resposta para https://stackoverflow.com/questions/692000/how-do-i-write-standard-error-to-a-file-while-using-tee-with-a-pipe/692009#692009 , https://stackoverflow.com/a/14737103/5499118 :

{ { ./aaa.sh | tee bbb.out; } 2>&1 1>&3 | tee ccc.out; } 3>&1 1>&2

Como verifiquei, funciona conforme explicado. A resposta leva para https://unix.stackexchange.com/a/18904/266260 , que leva para https://unix.stackexchange.com/a/3540/266260 .

Não entendo por que { ... 1>&3 ... } 3>&1funciona (como o redirecionamento posterior reverte o efeito do anterior), porque quando eu queria entender man bash:

Observe que a ordem dos redirecionamentos é significativa. Por exemplo, o comando

ls > dirlist 2>&1

direciona a saída padrão e o erro padrão para o arquivo dirlist, enquanto o comando

ls 2>&1 > dirlist

direciona apenas a saída padrão para o arquivo dirlist, porque o erro padrão foi duplicado da saída padrão antes que a saída padrão fosse redirecionada para dirlist.

Encontrei duplicação de descritores de arquivo no redirecionamento :

Os redirecionamentos são implementados por meio da família de funções do sistema dup. dup é a abreviação de duplicação e quando você faz isso, por exemplo:

3>&2

você duplica (dup2) o filedescriptor 2 no filedescriptor 3 ...

Portanto, entendo que 1>&3duplica 3 em 1 e eles apontam para o mesmo objeto a partir desse comando man dup.

Após um retorno bem-sucedido, os descritores de arquivo antigo e novo podem ser usados ​​indistintamente. Eles se referem à mesma descrição de arquivo aberto.

Pela dupexplicação, não espero 3>&1mudar nada, pois 3 e 1 já são iguais. Mas aparentemente não é o caso, pois omitir 3>&1 dos { { ./aaa.sh | tee bbb.out; } 2>&1 1>&3 | tee ccc.out; } 3>&1 1>&2resultados embash: 3: bad file descriptor

O que (se houver) está incorreto ao explicar o redirecionamento com dupchamadas? O que acontece internamente durante 1>&3e 3>&1? Talvez { }sejam importantes aqui, mas vejo que são usados ​​apenas para agrupamento e por man bash:

list é simplesmente executado no ambiente de shell atual.

shell
  • 2 2 respostas
  • 364 Views

2 respostas

  • Voted
  1. Best Answer
    ilkkachu
    2025-04-19T16:30:21+08:002025-04-19T16:30:21+08:00

    Então, simplificando um pouco, você tem

    { a 2>&1 1>&3 | b; } 3>&1
    

    Agora, os redirecionamentos fora do grupo de chaves precisam ser tratados separadamente dos de dentro; eles precisam ser definidos antes que qualquer coisa no grupo de colchetes seja executada. Assim, o grupo de colchetes vê os fds 1 e 3 apontando para o mesmo lugar (a saída padrão original).

    Em seguida, o pipe redireciona o stdout do lado esquerdo para o stdin do processo do lado direito. Isso também acontece antes da execução de qualquer um dos lados, ou de seus redirecionamentos.

    Vamos reescrever isso com a sintaxe de substituição de processo, apenas para mostrar os redirecionamentos em ordem da esquerda para a direita. ( >(b)executa b, disponibiliza seu stdin como um nome de arquivo e expande para esse nome. Então 1> >(b)redireciona o stdout para esse "arquivo" e bo stdin de .)

    { a 1> >(b) 2>&1 1>&3; } 3>&1
    

    ou mesmo,

    a 3>&1 1> >(b) 2>&1 1>&3
    

    Então, aprimeiro o stdout(1) original é copiado para o fd 3 (o redirecionamento que estava fora do grupo de chaves).

    Em seguida, bé iniciado, com ao stdout(1) do redirecionado para bo stdin do . aO stderr(2) do é redirecionado para o mesmo. Agora, os fds 1 e 2 vão para b, e o fd 3 vai para o stdout original.

    E, finalmente, ao stdout(1) de é redirecionado para onde o fd 3 vai, que é uma cópia do stdout original, desfazendo o redirecionamento anterior do stdout para b. Agora, o fd 1 vai para o stdout original novamente, enquanto o fd 2 vai para b. Como mencionado em outra resposta , agora você também pode fechar o fd 3 com , 3>&-já que ele não é mais necessário.

    Escrito dessa forma, alguém poderia perguntar se faz algum sentido, por que não fazer isso:

    a 2> >(b)
    

    E você poderia, mas a sintaxe do pipe não permite especificar qual fd ou fds redirecionar para o pipe, é sempre apenas stdout. E a substituição de processos não é um recurso POSIX, então nem todos os shells a suportam, e há o recurso irritante de que processos iniciados a partir da substituição de processos podem ser deixados em execução em segundo plano enquanto o shell principal continua, ao contrário dos pipes, onde o shell aguarda por todas as partes de um pipeline.

    O 1>&2que estava lá também "desfaz" o 2>&1fato de que quando o stderr de afoi redirecionado para o pipe, o stdout do pipe (todo o grupo de chaves) é redirecionado de volta para o stderr do script.

    • 7
  2. Sotto Voce
    2025-04-19T16:22:52+08:002025-04-19T16:22:52+08:00

    Acho que você está confuso porque a maioria das descrições de duplicações de descritores de arquivo são meio vagas e é fácil perder os detalhes essenciais para entender isso.

    5>/path/to/file

    Quando um processo abre um descritor de arquivo em um arquivo, um canal de entrada/saída é formado entre o processo e o arquivo (isso também se aplica quando o objeto aberto é um diretório, um fluxo, um arquivo de rede, etc.). Você pode imaginar isso como um pipeline entre o processo e o arquivo, e esse é um bom modelo para entender os detalhes essenciais: há duas extremidades no pipeline — uma no processo e a outra no arquivo. Este post do blog tem boas descrições do canal/pipeline de que estou falando aqui.

    6>&5

    A duplicação do descritor de arquivo pode ser visualizada como a criação de um segundo pipeline. O número desse pipeline é diferente no final do processo, mas a outra extremidade do novo pipeline leva ao mesmo arquivo. Os dois descritores de arquivo têm, cada um, um canal que leva ao mesmo arquivo de destino.

    5>/dev/null

    Posteriormente no script, o descritor de arquivo original pode ser alterado para um arquivo/dispositivo de destino diferente. Muitas descrições de redirecionamento ignoram o fato de que, quando o descritor já estava aberto, esse redirecionamento executa duas etapas em vez de uma. O canal do descritor de arquivo original (5) é fechado, removendo seu pipeline para o arquivo original, e então o descritor é aberto com um novo canal (pipeline) para o novo arquivo.

    Isso não altera o descritor de arquivo duplicado (6), que ainda tem seu pipeline para o arquivo original.

    5>&6

    Agora, o script deseja restaurar o descritor de arquivo alterado (5) de volta ao arquivo original. O pipeline aberto para o novo destino é fechado e o descritor duplicado (6) é duplicado novamente para o descritor original (5). Agora, o descritor original é novamente conectado ao arquivo original.

    6>&-

    Alguns scripts agora fecham o descritor duplicado após sua finalidade ter sido cumprida. Agora, apenas o descritor original (5) é aberto para o arquivo original.

    O conceito-chave é que um descritor de arquivo aberto é como um pipeline com uma extremidade no processo e a outra em um arquivo/diretório/dispositivo/fluxo/qualquer coisa. Duplicar o descritor cria algo novo na extremidade do processo, mas ele vai para o mesmo lugar na extremidade do arquivo.

    Usei os descritores 5 e 6 nos meus exemplos acima, mas esse tipo de cópia de um descritor para outro descritor e sua restauração são feitos com mais frequência com os descritores para stdin (0), stdout (1) ou stderr (2).

    • 6

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

    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