Comment connaître le nom du fichier de script dans un script Bash?

606

Comment puis-je déterminer le nom du fichier de script Bash à l'intérieur du script lui-même?

Comme si mon script est dans un fichier runme.sh, comment puis-je faire pour afficher le message "Vous exécutez runme.sh" sans coder en dur cela?

Ma99uS
la source
3
Similaire [Un script bash peut-il indiquer dans quel répertoire il est stocké?] (À stackoverflow.com/questions/59895/… )
Rodrigue

Réponses:

631
me=`basename "$0"`

Pour lire un lien symbolique 1 , qui n'est généralement pas ce que vous voulez (vous ne voulez généralement pas confondre l'utilisateur de cette façon), essayez:

me="$(basename "$(test -L "$0" && readlink "$0" || echo "$0")")"

OMI, cela produira une sortie déroutante. "J'ai lancé foo.sh, mais ça veut dire que je lance bar.sh!? Ça doit être un bug!" En outre, l'un des buts d'avoir des liens symboliques différemment nommés est de fournir des fonctionnalités différentes en fonction du nom sous lequel il est appelé (pensez à gzip et gunzip sur certaines plates-formes).


1 C'est-à-dire, pour résoudre les liens symboliques de telle sorte que lorsque l'utilisateur exécute foo.shqui est en fait un lien symbolique vers bar.sh, vous souhaitez utiliser le nom résolu bar.shplutôt que foo.sh.

Tanktalus
la source
40
$ 0 vous donne le nom par lequel le script a été invoqué, pas le vrai chemin du fichier de script réel.
Chris Conway
4
Cela fonctionne, sauf si vous êtes appelé via un lien symbolique. Mais, même alors, c'est généralement ce que vous voulez de toute façon, IME.
Tanktalus
78
Ne fonctionne pas pour les scripts d'origine plutôt qu'invoqués.
Charles Duffy
1
@Charles Duffy: Voir la réponse de Dimitre Radoulov ci-dessous .
Serge Wautier
8
-1, 1. readlink ne parcourra qu'un seul lien symbolique en profondeur, 2. $0dans le premier exemple est sujet à la séparation des mots, 3. $0est passé au nom de base, readlink et écho dans une position qui lui permet d'être traité comme un commutateur de ligne de commande . Je suggère plutôt me=$(basename -- "$0")ou beaucoup plus efficace au détriment de la lisibilité, me=${0##*/}. Pour les liens symboliques, en me=$(basename -- "$(readlink -f -- "$0")")supposant les utils gnu, sinon ce sera un script très long que je n'écrirai pas ici.
Score_Under
246
# ------------- SCRIPT ------------- # # ------------- APPELÉ ------ ------- #

#!/bin/bash

echo
echo "# arguments called with ---->  ${@}     "
echo "# \$1 ---------------------->  $1       "
echo "# \$2 ---------------------->  $2       "
echo "# path to me --------------->  ${0}     "
echo "# parent path -------------->  ${0%/*}  "
echo "# my name ------------------>  ${0##*/} "
echo
exit



# Remarquez sur la ligne suivante, le premier argument est appelé entre guillemets doubles, # et simples, car il contient deux mots


$   / misc / shell_scripts / check_root / show_parms . sh "'bonjour là" " "' william '" 

# ------------- RÉSULTATS ------------- #

# arguments appelés avec ---> 'bonjour là' 'william' # $ 1 ----------------------> 'bonjour là' # $ 2 ---- ------------------> 'william' # chemin vers moi --------------> / misc / shell_scripts / check_root / show_parms. sh # parent path -------------> / misc / shell_scripts / check_root # my name -----------------> show_parms.sh






# ------------- FIN ------------- #
Bill Hernandez
la source
11
@NickC voir Suppression de sous
chaîne
1
Comment puis-je obtenir juste show_params, c'est-à-dire le nom sans extension facultative?
Acumenus
Ne fonctionne pas si le script est appelé à partir d'un autre dossier. Le chemin est inclus dans le ${0##*/}. Testé à l'aide de GitBash.
AlikElzin-kilaka du
1
Ce qui précède n'a pas fonctionné pour moi à partir d'un script .bash_login, mais la solution Dimitre Radoulov de $ BASH_SOURCE fonctionne très bien.
John
@John Vous voulez sûrement dire .bash_profile ? Ou alternativement .bashrc ? Vous devez savoir cependant qu'il existe des différences subtiles dans la façon dont elles sont invoquées / obtenues. Je suis fatigué, mais si je pense bien, ce serait probablement le problème. Il est important de savoir si vous vous connectez à un système à distance, par exemple via ssh par rapport au système local.
Pryftan
193

Avec bash> = 3 les travaux suivants:

$ ./s
0 is: ./s
BASH_SOURCE is: ./s
$ . ./s
0 is: bash
BASH_SOURCE is: ./s

$ cat s
#!/bin/bash

printf '$0 is: %s\n$BASH_SOURCE is: %s\n' "$0" "$BASH_SOURCE"
Dimitre Radoulov
la source
24
Génial! C'est la réponse qui fonctionne à la fois pour ./scrip.sh et pour la source ./script.sh
zhaorufei
4
C'est ce que je veux, et c'est facilement utiliser " dirname $BASE_SOURCE" pour obtenir le répertoire que les scripts ont localisé.
Larry Cai
2
J'ai presque appris cette différence à la dure lors de l'écriture d'un script à suppression automatique. Heureusement, 'rm' était alias en 'rm -i' :)
kervin
de toute façon au. ./s pour obtenir le nom ./s au lieu de bash? J'ai trouvé que 1 $ n'est pas toujours réglé sur ./s ...
David Mokon Bond
1
C'est dans BASH_SOURCE , n'est-ce pas?
Dimitre Radoulov
69

$BASH_SOURCE donne la bonne réponse lors de la recherche du script.

Cependant, cela inclut le chemin d'accès pour obtenir uniquement le nom de fichier des scripts, utilisez:

$(basename $BASH_SOURCE) 
Zainka
la source
2
Cette réponse est à mon humble avis la meilleure parce que la solution utilise du code auto-documenté. $ BASH_SOURCE est totalement compréhensible sans lire aucune documentation alors que par exemple $ {0 ## * /} ne l'est pas
Andreas M. Oberheim
Cette réponse a plus de valeur si je crois parce que si nous courons comme . <filename> [arguments], $0donnerait le nom du shell de l'appelant. Bien au moins sur OSX, c'est sûr.
Mihir
68

Si le nom du script comporte des espaces, d' une manière plus robuste est d'utiliser "$0"ou "$(basename "$0")"- ou sur MacOS: "$(basename \"$0\")". Cela empêche le nom d'être altéré ou interprété de quelque manière que ce soit. En général, il est recommandé de toujours mettre des noms de variable entre guillemets dans le shell.

Josh Lee
la source
2
+1 pour la réponse directe + concis. si vous avez besoin de fonctionnalités de lien symbolique, je suggère de voir: la réponse de Travis B. Hartwell.
Trevor Boyd Smith
26

Si vous le souhaitez sans le chemin, vous utiliserez ${0##*/}

M. Muskrat
la source
Et si je le veux sans extension de fichier optionnelle?
Acumenus
Pour supprimer une extension, vous pouvez essayer "$ {VARIABLE% .ext}" où VARIABLE est la valeur que vous avez obtenue de $ {0 ## * /} et ".ext" est l'extension que vous souhaitez supprimer.
BrianV
19

Pour répondre à Chris Conway , sur Linux (au moins), vous feriez ceci:

echo $(basename $(readlink -nf $0))

readlink imprime la valeur d'un lien symbolique. S'il ne s'agit pas d'un lien symbolique, il imprime le nom du fichier. -n lui dit de ne pas imprimer de nouvelle ligne. -f lui dit de suivre complètement le lien (si un lien symbolique était un lien vers un autre lien, il le résoudrait également).

Travis B. Hartwell
la source
2
-n est inoffensif mais pas nécessaire car la structure $ (...) le coupera.
zhaorufei
16

J'ai trouvé que cette ligne fonctionnait toujours, que le fichier soit d'origine ou exécuté en tant que script.

echo "${BASH_SOURCE[${#BASH_SOURCE[@]} - 1]}"

Si vous souhaitez suivre l'utilisation des liens symboliques readlinksur le chemin que vous obtenez ci-dessus, récursivement ou non récursivement.

La raison pour laquelle le one-liner fonctionne s'explique par l'utilisation de la BASH_SOURCEvariable d'environnement et de son associé FUNCNAME.

BASH_SOURCE

Variable de tableau dont les membres sont les noms de fichiers source où les noms de fonction shell correspondants dans la variable de tableau FUNCNAME sont définis. La fonction shell $ {FUNCNAME [$ i]} est définie dans le fichier $ {BASH_SOURCE [$ i]} et appelée depuis $ {BASH_SOURCE [$ i + 1]}.

FUNCNAME

Une variable de tableau contenant les noms de toutes les fonctions shell actuellement dans la pile des appels d'exécution. L'élément d'index 0 est le nom de toute fonction shell en cours d'exécution. L'élément le plus bas (celui dont l'indice est le plus élevé) est "principal". Cette variable n'existe que lorsqu'une fonction shell est en cours d'exécution. Les affectations à FUNCNAME n'ont aucun effet et renvoient un état d'erreur. Si FUNCNAME n'est pas défini, il perd ses propriétés spéciales, même s'il est ensuite réinitialisé.

Cette variable peut être utilisée avec BASH_LINENO et BASH_SOURCE. Chaque élément de FUNCNAME a des éléments correspondants dans BASH_LINENO et BASH_SOURCE pour décrire la pile d'appels. Par exemple, $ {FUNCNAME [$ i]} a été appelé à partir du fichier $ {BASH_SOURCE [$ i + 1]} au numéro de ligne $ {BASH_LINENO [$ i]}. La fonction intégrée de l'appelant affiche la pile d'appels actuelle à l'aide de ces informations.

[Source: manuel Bash]

gkb0986
la source
2
Cela fonctionne si vous sourcez dans un fichier a (supposons que ale contenu est echo "${BASH_SOURCE[${#BASH_SOURCE[@]} - 1]}") à partir d'une session interactive - alors il vous donnera ale chemin. Mais si vous écrivez un script bavec source aet que vous l'exécutez ./b, il retournera ble chemin d'accès.
PSkocik
12

Ces réponses sont correctes pour les cas qu'elles indiquent mais il y a toujours un problème si vous exécutez le script à partir d'un autre script en utilisant le mot-clé 'source' (afin qu'il s'exécute dans le même shell). Dans ce cas, vous obtenez le $ 0 du script appelant. Et dans ce cas, je ne pense pas qu'il soit possible d'obtenir le nom du script lui-même.

Il s'agit d'un cas de bord et ne doit pas être pris trop au sérieux. Si vous exécutez le script à partir d'un autre script directement (sans «source»), l'utilisation de $ 0 fonctionnera.

Jim Dodd
la source
2
tu as un très bon point. Pas un cas de bord IMO. Il existe cependant une solution: voir la réponse de Dimitre Radoulov ci
Serge Wautier
10

Étant donné que certains commentaires portaient sur le nom de fichier sans extension, voici un exemple comment y parvenir:

FileName=${0##*/}
FileNameWithoutExtension=${FileName%.*}

Prendre plaisir!

Simon Mattes
la source
9

Re: Réponse de Tanktalus (acceptée) ci-dessus, une manière légèrement plus propre consiste à utiliser:

me=$(readlink --canonicalize --no-newline $0)

Si votre script provient d'un autre script bash, vous pouvez utiliser:

me=$(readlink --canonicalize --no-newline $BASH_SOURCE)

Je conviens qu'il serait déroutant de déréférencer les liens symboliques si votre objectif est de fournir des commentaires à l'utilisateur, mais il y a des occasions où vous avez besoin d'obtenir le nom canonique d'un script ou d'un autre fichier, et c'est la meilleure façon, imo.

Simon
la source
1
Merci pour les sourcenoms de script d.
Fredrick Gauss
8

Vous pouvez utiliser $ 0 pour déterminer le nom de votre script (avec le chemin complet) - pour obtenir le nom du script uniquement, vous pouvez couper cette variable avec

basename $0
VolkA
la source
8

si votre script shell d'invocation comme

/home/mike/runme.sh

$ 0 est le nom complet

 /home/mike/runme.sh

nom de base $ 0 obtiendra le nom du fichier de base

 runme.sh

et vous devez mettre ce nom de base dans une variable comme

filename=$(basename $0)

et ajoutez votre texte supplémentaire

echo "You are running $filename"

donc vos scripts comme

/home/mike/runme.sh
#!/bin/bash 
filename=$(basename $0)
echo "You are running $filename"
LawrenceLi
la source
7
this="$(dirname "$(realpath "$BASH_SOURCE")")"

Cela résout les liens symboliques (realpath fait cela), gère les espaces (les guillemets doubles font cela) et trouvera le nom du script actuel même lorsqu'il est source (. ./Myscript) ou appelé par d'autres scripts ($ BASH_SOURCE gère cela). Après tout cela, il est bon de l'enregistrer dans une variable d'environnement pour une réutilisation ou pour une copie facile ailleurs (this =) ...

jcalfee314
la source
3
FYI realpathn'est pas une commande BASH intégrée. Il s'agit d'un exécutable autonome qui n'est disponible que dans certaines distributions
StvnW
4

Dans bashvous pouvez obtenir le nom du fichier de script en utilisant $0. Généralement $1, $2etc sont pour accéder aux arguments CLI. Il en va de même $0pour accéder au nom qui déclenche le script (nom du fichier script).

#!/bin/bash
echo "You are running $0"
...
...

Si vous invoquez le script avec path comme /path/to/script.shalors, vous $0obtiendrez également le nom de fichier avec path. Dans ce cas, vous devez utiliser $(basename $0)pour obtenir uniquement le nom du fichier de script.

rashok
la source
3
echo "$(basename "`test -L ${BASH_SOURCE[0]} \
                   && readlink ${BASH_SOURCE[0]} \
                   || echo ${BASH_SOURCE[0]}`")"
ecwpz91
la source
2

$0ne répond pas à la question (si je comprends bien). Une démonstration:

$ cat script.sh
#! / bin / sh
echo `nom de base $ 0`
$ ./script.sh 
script.sh
$ ln script.sh linktoscript
$ ./linktoscript 
linktoscript

Comment ./linktoscriptimprimer script.sh?

[EDIT] Par @ephemient dans les commentaires ci-dessus, bien que le lien symbolique puisse sembler artificiel, il est possible de jouer avec $0tel qu'il ne représente pas une ressource de système de fichiers. L'OP est un peu ambigu sur ce qu'il voulait.

Chris Conway
la source
a ajouté une réponse à ma réponse ci-dessus, mais je ne suis pas d'accord pour dire que c'est ce qui est vraiment souhaité (ou que vous devriez le vouloir, sauf circonstances atténuantes que je ne peux pas actuellement comprendre)
Tanktalus
2
Je pense que l'impression de linktoscript au lieu de script.sh est une fonctionnalité, pas un bug. Plusieurs commandes Unix utilisent leur nom pour modifier leur comportement. Un exemple est vi / ex / view.
mouviciel
@Tanktalus: Il y a des cas où vous voulez que le comportement change en fonction de l'emplacement réel du fichier - sur un projet précédent, j'avais un script "Active branch", qui créerait un lien symbolique vers le chemin d'accès à plusieurs outils de la branche où j'étais travaille sur; ces outils pourraient alors découvrir dans quel répertoire ils avaient été extraits pour s'exécuter sur les bons fichiers.
Andrew Aylett
2

Info grâce à Bill Hernandez. J'ai ajouté quelques préférences que j'adopte.

#!/bin/bash
function Usage(){
    echo " Usage: show_parameters [ arg1 ][ arg2 ]"
}
[[ ${#2} -eq 0 ]] && Usage || {
    echo
    echo "# arguments called with ---->  ${@}     "
    echo "# \$1 ----------------------->  $1       "
    echo "# \$2 ----------------------->  $2       "
    echo "# path to me --------------->  ${0}     " | sed "s/$USER/\$USER/g"
    echo "# parent path -------------->  ${0%/*}  " | sed "s/$USER/\$USER/g"
    echo "# my name ------------------>  ${0##*/} "
    echo
}

À votre santé

linxuser
la source
1

Court, clair et simple, en my_script.sh

#!/bin/bash

running_file_name=$(basename "$0")

echo "You are running '$running_file_name' file."

Production:

./my_script.sh
You are running 'my_script.sh' file.
Bơ Loong A Nhứi
la source
0

Voici ce que je suis venu avec, inspiré par Dimitre Radoulov réponse de (que j'upvoted, par la voie) .

script="$BASH_SOURCE"
[ -z "$BASH_SOURCE" ] && script="$0"

echo "Called $script with $# argument(s)"

quelle que soit la façon dont vous appelez votre script

. path/to/script.sh

ou

./path/to/script.sh
Salathiel Genèse
la source
-1

echo "Vous exécutez $ 0"

mmacaulay
la source
N'est pas un script bash, c'est le chemin complet.
e-info128
-6

quelque chose comme ça?

export LC_ALL=en_US.UTF-8
#!/bin/bash
#!/bin/sh

#----------------------------------------------------------------------
start_trash(){
ver="htrash.sh v0.0.4"
$TRASH_DIR  # url to trash $MY_USER
$TRASH_SIZE # Show Trash Folder Size

echo "Would you like to empty Trash  [y/n]?"
read ans
if [ $ans = y -o $ans = Y -o $ans = yes -o $ans = Yes -o $ans = YES ]
then
echo "'yes'"
cd $TRASH_DIR && $EMPTY_TRASH
fi
if [ $ans = n -o $ans = N -o $ans = no -o $ans = No -o $ans = NO ]
then
echo "'no'"
fi
 return $TRUE
} 
#-----------------------------------------------------------------------

start_help(){
echo "HELP COMMANDS-----------------------------"
echo "htest www                 open a homepage "
echo "htest trash               empty trash     "
 return $TRUE
} #end Help
#-----------------------------------------------#

homepage=""

return $TRUE
} #end cpdebtemp

# -Case start
# if no command line arg given
# set val to Unknown
if [ -z $1 ]
then
  val="*** Unknown  ***"
elif [ -n $1 ]
then
# otherwise make first arg as val
  val=$1
fi
# use case statement to make decision for rental
case $val in
   "trash") start_trash ;;
   "help") start_help ;;
   "www") firefox $homepage ;;
   *) echo "Sorry, I can not get a $val   for you!";;
esac
# Case stop
hynt
la source
5
-1, ne répond pas à la question (ne montre pas comment trouver le nom du script), et est un exemple déroutant dans un script très bogué.
Score_Under