Eu tenho um certo script bash, que deseja preservar o /dev/stdout
local original antes de substituir o descritor do 1º arquivo por outro local.
Então, naturalmente, eu escrevi algo como
old_stdout=$(readlink -f /dev/stdout)
E não deu certo. Muito rapidamente entendi qual era o problema:
test@ubuntu:~$ echo $(readlink -f /dev/stdout)
/proc/5175/fd/pipe:[31764]
test@ubuntu:~$ readlink -f /dev/stdout
/dev/pts/18
Obvioulsly, $()
é executado em um subshell, que é canalizado para o shell pai.
Portanto, a pergunta é: existe uma maneira confiável (com escopo de portabilidade entre distribuições Linux) para salvar o /dev/stdout
local como uma string em um script bash?
bash
io-redirection
io
alexey.e.egorov
fonte
fonte
/dev/stdout
resolveria o problema com a impressão de mensagens no modo silencioso. A alternativa é redirecionar todas as outras ações que produzem saída, e há várias delas. Aproximadamente 100 vezes mais que as mensagens de interação do usuário.stderr
. É por exemplo, por que os prompts são enviadosstderr
por padrão.stderr
também deve ser redirecionado e salvo, pois o script chama vários programas externos e todas as mensagens de erro possíveis devem ser coletadas e registradas.Respostas:
Para salvar um descritor de arquivo, você o duplica em outro arquivo fd. Salvar um caminho para o arquivo correspondente não é suficiente; você precisará salvar o modo de abertura, os sinalizadores de abertura, a posição atual no arquivo e assim por diante. E, é claro, para tubos ou soquetes anônimos, que não funcionariam, pois não têm caminho. O que você deseja salvar é a descrição do arquivo aberto a que o fd se refere, e a duplicação de um fd está realmente retornando um novo fd à mesma descrição de arquivo aberto .
Para duplicar um descritor de arquivo em outro, com shell semelhante ao Bourne, a sintaxe é:
Acima, o fd 1 é duplicado no fd 3.
Qualquer coisa que o fd 3 já estivesse aberto antes seria encerrada, mas observe que os fds 3 a 9 (geralmente mais, até 99 com
yash
) são reservados para esse fim (e não têm significado especial contrário a 0, 1 ou 2), o A shell sabe que não deve usá-los para seus próprios negócios internos. A única razão pela qual o fd 3 teria sido aberto anteriormente é porque você fez isso no script 1 ou foi vazado pelo chamador.Em seguida, você pode alterar o stdout para outra coisa:
E mais tarde, para restaurar o stdout:
(
3>&-
sendo fechar o descritor de arquivo que não precisamos mais).Agora, o problema é que, exceto no ksh, todos os comandos que você executar depois
exec 3>&1
herdarão o fd 3. Esse é um vazamento de fd. Geralmente não é grande coisa, mas isso pode causar problemas.ksh
define o sinalizador close-on-exec nesses fds (para fds acima de 2), mas nenhum outro shells e outros shells não têm como definir esse sinalizador manualmente.A solução para outro shell é fechar o fd 3 para cada comando, como:
Pesado. Aqui, a melhor maneira seria não usar
exec
, mas redirecionar os grupos de comandos:Lá, é o shell que cuida de salvar o stdout e restaurá-lo posteriormente (e o faz internamente duplicando-o em um fd (acima de 9, acima de 99 para
yash
) com o conjunto de sinalizadores close-on-exec ).Nota 1
Agora, o gerenciamento desses fds 3 a 9 pode ser complicado e problemático se você os usar extensivamente ou em funções, especialmente se o seu script usar algum código de terceiros que por sua vez possa usá-los.
Alguns escudos (
zsh
,bash
,ksh93
, tudo foi adicionado o recurso ( sugerido por Oliver Kiddle dezsh
) em torno do mesmo tempo em 2005 depois de ter sido discutido entre seus desenvolvedores) tem uma sintaxe alternativa para atribuir o primeiro fd livre acima de 10 vez que ajuda neste caso:fonte
rc.local
serviço, por exemplo, para que você realmente tenha usado algo parecidoexec {FD}>&1
ou algo assim. Mas isso é suportado apenas no bash 4, o que é realmente triste. Portanto, isso não é realmente portátil.eval "exec $i>&1"
é uma coisa que eu gostaria de evitar, devido à sua dificuldade. Posso realmente confiar que fds acima de 9 seriam gratuitos então?bash
permitirá que você atire no próprio pé.Como você pode ver, o script bash não é como uma linguagem de programação comum, na qual você pode atribuir descritores de arquivo.
A solução mais simples é usar um sub shell para executar o que você deseja redirecionar, para que o processamento possa ser revertido para o shell superior que possui sua E / S padrão intacta.
Uma solução alternativa seria usar
tty
para identificar o dispositivo TTY e controlar a E / S no seu script. Por exemplo:e então você pode ..
fonte
$$
obteria o PID do processo atual; no caso de shell interativo ou script, o PID do shell relevante.Então você pode usar:
Exemplo:
fonte
/proc
estrutura específica causa problemas de portabilidade, assim como o uso/dev/stdout
mencionado na pergunta./proc
estrutura específica? Ele iria trabalhar em qualquer Linux que temprocfs
..procfs
quase sempre está presente, mas geralmente vemos questões de portabilidade e uma boa metodologia de desenvolvimento inclui considerar a portabilidade para outros sistemas.bash
pode ser executado em vários sistemas operacionais.