Existe alguma diferença entre “.” e “source” no bash, afinal de contas?

29

Eu estava procurando a diferença entre o "." e comandos "fonte" embutidos e algumas fontes (por exemplo, em esta discussão, e a bash página do manual) sugerem que são exatamente os mesmos.

No entanto, seguindo um problema com variáveis de ambiente, realizei um teste. Eu criei um arquivo testenv.sh que contém:

#!/bin/bash
echo $MY_VAR

No prompt de comando, executei o seguinte:

> chmod +x testenv.sh
> MY_VAR=12345
> ./testenv.sh

> source testenv.sh
12345
> MY_VAR=12345 ./testenv.sh
12345

[observe que o primeiro formulário retornou uma string vazia]

Portanto, este pequeno experimento sugere que é uma diferença afinal, onde para o comando "source", o ambiente filho herda todas as variáveis do pai pai, onde o "." isso não acontece.

Estou faltando alguma coisa, ou este é um recurso não documentado / preterido de bash ?

[GNU bash, versão 4.1.5 (1) -release (x86_64-pc-linux-gnu)]

    
por ysap 30.08.2012 / 03:07

2 respostas

54

Resposta curta

Em sua pergunta, o segundo comando não usa nem o . shell embutido nem o source interno. Em vez disso, você está realmente executando o script em um shell separado, invocando-o pelo nome (como qualquer outro arquivo executável). Isso fornece um conjunto separado de variáveis de ambiente (embora, se você exportar uma variável de ambiente em seu shell pai, ele seja incluído). Se você alterar o / para um espaço, ele será executado com o . integrado, o que equivale a source .

Explicação estendida

Esta é a sintaxe do shell source embutido, que executa o conteúdo de um script no shell atual (e, portanto, com as variáveis de ambiente do shell atual):

source testenv.sh

Esta é a sintaxe do . embutido, que faz a mesma coisa que source :

. testenv.sh

No entanto, essa sintaxe executa o script como um arquivo executável, iniciando um novo shell para executá-lo:

./testenv.sh

Isso não está usando o . incorporado. Em vez disso, . faz parte do caminho para o arquivo que você está executando. De um modo geral, você pode executar qualquer arquivo executável em um shell chamando-o com um nome que contenha pelo menos um caractere / . Para executar um arquivo no diretório atual, precedendo-o por ./ é, portanto, a maneira mais fácil. A menos que o diretório atual esteja no seu PATH , você não poderá executar o script com o comando testenv.sh . Isso evita que as pessoas executem acidentalmente arquivos no diretório atual quando pretendem executar um comando do sistema ou algum outro arquivo existente em algum diretório listado na variável de ambiente PATH .

Como a execução de um arquivo por nome (em vez de usar source ou . ) o executa em um novo shell, ele terá seu próprio conjunto de variáveis de ambiente. As variáveis de ambiente herdam das variáveis de ambiente do processo de chamada (que, neste caso, é seu shell interativo). No entanto, para que uma variável de ambiente seja passada para o novo shell, um dos seguintes itens deve ser o caso:

  • A variável de ambiente foi exportada. Use o shell export embutido para isso. No seu exemplo, você pode usar export MY_VAR=12345 para definir e exportar a variável em uma etapa ou, se já estiver definida, você pode simplesmente usar export MY_VAR .

  • A variável de ambiente é explicitamente configurada e passada para o comando que você está executando. Isso geralmente faz isso:

    MY_VAR=12345 ./testenv.sh
    

./ Sintaxe para Scripts Requer uma Linha Hashbang para Funcionar (Corretamente)

A propósito, por favor note que, quando você invoca um executável pelo nome como acima (e não com o . ou source shell built-ins), o programa shell é usado para executá-lo é não normalmente determinado por qual shell você está usando. Em vez disso:

  • Para arquivos binários, o kernel pode ser configurado para executar arquivos desse tipo específico. Ele examina os dois primeiros bytes do arquivo para um "número mágico" que indica que tipo de executável binário é. É assim que os binários executáveis podem ser executados.

    Obviamente, isso é extremamente importante, porque um script não pode ser executado sem um shell ou outro interpretador, que é um binário executável! Além disso, muitos comandos e aplicativos são compilados em binários em vez de scripts.

    ( #! é a representação de texto do "número mágico" indicando um executável de texto.)

  • Para arquivos que devem ser executados em um shell ou outra linguagem interpretada, a primeira linha se parece com:

    #!/bin/sh
    

    /bin/sh pode ser substituído por qualquer outro shell ou intérprete destinado a executar o programa. Por exemplo, um programa em Python pode começar com a linha:

    #!/usr/bin/python
    

    Essas linhas são chamadas de hashbang, shebang e vários outros nomes semelhantes. Veja esta entrada do FOLDOC , esta Artigo da Wikipédia e É #! / Bin / sh lido pelo intérprete para mais informações.

  • Se um arquivo de texto for marcado como executável e você executá-lo a partir do seu shell (como ./filename ), mas não começa com #! , o kernel falha ao executá-lo. No entanto, vendo que isso aconteceu, seu shell irá tentar executá-lo passando seu nome para algum shell. Existem poucos requisitos colocados no que shell que é ( "o shell deve executar um comando equivalente a ter um shell invocado ..." ). Na prática , alguns shells - incluindo bash * - executam outra instância deles mesmos, enquanto outros usam /bin/sh .É altamente recomendável que você evite isso e use uma hashbang line (ou execute o script passando-o ao interpretador desejado, por exemplo, bash filename ).

    * manual do GNU Bash , 3.7.2 Comando de pesquisa e execução : "Se esta execução falha porque o arquivo não está no formato executável, e o arquivo não é um diretório, ele é considerado um script de shell e o shell o executa conforme descrito em Shell Scripts . "

por Eliah Kagan 30.08.2012 / 03:52
13

Sim, você está perdendo alguma coisa.

Eu acho que você está confundindo o '.' isso significa diretório atual, como em ./testenv.sh e o '.' isso significa source (que é um comando interno). Então, no caso quando '. significa que source seria . ./testenv.sh . Faz sentido?

Então tente isto:

MY_VAR=12345 
. ./testenv.sh
    
por user1477 30.08.2012 / 03:43