Quando eu executo este script, destinado a 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: eu pensei que " #
" era apenas um prefixo de comentário e, nesse caso, " #! /usr/bin/bash
" não é nada mais do que um comentário. Mas " #!
" carrega algum significado maior do que apenas um comentário?
Quando o shell interativo atual for
bash
, e você executar um script sem#!
-line,bash
o script será executado. O processo aparecerá naps ax
saída como apenasbash
.Em outro terminal:
Relacionado:
As seções relevantes formam o
bash
manual:Isso significa que rodar
./foo.sh
na linha de comando, quandofoo.sh
não tem#!
-line, é o mesmo que rodar os comandos no arquivo em um subshell, ou seja, comoCom uma linha adequada
#!
apontando para, por exemplo/bin/bash
, é como fazerQuando um script de shell começa com
#!
, 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 personagens#!
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.) não vou mencionar). Aexecve
chamada do sistema substitui o código do processo atualmente em execução; há uma chamada de sistema separadafork
para criar um novo processo.Para fazer isso, o kernel tem que suportar o formato do arquivo executável. Este arquivo deve conter código de máquina, organizado de forma que o kernel entenda. 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 com
#!
, 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 discutirei aqui ). Quando o kernel é instruído a executar o arquivo/my/script
e 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
é um arquivo de script que deve ser executado.E se um arquivo não contiver código nativo em um formato que o kernel entenda e não começar com um shebang? Bem, então 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 analisa o conteúdo do arquivo e verifica se ele se parece com um script de shell. Se o shell achar que o arquivo se parece com um script de shell, ele o executa sozinho. Os detalhes de como ele faz isso dependem do shell. Você pode ver um pouco do que está acontecendo adicionandops $$
em seu script e muito mais observando o processostrace -p1234 -f -eprocess
onde 1234 é o PID do shell.No bash, esse mecanismo de fallback é implementado chamando,
fork
mas nãoexecve
. O processo filho bash 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 de código bash original e os argumentos de linha de comando originais passados quando você invocou o bash originalmente. ATT ksh se comporta da mesma maneira.Dash, por outro lado, reage
ENOEXEC
chamando/bin/sh
com o caminho para o script passado como argumento. Em outras palavras, quando você executa um script shebangless do traço, ele se comporta como se o script tivesse uma linha shebang com#!/bin/sh
. Mksh e zsh se comportam da mesma maneira.No primeiro caso, o script é executado por um filho bifurcado do seu shell atual.
Você deve primeiro executar
echo $$
e, em seguida, dar uma olhada em um shell que tenha o id do processo do seu shell como id do processo pai.