Quando executo esse script, pretendia ser executado até ser morto ...
# foo.sh
while true; do sleep 1; done
... Não consigo encontrá-lo usando ps ax
:
>./foo.sh
// In a separate shell:
>ps ax | grep foo.sh
21110 pts/3 S+ 0:00 grep --color=auto foo.sh
... mas se eu apenas adicionar o #!
cabeçalho " " comum ao script ...
#! /usr/bin/bash
# foo.sh
while true; do sleep 1; done
... então o script se torna localizável pelo mesmo ps
comando ...
>./foo.sh
// In a separate shell:
>ps ax | grep foo.sh
21319 pts/43 S+ 0:00 /usr/bin/bash ./foo.sh
21324 pts/3 S+ 0:00 grep --color=auto foo.sh
Porque isto é assim?
Esta pode ser uma pergunta relacionada: " #
" pensei que era apenas um prefixo de comentário e, se for, " #! /usr/bin/bash
" nada mais é do que um comentário. Mas " #!
" carrega algum significado maior do que apenas um comentário?
shell-script
shell
ps
shebang
StoneThrow
fonte
fonte
Respostas:
Quando o shell interativo atual estiver
bash
e você executar um script sem#!
linha,bash
o script será executado. O processo aparecerá naps ax
saída como justobash
.Em outro terminal:
Palavras-chave:
As seções relevantes formam o
bash
manual:Isso significa que executar
./foo.sh
na linha de comando, quandofoo.sh
não possui uma#!
linha, é o mesmo que executar os comandos no arquivo em uma subshell, ou seja, comoCom uma
#!
linha adequada apontando para/bin/bash
, por exemplo , é como fazerfonte
ps
mostra o script sendo executado como "/usr/bin/bash ./foo.sh
". Portanto, no primeiro caso, como você diz, o bash executará o script, mas esse script não precisa ser "passado" para o executável do bash bifurcado, como no segundo caso? (e se sim, imagino que seria possível encontrar o cachimbo para grep ...?)$$
ainda aponta para o antigo no caso de subshell (echo $BASHPID
/bash -c 'echo $PPID'
).Quando um script de shell começa
#!
, essa primeira linha é um comentário no que diz respeito ao shell. No entanto, os dois primeiros caracteres são significativos para outra parte do sistema: o kernel. Os dois caracteres#!
são chamados de shebang . Para entender o papel do shebang, você precisa entender como um programa é executado.A execução de um programa a partir de um arquivo requer ação do kernel. Isso é feito como parte da
execve
chamada do sistema. O kernel precisa verificar as permissões do arquivo, liberar os recursos (memória, etc.) associados ao arquivo executável atualmente em execução no processo de chamada, alocar recursos para o novo arquivo executável e transferir o controle para o novo programa (e mais coisas que Eu não vou mencionar). Aexecve
chamada do sistema substitui o código do processo em execução no momento; há uma chamada de sistema separadafork
para criar um novo processo.Para fazer isso, o kernel precisa suportar o formato do arquivo executável. Este arquivo deve conter o código da máquina, organizado da maneira que o kernel entende. Um script de shell não contém código de máquina, portanto, não pode ser executado dessa maneira.
O mecanismo shebang permite que o kernel adie a tarefa de interpretar o código para outro programa. Quando o kernel vê que o arquivo executável começa
#!
, ele lê os próximos caracteres e interpreta a primeira linha do arquivo (menos o#!
espaço inicial e opcional) como um caminho para outro arquivo (mais argumentos, que não serão discutidos aqui) ) Quando o kernel é dito para executar o arquivo/my/script
, e ele vê que o arquivo começa com a linha#!/some/interpreter
, o kernel executa/some/interpreter
com o argumento/my/script
. Cabe então/some/interpreter
decidir que/my/script
esse arquivo de script deve ser executado.E se um arquivo não contiver código nativo em um formato que o kernel compreenda e não iniciar com um shebang? Bem, o arquivo não é executável e a
execve
chamada do sistema falha com o código de erroENOEXEC
(erro de formato executável).Este pode ser o fim da história, mas a maioria dos shells implementa um recurso de fallback. Se o kernel retornar
ENOEXEC
, o shell examinará o conteúdo do arquivo e verificará se ele se parece com um script de shell. Se o shell achar que o arquivo se parece com um script de shell, ele será executado sozinho. Os detalhes de como isso acontece depende do shell. Você pode ver um pouco do que está acontecendo adicionandops $$
no seu script e muito mais assistindo ao processo emstrace -p1234 -f -eprocess
que 1234 é o PID do shell.No bash, esse mecanismo de fallback é implementado chamando
fork
mas nãoexecve
. O processo bash filho limpa seu estado interno sozinho e abre o novo arquivo de script para executá-lo. Portanto, o processo que executa o script ainda está usando a imagem do código do bash original e os argumentos da linha de comando originais passados quando você chamou o bash originalmente. O ATT ksh se comporta da mesma maneira.Dash, ao contrário, reage
ENOEXEC
chamando/bin/sh
com o caminho para o script passado como argumento. Em outras palavras, quando você executa um script sem barra a partir do traço, ele se comporta como se o script tivesse uma linha shebang#!/bin/sh
. Mksh e zsh se comportam da mesma maneira.fonte
bash
é bifurcado, ele tem acesso à mesmaargv[]
matriz que seu pai, que é como ele sabe "os argumentos originais da linha de comando passados quando você chamou o bash originalmente" e se então, é por isso que o filho não recebe o script original como argumento explícito (daí o fato de ele não ser encontrado pelo grep) - é preciso?BINFMT_SCRIPT
módulo controla isso e pode ser removido / modularizado, embora geralmente esteja vinculado estaticamente ao kernel), mas não vejo por que você desejaria, exceto talvez em um sistema incorporado . Como solução alternativa para essa possibilidade,bash
possui um sinalizador de configuração (HAVE_HASH_BANG_EXEC
) para compensar!ps
relatórios são os argumentos da linha de comando, mas apenas até certo ponto: ele precisa modificar um buffer de memória existente, não pode aumentar esse buffer. Portanto, se o bash tentasse modificá-loargv
para adicionar o nome do script, nem sempre funcionaria. A criança não é "aprovada em um argumento" porque nunca há umaexecve
chamada de sistema na criança. É apenas a mesma imagem do processo bash que continua sendo executada.No primeiro caso, o script é executado por um filho extraído do seu shell atual.
Você deve primeiro executar
echo $$
e, em seguida, examinar um shell que tenha o ID do processo do seu shell como ID do processo pai.fonte