choisir entre 0 $ et BASH_SOURCE

118

Comment choisir entre "$0"et"${BASH_SOURCE[0]}"

Cette description de GNU ne m'a pas beaucoup aidé.

    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
la source
BASH_SOURCEa été ajouté à bash-3.0-alpha. Vous ne l'avez peut-être pas, selon votre régime de test. Je l'ai trouvé manquant à la fois sur les premiers Solaris et OS X. Voir aussi return: ne peut «retourner» qu'à partir d'une fonction ou d'un script source sur U & L.SE.
jww

Réponses:

174

Remarque: pour une solution compatible POSIX, consultez cette réponse .

${BASH_SOURCE[0]}(ou, plus simplement, $BASH_SOURCE[1] ) contient le chemin (potentiellement relatif) du script contenant dans tous les scénarios d'appel, notamment également lorsque le script est source , ce qui n'est pas vrai pour $0.

De plus, comme le souligne Charles Duffy , $0peut être défini sur une valeur arbitraire par l'appelant.
D'un autre côté, $BASH_SOURCE peut être vide, si aucun fichier nommé n'est impliqué; par exemple:
echo 'echo "[$BASH_SOURCE]"' | bash

L'exemple suivant illustre ceci:

Script foo:

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

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

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

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

$0fait partie de la spécification du shell POSIX, alors que BASH_SOURCE, comme son nom l'indique, est spécifique à Bash.


[1] Lecture facultative: ${BASH_SOURCE[0]}vs$BASH_SOURCE .:

Bash vous permet de référencer un élément 0d'une variable de tableau en utilisant la notation scalaire : au lieu d'écrire ${arr[0]}, vous pouvez écrire $arr; en d'autres termes: si vous référencez la variable comme s'il s'agissait d'un scalaire , vous obtenez l'élément à l'index 0.

L'utilisation de cette fonctionnalité masque le fait qu'il $arrs'agit d'un tableau, c'est pourquoi le populaire shell-code linter shellcheck.net émet l'avertissement suivant (au moment d'écrire ces lignes ):

SC2128: L'extension d'un tableau sans index ne donne que le premier élément.

Sur une note latérale: bien que cet avertissement soit utile, il pourrait être plus précis, car vous n'obtiendrez pas nécessairement le premier élément: c'est spécifiquement l'élément à l'index 0qui est retourné, donc si le premier élément a un index plus élevé - qui est possible dans Bash - vous obtiendrez la chaîne vide; essayez 'a[1]='hi'; echo "$a"'.
(En revanche, zshjamais le renégat, en effet ne retourne le premier élément, quel que soit son indice).

Vous pouvez choisir d'éviter cette fonctionnalité en raison de son obscurité, mais elle fonctionne de manière prévisible et, de manière pragmatique, vous aurez rarement, voire jamais, besoin d'accéder à des indices autres que ceux 0de la variable de tableau ${BASH_SOURCE[@]}.

mklement0
la source
donc $ BASH_SOURCE est plus générique et fonctionne dans plus de circonstances?
Alexander Mills
2
@AlexanderMills Oui, si vous utilisez Bash, $BASH_SOURCEc'est le meilleur choix.
mklement0
18

Ces scripts peuvent aider à illustrer. Le script externe appelle le script du milieu, qui appelle le script interne:

$ 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] = ''

Cependant, si nous modifions les appels de script aux sourceinstructions:

$ 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
la source
1

Pour la portabilité, utilisez ${BASH_SOURCE[0]}quand il est défini, et $0autrement. Ça donne

${BASH_SOURCE[0]:-$0}

Notamment, disons zsh, le $ 0 contient le chemin de fichier correct même si le script est sourced.

user7610
la source