web-development-kb-pt.site

Diferentes maneiras de executar um script Shell

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?

44
phunehehe

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. Scripts que requerem bash podem funcionar um pouco errados quando chamados, fazendo /bin/sh script porque 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.

32
Shawn J. Goff

A maioria das pessoas depura scripts do Shell adicionando o seguinte 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), adicionando uma linha como set -x, salve o arquivo e execute-o. Então, quando terminar, siga as mesmas etapas e remova o set -x, etc. 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
...
...
9
Stefan Lasiewski

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. Scripts que requerem bash podem funcionar um pouco errados quando chamados, fazendo /bin/sh script porque você acabou de chamar um script 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/sh é de fato um link simbólico para outro Shell - em épocas anteriores /bin/bash, hoje em dia /bin/dash. Mas quando um deles é chamado como /bin/sh, eles se comportam de maneira diferente, ou seja, aderem 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/bash no 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 invocar o Shell certo.

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

cat foo.sh | dash
dash < foo.sh
7
user unknown

. e source sã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.

Usando o caminho ou dando a /bin/sh cria um novo processo no qual os comandos são executados.

5
mouviciel
sh script
bash script

Estou pensando se há mais ...

. e source são iguais. Após a execução, quaisquer alterações de ambiente em script serã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.

2
livibetter

. 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
1
bollovan
. ./filename
# ( dot space dot slash filename )

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

1
jrh_enginnering

" serland 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 ().

1
Bruce Ediger