Qual é a diferença entre "source x", ". x "e" ./x "no Bash?

11

Eu tenho uma fonte bash da run.shseguinte forma,

#!/bin/bash
if [ $# -ne 1 ]; then
    exit
fi
...

quando executo de duas maneiras, há comportamentos diferentes. A primeira maneira é,

source run.sh

Ele fechará o terminal após a execução. A segunda maneira é,

./run.sh

isso simplesmente terminará de executar o script e permanecerá no terminal. Estou perguntando se existe um comando para sair de scripts bash para ambos source run.she ./run.shexecução. Eu tentei returntambém, o que não funciona bem em ./run.shexecução.

De um modo mais geral, estou interessado em saber por que isso está acontecendo e qual a diferença entre usar "fonte" e "." para execução de script?

Richard
fonte

Respostas:

16

Antes de responder, acho que são necessários alguns esclarecimentos. Vamos analisar as três linhas a seguir:

source run.sh
. run.sh
./run.sh

As duas primeiras linhas são exatamente idênticas: .é de fato um apelido para source. O que sourcefaz é executar o script do shell no contexto atual; portanto, uma chamada para exitencerrará o shell.

A terceira linha (que é a que o confunde) não tem nada a ver com as outras linhas. ./run.shé apenas um caminho e é o mesmo que (por exemplo) /home/user/run.shou /usr/bin/something. Lembre-se sempre de que os comandos no shell são separados por um espaço. Portanto, neste caso, o comando não é ., mas é ./run.sh: isso significa que um sub shell será executado e que exitterá efeito apenas no sub shell.

Andrea Corbellini
fonte
5

Três caminhos:

Você pode colocar o script em uma função e usar apenas return.

#!/usr/bin/env bash
main() {
    ...
    return 1
    ...
}
main "$@"

Você pode testar se o script está sendo originado por um shell interativo.

if [[ $- = *i* ]]; then
    return 1
else
    exit 1
fi

Você pode tentar retornar e, se falhar, sair.

return 1 2>/dev/null || exit 1
Geirha
fonte
Alguma dica de como funciona o encantamento mágico $- = *i* ?
Deadbeef404
@ deadbeef404 O parâmetro especial -contém os sinalizadores de opção atualmente ativos. O teste verifica se o -isinalizador está ativo. Veja gnu.org/software/bash/manual/html_node/Special-Parameters.html
geirha
1

Pense no comando 'source' como na instrução 'include'. Ele pega o conteúdo do argumento e o executa como se tivesse sido executado diretamente. Nesse caso, seu comando é 'source' com um argumento 'run.sh' e run.sh é executado exatamente como se você tivesse digitado o conteúdo de run.sh na linha de comando.

Quando você executa './run.sh', './run.sh' é seu comando e não possui argumentos. Como esse arquivo é de texto sem formatação e não binário, seu shell procura um intérprete no shebang ('#!' Na primeira linha) e encontra '/ bin / bash'. Portanto, seu shell inicia uma nova instância do bash e o conteúdo do run.sh é executado dentro dessa nova instância.

Na primeira instância, quando o bash atinge o comando 'exit', ele é executado exatamente como se você o tivesse digitado na linha de comando. Nos segundos casos, ele é executado no processo do bash, seu shell foi iniciado, portanto, apenas essa instância do bash recebe um comando 'exit'.

Quando você digita uma linha no bash, qualquer coisa antes do primeiro espaço é tratada como um comando e qualquer coisa a seguir é tratada como argumento. O comando '.' é um alias de 'source'. Quando você corre '. run.sh 'o'. ' é um comando por si só, pois é separado dos argumentos por um espaço. Quando você executa './run.sh', seu comando é './run.sh' e '.' faz parte do caminho relativo para run.sh com o '.' representando sua pasta atual.

smokes2345
fonte
Se você é um programador de C / C ++ que deseja melhorar com os scripts de shell / bash, esta é a resposta perfeita.
Justin