Qual interpretador de shell executa um script sem shebang?


16

Suponha que o shell padrão da minha conta seja zsh, mas eu abri o terminal e iniciei o bash e executei um script chamado prac002.sh, qual interpretador de shell seria usado para executar o script, zsh ou bash? Considere o seguinte exemplo:

papagolf@Sierra ~/My Files/My Programs/Learning/Shell % sudo cat /etc/passwd | grep papagolf
[sudo] password for papagolf: 
papagolf:x:1000:1001:Rex,,,:/home/papagolf:/usr/bin/zsh
# papagolf's default shell is zsh

papagolf@Sierra ~/My Files/My Programs/Learning/Shell % bash
# I fired up bash. (See that '%' prompt in zsh changes to '$' prompt, indicating bash.)

papagolf@Sierra:~/My Files/My Programs/Learning/Shell$ ./prac002.sh 
Enter username : Rex
Rex
# Which interpreter did it just use?

** EDIT: ** Aqui está o conteúdo do script

papagolf@Sierra ~/My Files/My Programs/Learning/Shell % cat ./prac002.sh 
read -p "Enter username : " uname
echo $uname

O que o script diz no topo?
Michael Homer

@ MichaelHomer: Nada. Acabei de editar a pergunta para adicionar o conteúdo do script. O script tem permissão executável.
7_R3X

Se você deseja usar o shell atual, coloque-o na fonte de pontos, como em . prac002.sh, assumindo que seu script esteja no diretório atual.
Thecarpy

11
Execute . ./prac002.she ele será executado com o shell atual, ou seja, ponto ( .), espaço () seguido pelo caminho do seu script. Isso se chama fonte de pontos do seu script. ;-)
thecarpy

Respostas:


18

Como o script não começa com uma #!linha shebang indicando qual intérprete usar, o POSIX diz que :

Se a execl()função falhar devido a um erro equivalente ao erro [ENOEXEC] definido no volume System Interfaces do POSIX.1-2008, o shell executará um comando equivalente a ter um shell invocado com o nome do caminho resultante da pesquisa como seu primeiro operando , com os argumentos restantes passados ​​para o novo shell, exceto que o valor de "$ 0" no novo shell pode ser definido como o nome do comando. Se o arquivo executável não for um arquivo de texto, o shell poderá ignorar a execução deste comando. Nesse caso, ele deve escrever uma mensagem de erro e retornar um status de saída 126.

Essa frase é um pouco ambígua e conchas diferentes têm interpretações diferentes.

Nesse caso, o Bash executará o script usando-se . Por outro lado, se você o executasse a partir do zsh, o zsh usariash (o que quer que esteja no seu sistema).

Você pode verificar esse comportamento para este caso adicionando estas linhas ao script:

echo $BASH_VERSION
echo $ZSH_VERSION

Você notará que, no Bash, a primeira linha gera sua versão, enquanto a segunda nunca diz nada, independentemente do shell que você usa.

  • Se você /bin/shé, digamos dash,, nenhuma linha produzirá nada quando o script for executado a partir do zsh ou dash.
  • Se você /bin/shé um link para o Bash, verá a primeira linha de saída em todos os casos.
  • Se /bin/shhouver uma versão diferente do Bash do que você estava usando diretamente, você verá uma saída diferente ao executar o script diretamente do bash e do zsh.

O ps -p $$comando da resposta do rools também mostrará informações úteis sobre o comando que o shell usou para executar o script.


Eu votei, no entanto, ele usa o SHELL DEFAULT se nenhum shebang for especificado, qualquer shell padrão que você especificou. É por isso que você sempre deve especificar um shebang.
Thecarpy

4
Não, o que realmente era o ponto principal da resposta. Sua segunda frase é certamente verdadeira.
Michael Homer

você está certo, o bash executa usando-se, o shell padrão na maioria das minhas caixas é .... bash, a partir daí minha confusão. Eu só testado em Solaris 8 com ksh como shell padrão, com certeza, corre bater-lo como o bash ... aprendido algo novo hoje, graças ;-) (upvoted!)
thecarpy

Obrigado. A falha de execl()chamada acontece quando um script de shell não contém um shebang e é executado como scriptname? Isso não acontece quando um script de shell é executado como bash scriptname? Isso não acontece quando um script de shell contém um shebang e é executado como scriptname?
StackExchange for All


7

Como o arquivo não é de nenhum dos tipos de executáveis ​​reconhecidos pelo sistema e, assumindo que você tem permissão para executá-lo, a execve()chamada do sistema normalmente falhará com um erro ENOEXEC( não executável ).

O que acontece então depende da aplicação e / ou função da biblioteca usada para executar o comando.

Pode ser, por exemplo, um shell, a função execlp()/ execvp()libc.

A maioria dos outros aplicativos usará um deles quando executar um comando. Eles invocam um shell, por exemplo, por meio da system("command line")função libc, que normalmente invoca shpara analisar essa linha de comando (cujo caminho pode ser determinado em tempo de compilação (como /bin/shvs /usr/xpg4/bin/shno Solaris)), ou invoca o shell armazenado $SHELLpor eles mesmos, como vicom seu !comando ou com xterm -e 'command line'muitos outros comandos ( su user -cinvocará o shell de login do usuário em vez de $SHELL).

Geralmente, um arquivo de texto sem shebang que não inicia #é considerado como um shscript. Qual shé o que irá variar embora.

execlp()/ execvp(), ao execve()retornar ENOEXEC, normalmente o invocará sh. Para sistemas que possuem mais de um, shporque podem estar em conformidade com mais de um padrão, o que shnormalmente será determinado no momento da compilação (do aplicativo usando execvp()/ execlp()vinculando um blob diferente de código que se refere a um caminho diferente para sh). Por exemplo, no Solaris, isso será /usr/xpg4/bin/sh(um padrão, POSIX sh) ou /bin/sh(o shell Bourne (um shell antiquado) no Solaris 10 e mais antigo, ksh93 no Solaris 11).

Quando se trata de conchas, há muita variação. bash, AT&T ksh, o shell Bourne normalmente interpreta o próprio script (em um processo filho, a menos que execseja usado) depois de simular a execve(), que está desmarcada todas as variáveis ​​não exportadas, fechou todos os fds de execução imediata, removeu todas as traps personalizadas, aliases, funções ... ( bashinterpretará o script no shmodo). yashse executará (com sho argv[0]modo no shmodo) para interpretá-lo.

zsh, pdksh, ashConchas baseados irá tipicamente invocar sh(o caminho de que determinado no momento da compilação).

Para cshe tcsh(e o shde alguns BSDs iniciais), se o primeiro caractere do arquivo for #, eles se executarão para interpretá-lo e de shoutra forma. Isso remonta a um período pré-shebang, onde cshele reconheceu #como comentários, mas não o shell Bourne, então #havia uma dica de que era um script csh.

fish(pelo menos versão 2.4.0), apenas retorna um erro se execve()falhar (ele não tenta tratá-lo como um script).

Alguns shells (como a bashAT&T ksh) primeiro tentarão determinar heuristicamente se o arquivo provavelmente deve ser um script ou não. Portanto, você pode achar que alguns shells se recusam a executar um script se ele tiver um caractere NUL nos primeiros bytes.

Observe também que, se houver execve()falha no ENOEXEC, mas o arquivo tiver uma linha shebang, alguns shells tentarão interpretar essa linha shebang.

Então, alguns exemplos:

  • Quando $SHELLestiver /bin/bash, xterm -e 'myscript with args'terá myscriptinterpretado por bashno shmodo. Enquanto estiver com xterm -e myscript with args, xtermusará execvp()para que o script seja interpretado por sh.
  • su -c myscriptno Solaris 10, onde rooto shell de logon é /bin/she /bin/shé o shell Bourne terá sido myscriptinterpretado pelo shell Bourne.
  • /usr/xpg4/bin/awk 'BEGIN{system("myscript")'no Solaris 10, ele será interpretado por /usr/xpg4/bin/sh(o mesmo para /usr/xpg4/bin/env myscript).
  • find . -prune -exec myscript {} \;no Solaris 10 (usando execvp()), ele será interpretado por /bin/sheven with /usr/xpg4/bin/find, mesmo em um ambiente POSIX (um erro de conformidade).
  • csh -c myscriptterá interpretado por cshse começa com #, com o shcontrário.

Em suma, você não pode ter certeza de qual shell será usado para interpretar esse script se você não souber como e pelo que será chamado.

De qualquer forma, read -pé bashapenas uma sintaxe, portanto, você deve garantir que o script seja interpretado por bash(e evitar essa .shextensão enganosa ). Você conhece o caminho do bashexecutável e usa:

#! /path/to/bash -
read -p ...

Ou você pode tentar confiar na $PATHpesquisa do bashexecutável (supondo que bashesteja instalado) usando:

#! /usr/bin/env bash
read -p ...

( envé quase onipresente encontrado em /usr/bin). Como alternativa, você pode torná-lo compatível com POSIX + Bourne e, nesse caso, você pode usá-lo /bin/sh. Todos os sistemas terão a /bin/sh. Na maioria deles, é (na maior parte) compatível com POSIX, mas você ainda pode encontrar de vez em quando um shell Bourne lá.

#! /bin/sh -
printf >&2 'Enter a user name: '
read user
printf '%s\n' "$user"

1

Quando você não possui nenhuma linha #!(chamada shebang ), sh é usado. Para verificar isso, você pode executar o seguinte script.

ps -p $$
echo -n "The real shell is: "
realpath /proc/$$/exe

No meu computador eu recebo

  PID TTY          TIME CMD
13718 pts/16   00:00:00 sh
The real program is: /usr/bin/bash

mesmo que meu shell padrão seja zsh . Ele usa o bash, pois na minha máquina, o comando sh é implementado pelo bash .


11
Por que você recusou isso? Por favor, explique ou é inútil ...
rools

Aborrecedores (abaixadores silenciosos e anônimos) odiarão. Aprendi algo com sua resposta, então aqui está um voto positivo. :-)
Mac
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.