escolhendo entre $ 0 e BASH_SOURCE

118

Como se escolhe entre "$0"e"${BASH_SOURCE[0]}"

Esta descrição do GNU não me ajudou muito.

    BASH_SOURCE

 An array variable whose members are the source filenames where the
 corresponding shell function names in the FUNCNAME array variable are
 defined. The shell function ${FUNCNAME[$i]} is defined in the file
 ${BASH_SOURCE[$i]} and called from ${BASH_SOURCE[$i+1]}
H2ONaCl
fonte
BASH_SOURCEfoi adicionado em bash-3.0-alpha. Você pode não ter, dependendo do seu regime de teste. Descobri que ele estava faltando no Solaris anterior e no OS X. Veja também return: só pode `retornar 'de uma função ou script originado em U & L.SE.
jww 05 de

Respostas:

174

Observação: para uma solução compatível com POSIX, veja esta resposta .

${BASH_SOURCE[0]}(ou, mais simplesmente, $BASH_SOURCE[1] ) contém o caminho (potencialmente relativo) do script que o contém em todos os cenários de invocação, notavelmente também quando o script é originado , o que não é verdadeiro para $0.

Além disso, como Charles Duffy aponta, $0pode ser definido como um valor arbitrário pelo chamador.
Por outro lado, $BASH_SOURCE pode estar vazio, se nenhum arquivo nomeado estiver envolvido; por exemplo:
echo 'echo "[$BASH_SOURCE]"' | bash

O exemplo a seguir ilustra isso:

Script foo:

#!/bin/bash
echo "[$0] vs. [${BASH_SOURCE[0]}]"

$ bash ./foo
[./foo] vs. [./foo]

$ ./foo
[./foo] vs. [./foo]

$ . ./foo
[bash] vs. [./foo]

$0faz parte da especificação do shell POSIX, enquanto BASH_SOURCE, como o nome sugere, é específico do Bash.


[1] Leitura opcional: ${BASH_SOURCE[0]}vs$BASH_SOURCE .:

O Bash permite que você faça referência a um elemento 0de uma variável de array usando notação escalar : em vez de escrever ${arr[0]}, você pode escrever $arr; em outras palavras: se você referenciar a variável como se fosse um escalar , você obtém o elemento no índice 0.

O uso desse recurso obscurece o fato de que $arré uma matriz, e é por isso que o popular código de shell linter shellcheck.net emite o seguinte aviso (no momento desta redação):

SC2128: Expandir uma matriz sem um índice fornece apenas o primeiro elemento.

Em uma observação lateral: embora este aviso seja útil, pode ser mais preciso, porque você não obterá necessariamente o primeiro elemento: é especificamente o elemento no índice 0que é retornado, portanto, se o primeiro elemento tiver um índice mais alto - que é possível no Bash - você obterá a string vazia; tente 'a[1]='hi'; echo "$a"'.
(Em contraste, zsh, sempre o renegado, na verdade, faz retornar o primeiro elemento, independentemente do seu índice).

Você pode optar por evitar esse recurso devido à sua obscuridade, mas ele funciona de forma previsível e, pragmaticamente falando, você raramente, ou nunca, precisará acessar outros índices além 0da variável de array ${BASH_SOURCE[@]}.

mklement0
fonte
então $ BASH_SOURCE é mais genérico e funciona em mais circunstâncias?
Alexander Mills
2
@AlexanderMills Sim, se você estiver usando o Bash, $BASH_SOURCEé a melhor escolha.
mklement0
18

Esses scripts podem ajudar a ilustrar. O script externo chama o script do meio, que chama o script interno:

$ cat outer.sh
#!/usr/bin/env bash
./middle.sh
$ cat middle.sh
#!/usr/bin/env bash
./inner.sh
$ cat inner.sh
#!/usr/bin/env bash
echo "\$0 = '$0'"
echo "\${BASH_SOURCE[0]} = '${BASH_SOURCE[0]}'"
echo "\${BASH_SOURCE[1]} = '${BASH_SOURCE[1]}'"
echo "\${BASH_SOURCE[2]} = '${BASH_SOURCE[2]}'"
$ ./outer.sh
$0 = './inner.sh'
$BASH_SOURCE[0] = './inner.sh'
$BASH_SOURCE[1] = ''
$BASH_SOURCE[2] = ''

No entanto, se alterarmos as chamadas de script para sourceinstruções:

$ cat outer.sh
#!/usr/bin/env bash
source ./middle.sh
$ cat middle.sh
#!/usr/bin/env bash
source ./inner.sh
$ cat inner.sh
#!/usr/bin/env bash
echo "\$0 = '$0'"
echo "\${BASH_SOURCE[0]} = '${BASH_SOURCE[0]}'"
echo "\${BASH_SOURCE[1]} = '${BASH_SOURCE[1]}'"
echo "\${BASH_SOURCE[2]} = '${BASH_SOURCE[2]}'"
$ ./outer.sh
$0 = './outer.sh'
$BASH_SOURCE[0] = './inner.sh'
$BASH_SOURCE[1] = './middle.sh'
$BASH_SOURCE[2] = './outer.sh'
Huw Walters
fonte
1

Para portabilidade, use ${BASH_SOURCE[0]}quando estiver definido e de $0outra forma. Isso dá

${BASH_SOURCE[0]:-$0}

Notavelmente, em dizer zsh, o $ 0 contém o caminho de arquivo correto, mesmo se o script for sourced.

usuário7610
fonte