Redirecionando apenas stderr para um pipe

3

Este snippet de código é do Guia avançado de scripts do bash .

# Redirecting only stderr to a pipe.

exec 3>&1                              # Save current "value" of stdout.
ls -l 2>&1 >&3 3>&- | grep bad 3>&-    # Close fd 3 for 'grep' (but not 'ls').
#              ^^^^   ^^^^
exec 3>&-                              # Now close it for the remainder of the script.

# Thanks, S.C.

Os comentários explicam que o código fecha fd 3 apenas para 'grep'. mas o fecha fd 3 duas vezes. Por que precisamos fazer assim? É errado fechar o fd 3 apenas uma vez para 'grep' assim?

ls -l 2>&1 >&3 | grep bad 3>&-    
    
por MS.Kim 02.04.2014 / 08:42

2 respostas

5

Nem ls nem grep precisam de algo aberto no fd3 (eles não usam esse fd), então é melhor fechá-lo (libere os recursos que você não precisa). Nós só usamos o fd 3 para restaurar ls stdout para o original (antes de executar ls ).

Lembre-se de que, em uma linha de tubulação, todos os comandos são executados simultaneamente em seu próprio processo. Redirecionamentos se aplicam para cada um separadamente, então fechar o fd 3 para um não pode fechá-lo para o outro.

Não fará muita diferença na prática, neste caso, fechar ou não fechar, a não ser que você alcance um limite no número de descritores de arquivos mais cedo, se não o fizer.

Em outros casos (comandos que iniciam outros processos, por exemplo), podem conter recursos e causar problemas. Pense, por exemplo, se o stdout é um pipe, por exemplo, um processo de segundo plano pode acabar herdando esse fd, impedindo que qualquer leitor desse canal veja o EOF até que o processo retorne.

Uma maneira melhor de escrever seria:

{ ls -l 2>&1 >&3 3>&- | grep bad 3>&-; } 3>&1

Dessa forma, o fd3 é redirecionado apenas temporariamente para a duração desse grupo de comando (e restaurado depois ou fechado).

Veja a diferença:

$ { ls -l /proc/self/fd 2>&1 >&3 3>&- | grep bad 3>&-; } 3>&1
total 0
lrwx------ 1 stephane stephane 64 Apr  2 09:29 0 -> /dev/pts/4
lrwx------ 1 stephane stephane 64 Apr  2 09:29 1 -> /dev/pts/4
l-wx------ 1 stephane stephane 64 Apr  2 09:29 2 -> pipe:[575886]
lr-x------ 1 stephane stephane 64 Apr  2 09:29 3 -> /proc/20918/fd/
$ { ls -l /proc/self/fd 2>&1 >&3  | grep bad 3>&-; } 3>&1
total 0
lrwx------ 1 stephane stephane 64 Apr  2 09:29 0 -> /dev/pts/4
lrwx------ 1 stephane stephane 64 Apr  2 09:29 1 -> /dev/pts/4
l-wx------ 1 stephane stephane 64 Apr  2 09:29 2 -> pipe:[575900]
lrwx------ 1 stephane stephane 64 Apr  2 09:29 3 -> /dev/pts/4
lr-x------ 1 stephane stephane 64 Apr  2 09:29 4 -> /proc/20926/fd/

Na segunda invocação, ls teve seu fd 3 também aberto para o terminal (que estava aberto no stdout no momento em que executei o pipeline) sem um bom motivo.

Note que em ksh93, com exec , você não precisa fechar esses fds, pois fds diferentes de 0, 1, 2 são automaticamente fechados após a execução de um comando.

$ ksh93 -c 'exec 3>&1; ls -l /dev/fd/'
total 0
lrwx------ 1 stephane stephane 64 Apr  2 09:34 0 -> /dev/pts/16
lrwx------ 1 stephane stephane 64 Apr  2 09:34 1 -> /dev/pts/16
lrwx------ 1 stephane stephane 64 Apr  2 09:34 2 -> /dev/pts/16
lr-x------ 1 stephane stephane 64 Apr  2 09:34 3 -> /proc/21105/fd
$ ksh93 -c 'exec 3>&1; ls -l /dev/fd/ 3>&3'
total 0
lrwx------ 1 stephane stephane 64 Apr  2 09:34 0 -> /dev/pts/16
lrwx------ 1 stephane stephane 64 Apr  2 09:34 1 -> /dev/pts/16
lrwx------ 1 stephane stephane 64 Apr  2 09:34 2 -> /dev/pts/16
lrwx------ 1 stephane stephane 64 Apr  2 09:34 3 -> /dev/pts/16
lr-x------ 1 stephane stephane 64 Apr  2 09:34 4 -> /proc/21108/fd

Eu não sei porque diz (mas não 'ls') acima, soa como um erro (possivelmente meu; -).

    
por 02.04.2014 / 09:01
3

Feche fd3 duas vezes porque você bifurca duas subshell no script, cada subshell é herdada e copia o descritor de arquivo pai. Fechar fd3 em cada subshell não afeta os outros (e o pai também).

Portanto, a linha de comentários é muito obscura e causa enganos.

Se você quiser redirecionar apenas stder para pipe, use process substitution :

ls -l 2> >(grep bad)

ou troque stderr e stdout :

ls -l 3>&1 1>&2 2>&3 | grep bad
    
por 02.04.2014 / 09:45