Quando você redireciona uma lista de comandos que contém um redirecionamento de exec, o exec> / dev / null ainda não parece ser aplicado posteriormente, como por exemplo:
{ exec >/dev/null; } >/dev/null; echo "Hi"
"Oi" é impresso.
Fiquei com a impressão de que a {}
lista de comandos não é considerada um subshell, a menos que faça parte de um pipeline, portanto exec >/dev/null
ainda deve ser aplicada no ambiente de shell atual em minha mente.
Agora, se você mudar para:
{ exec >/dev/null; } 2>/dev/null; echo "Hi"
não há saída conforme o esperado; o descritor de arquivo 1 permanece apontado para / dev / null para futuros comandos também. Isso é mostrado executando novamente:
{ exec >/dev/null; } >/dev/null; echo "Hi"
o que não dará saída.
Tentei fazer um script e segui-lo, mas ainda não tenho certeza do que está acontecendo aqui.
Em cada ponto deste script, o que está acontecendo com o descritor de arquivo STDOUT?
Edição: Adicionando minha saída strace:
read(255, "#!/usr/bin/env bash\n{ exec 1>/de"..., 65) = 65
open("/dev/null", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
fcntl(1, F_GETFD) = 0
fcntl(1, F_DUPFD, 10) = 10
fcntl(1, F_GETFD) = 0
fcntl(10, F_SETFD, FD_CLOEXEC) = 0
dup2(3, 1) = 1
close(3) = 0
close(10) = 0
open("/dev/null", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
fcntl(1, F_GETFD) = 0
fcntl(1, F_DUPFD, 10) = 10
fcntl(1, F_GETFD) = 0
fcntl(10, F_SETFD, FD_CLOEXEC) = 0
dup2(3, 1) = 1
close(3) = 0
dup2(10, 1) = 1
fcntl(10, F_GETFD) = 0x1 (flags FD_CLOEXEC)
close(10) = 0
fstat(1, {st_mode=S_IFCHR|0666, st_rdev=makedev(1, 3), ...}) = 0
ioctl(1, TCGETS, 0x7ffee027ef90) = -1 ENOTTY (Inappropriate ioctl for device)
write(1, "hi\n", 3) = 3
fonte
close(10)
. Você também pode postar todo o conteúdo do script que executou?;
depois}
, que altera o significado de> /dev/null
não aplicar à lista composta,{}
afinal.Respostas:
Vamos seguir
passo a passo.
Existem dois comandos:
uma.
{ exec >/dev/null; } >/dev/null
, Seguido porb.
echo "Hi"
O shell executa primeiro o comando (a) e depois o comando (b).
A execução do
{ exec >/dev/null; } >/dev/null
produto é a seguinte:uma. Primeiro, o shell executa o redirecionamento
>/dev/null
e lembra-se de desfazê-lo quando o comando termina .b. Em seguida, o shell é executado
{ exec >/dev/null; }
.c. Por fim, o shell alterna a saída padrão de volta para onde estava. (Esse é o mesmo mecanismo que in
ls -lR /usr/share/fonts >~/FontList.txt
- redirecionamentos são feitos apenas pela duração do comando ao qual eles pertencem.)Depois que o primeiro comando é concluído, o shell é executado
echo "Hi"
. A saída padrão está onde estava antes do primeiro comando.fonte
Para não usar um subcasca ou subprocesso, quando a saída de uma lista composta
{}
é canalizada>
, o shell salva o descritor STDOUT antes de executar a lista composta e a restaura depois. Portanto, aexec >
lista composta não leva seu efeito além do ponto em que o descritor antigo é restabelecido como STDOUT.Vamos dar uma olhada na parte relevante de
strace bash -c '{ exec >/dev/null; } >/dev/null; echo hi' 2>&1 | cat -n
:Você pode ver como, na linha 134, o descritor
1
(STDOUT
) é copiado para outro descritor com índice, pelo menos10
(é o queF_DUPFD
faz; ele retorna o descritor mais baixo disponível, começando no número especificado após duplicar nesse descritor). Veja também como, na linha 137, o resultado deopen("/dev/null")
(descritor3
) é copiado no descritor1
(STDOUT
). Finalmente, on-line147
, o antigoSTDOUT
salvo no descritor10
é copiado de volta para o descritor1
(STDOUT
). O efeito líquido é isolar a alteração paraSTDOUT
on-line144
(que corresponde ao interiorexec >/dev/null
).fonte
exec
.A diferença entre
{ exec >/dev/null; } >/dev/null; echo "Hi"
e{ exec >/dev/null; }; echo "Hi"
é que o redirecionamento duplo fazdup2(10, 1);
antes de fechar o fd 10, que é a cópia do originalstdout
, antes de executar o próximo comando (echo
).Isso acontece porque o redirecionamento externo está realmente sobrepondo o redirecionamento interno. É por isso que ele copia de volta o
stdout
fd original depois de concluído.fonte