Diferentes maneiras de executar um script de shell

44

Existem várias maneiras de executar um script, os que eu sei são:

/path/to/script # using the path (absolute or relative)
. script        # using the . (dot)
source script   # using the `source` command

São mais disso? Quais são as diferenças entre eles? Existem situações em que devo usar um e não outro?

phunehehe
fonte
É bom saber, graças às suas perguntas e respostas abaixo, especialmente ao Shawn's. Eu gostaria de adicionar algo que não era muito aparente para mim até que eu fiz alguns testes. A inclusão de um "/" na segunda maneira acima levará o comando ao modo 1 acima. Ou seja, enquanto "./myscript.sh" segue o modo 1, ". Myscript.sh" adere ao modo 2. Você mencionou "usando o caminho (absoluto ou relativo)", mas apenas queria tornar isso aparente.
Arun

Respostas:

32

Outra maneira é chamar o intérprete e passar o caminho para o script:

/bin/sh /path/to/script

O ponto e a fonte são equivalentes. (EDIT: não, não são: como KeithB aponta em um comentário em outra resposta, "." Só funciona em shells relacionados ao bash, onde "source" funciona nos shells relacionados ao bash e csh.) Ele executa o script em -place (como se você tivesse copiado e colado o script ali). Isso significa que quaisquer funções e variáveis ​​não locais no script permanecem. Isso também significa que, se o script gravar um CD em um diretório, você ainda estará lá quando terminar.

As outras maneiras de executar um script o executarão em seu próprio subshell. Variáveis ​​no script ainda não estão ativas quando terminar. Se o script alterou os diretórios, não afetou o ambiente de chamada.

/ path / to / script e / bin / sh script são ligeiramente diferentes. Normalmente, um script tem um "shebang" no começo que se parece com isso:

#! /bin/bash

Este é o caminho para o interpretador de scripts. Se ele especificar um intérprete diferente do que quando você o executa, ele pode se comportar de maneira diferente (ou pode não funcionar).

Por exemplo, scripts Perl e scripts Ruby começam com (respectivamente):

#! /bin/perl

e

#! /bin/ruby

Se você executar um desses scripts executando /bin/sh script, eles não funcionarão.

Na verdade, o Ubuntu não usa o shell bash, mas um muito semelhante chamado dash. Os scripts que requerem bash podem funcionar um pouco errados quando chamados, /bin/sh scriptporque você acabou de chamar um script bash usando o interpretador de traços.

Outra pequena diferença entre chamar o script diretamente e passar o caminho do script para o intérprete é que o script deve ser marcado como executável para executá-lo diretamente, mas não para executá-lo passando o caminho para o intérprete.

Outra variação menor: você pode prefixar qualquer uma dessas maneiras de executar um script com eval, para poder ter

eval sh script
eval script
eval . script

e assim por diante. Na verdade, não muda nada, mas pensei em incluí-lo por completo.

Shawn J. Goff
fonte
6
Dizer "o Ubuntu realmente não usa o shell bash" é impreciso e tecnicamente incorreto. Ubuntu faz utilizar o shell bash, o ponto é que shcorresponde a dash, mas não para bash.
Faheem Mitha
@ Shawn Nos primeiros parágrafos, você escreveu "Ele executa o script no local (como se você tivesse copiado e colado o script ali mesmo). Isso significa que quaisquer funções e variáveis ​​não locais no script permanecem." o que você quer dizer com a segunda linha aqui? Você pode explicar, por favor.
6126 Geek
@ Quando você executa um script como um processo filho (da maneira normal), todas as variáveis ​​e funções que ele define (seu ambiente) desaparecem quando o processo termina. Se você originar o script, essas variáveis ​​e funções serão criadas no ambiente atual; Quando o script termina, as alterações no ambiente permanecem.
perfil completo de Shawn J. Goff
@ ShawnJ.Goff obrigado pelo esclarecimento. +1.
77712 Geek
Torção de plotagem: Bourne Shell (sh) aceita apenas pontos - não fonte, pois é um bash-builtin. pubs.opengroup.org/onlinepubs/9699919799/utilities/… Então, eu diria que a maneira mais portátil é um ponto.
Dez /
9

A maioria das pessoas depura scripts shell adicionando os seguintes sinalizadores de depuração ao script:

set -x     # Print command traces before executing command.
set -v     # Prints shell input lines as they are read.
set -xv    # Or do both

Mas isso significa que você precisa abrir o arquivo com um editor (supondo que você tenha permissões para editar o arquivo), adicionar uma linha como set -x, salvar o arquivo e executar o arquivo. Quando terminar, você precisará seguir os mesmos passos e remover os set -xetc. etc. Isso pode ser entediante.

Em vez de fazer tudo isso, você pode definir os sinalizadores de depuração na linha de comando:

$ bash -x ~/bin/ducks
+ du -cks -x dir1 dir2 dir3 file1 file2 file3
+ sort -n
+ tail .ducks
123 etc
424 bin
796 total



$ sh -xv ~/bin/ducks  
#!/usr/bin/env bash

# Find the disk hog
# Borrowed from http://oreilly.com/pub/h/15
...
...
Stefan Lasiewski
fonte
2
Uma dica relacionada: eu adquiri o hábito de colocar emulate sh 2>/dev/nullno topo dos meus scripts de shell. Quando executado com zsh, isso o coloca no modo compatível com POSIX. Quando executado com outras conchas, a linha não tem efeito. Então eu posso executar o script com zsh -x /path/to/script. Eu gosto do zsh aqui porque fornece melhores traços do que o bash ou o ksh.
Gilles 'SO- stop be evil'
7

Shawn J. Goff fez muitos pontos positivos, mas não incluiu toda a história:

Na verdade, o Ubuntu não usa o shell bash, mas um muito semelhante chamado dash. Os scripts que exigem o bash podem funcionar um pouco errados quando chamados por meio de /bin/shscript, porque você acabou de chamar um script do bash usando o interpretador de traços.

Muitos scripts do sistema (como no init.d, no / etc e assim por diante) têm um shebang #!/bin/sh, mas /bin/shna verdade são um link simbólico para outro shell - nos tempos antigos /bin/bash, atualmente /bin/dash. Mas quando um deles é chamado como /bin/sh, ele se comporta de maneira diferente, ou seja, adere ao modo de compatibilidade com POSIX.

Como eles fazem isso? Bem, eles inspecionam como foram invocados.

O próprio shellscript pode testar como foi chamado e fazer coisas diferentes, dependendo disso? Sim pode. Portanto, a maneira como você o invoca sempre pode levar a resultados diferentes, mas é claro que raramente é feito para incomodá-lo. :)

Como regra geral: se você estiver aprendendo um shell específico, como o bash, e escrever comandos em um tutorial do bash, coloque #!/bin/basho título, não #!/bin/sh, exceto onde indicado em contrário. Caso contrário, seus comandos podem falhar. E se você não tiver escrito um script, invoque-o diretamente ( ./foo.sh, bar/foo.sh) em vez de adivinhar um shell ( sh foo.sh, sh bar/foo.sh). O shebang deve chamar a concha certa.

E aqui estão dois outros tipos de invocação:

cat foo.sh | dash
dash < foo.sh
Usuário desconhecido
fonte
5

.e sourcesão equivalentes, pois não geram um subprocesso, mas executam comandos no shell atual. Isso é importante quando o script define variáveis ​​de ambiente ou altera o diretório de trabalho atual.

Usar o caminho ou atribuir a ele /bin/shcria um novo processo no qual os comandos são executados.

mouviciel
fonte
2
sh script
bash script

Estou pensando se há mais ...

.e sourcesão os mesmos. Após a execução, quaisquer alterações no ambiente scriptserão mantidas. Normalmente, ele seria usado para originar uma biblioteca Bash, para que a biblioteca possa ser reutilizada em muitos scripts diferentes.

Também é uma boa maneira de manter o diretório atual. Se você alterar o diretório no script, ele não será aplicado no shell em que você executa esse script. Mas se você o originar para executá-lo, após a saída do script, o diretório atual será mantido.

livibetter
fonte
2
.só funciona em sh / bash e shells relacionados. sourcetambém funciona em csh e shells relacionados.
KeithB
1

" Userland exec " conta como uma maneira diferente? O Userland exec carrega o código e o executa sem o uso de uma chamada de sistema execve ().

Bruce Ediger
fonte
1
. ./filename
# ( dot space dot slash filename )

Executa o script no shell atual quando o diretório não está no caminho.

Stefan
fonte
1

. e source são um pouco diferentes no zsh pelo menos (é isso que eu uso) porque

source file

Funciona, enquanto

. file

não precisa

. ./file
bollovan
fonte