O shell fork quando eu uso comandos internos?

6

Quando executo o comando type type , isso me dá um resultado para a saída padrão.

Então, agora eu tento este comando:

type type > abc.txt

Ele redireciona a saída padrão para abc.txt . Portanto, se esse comando for executado no mesmo processo (bash propriamente dito), o descritor de arquivo 1 apontará para abc.txt . Depois disso, sempre que eu uso um comando, esses resultados devem ir para o arquivo abc.txt , porque o descritor de arquivo 1 aponta para abc.txt .

No entanto, os resultados sempre vão para a saída padrão. Isso significa que os built-ins do shell são executados sob um processo bifurcado?

    
por A.Cho 01.03.2016 / 16:06

3 respostas

10

Redirecionamento de saída

Descritor de arquivo 1 representa stdout , o fluxo de saída padrão. Quando o redirecionamento de saída é usado em type type > abc.txt , o shell abre o arquivo abc.txt para gravação e o descritor de arquivo 1 é modificado para que aponte para o arquivo aberto em vez do dispositivo terminal.

No entanto, este redirecionamento só se aplica ao comando atual sendo executado, portanto, isso não implica que o comando seja executado em um processo bifurcado (ou subshell).

Redirecionamento persistente

Se você quiser que o redirecionamento persista, você pode usar o exec shell embutido para modificar os descritores de arquivo, por exemplo, para redirecionar a saída padrão para comandos sucessivos, execute o seguinte comando.

exec >abc.txt

Tenha cuidado ao executar isso, pois sua sessão de shell será difícil de usar se todos saída de comando está sendo redirecionada para um arquivo em vez de seu dispositivo de terminal. Você pode restaurar o descritor de arquivo stdout para o dispositivo de saída do terminal, redirecionando-o para o mesmo dispositivo apontado por stderr (descritor de arquivo 2 ):

exec >&2

Recursos relacionados

Para mais detalhes, consulte:

por 01.03.2016 / 16:32
19

Você tem razão em que, normalmente, para redirecionar um comando, tudo que o shell precisa fazer é bifurcar um processo filho, fazer o redirecionamento apenas no filho (os descritores de arquivo pai não são afetados) e, em seguida, executar o comando lá (enquanto o pai apenas espera pela criança).

Para comandos incorporados (ou comandos compostos ou funções ...), não há fork nem exec. No entanto, depois que um comando interno redirecionado é encerrado, os descritores de arquivo voltam a ser como eram antes.

Para fazer isso, o shell apenas salva uma cópia do descritor de arquivo redirecionado para outro descritor de arquivo antes de fazer o redirecionamento, marca esse descritor de arquivo com o sinalizador O_CLOEXEC (caso o servidor interno execute um comando como eval ou command iria), e quando o builtin retorna, o shell restaura o descritor de arquivo.

Você pode ver isso, por exemplo:

strace sh -c 'echo test > /dev/null; :'

Você verá (apenas entradas relevantes incluídas):

  • open("/dev/null", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3 : abre o arquivo para redirecionar para.
  • fcntl(1, F_DUPFD, 10) = 10 : duplique o stdout original para o primeiro fd disponível > = 10 (aqui 10).
  • fcntl(10, F_SETFD, FD_CLOEXEC) = 0 : define o sinalizador O_CLOEXEC
  • dup2(3, 1) = 1 : tornar o arquivo redirecionado stdout
  • close(3) = 0 : não é mais necessário
  • write(1, "test\n", 5) = 5 : echo executa e grava "test \ n" em seu stdout (agora redirecionado para / dev / null).
  • dup2(10, 1) = 1 : restaurar stdout
  • close(10) = 0 : fechar fd 10 não é mais necessário.

Observe que no shell Bourne, o redirecionamento de um comando composto causou uma bifurcação (como em a=0; { a=1; echo "$a"; } >&2; echo "$a" , você obteria 1 , em seguida, 0 ). Para contornar isso, você realmente tinha que fazer o seguinte manualmente:

Em vez de

while cmd1; do cmd2; i='expr "$i" + 1'; done > file

Você tinha que fazer:

exec 3>&1 > file
while cmd1 3>&-; do cmd2 3>&-; i='expr "$i" + 1 3>&-'; done
exec >&3 3>&-

(com 3>&- para cada comando, já que fd não possui o flag O_CLOEXEC).

E nas versões iniciais do shell Bourne, você não podia redirecionar os recursos internos. Em um Unix V7 em um pdp11 emulado:

$ eval a=1 > /tmp/x
illegal io
$ read a < /etc/passwd
illegal io
    
por 01.03.2016 / 17:47
0

Assim que o comando conclui, os arquivos de entrada são fechados, tornando-o disponível para gravação por outros processos, o descritor de arquivo é liberado e o processo filho é finalizado.

    
por 01.03.2016 / 23:40