Woher kenne ich den Namen der Skriptdatei in einem Bash-Skript?
605
Wie kann ich den Namen der Bash-Skriptdatei im Skript selbst ermitteln?
Wenn sich mein Skript in einer Datei befindet runme.sh, wie würde ich es dann schaffen, die Meldung "Sie führen runme.sh aus" anzuzeigen, ohne dies fest zu codieren?
Versuchen Sie Folgendes , um einen Symlink 1 zu lesen , der normalerweise nicht Ihren Wünschen entspricht (Sie möchten den Benutzer normalerweise nicht auf diese Weise verwirren):
IMO, das wird verwirrende Ausgabe erzeugen. "Ich habe foo.sh ausgeführt, aber es heißt, ich habe bar.sh ausgeführt!? Muss ein Fehler sein!" Außerdem besteht einer der Zwecke von Symlinks mit unterschiedlichen Namen darin, unterschiedliche Funktionen bereitzustellen, die auf dem Namen basieren, als den sie bezeichnet werden (denken Sie auf einigen Plattformen an gzip und gunzip).
1 Das heißt, um Symlinks so aufzulösen, dass Sie beim Ausführen des Benutzers, foo.shder tatsächlich ein Symlink ist bar.sh, den aufgelösten Namen bar.shanstelle von verwenden möchten foo.sh.
-1, 1. readlink bewegt sich nur einen Symlink tief, 2. $0im ersten Beispiel unterliegt die $0Wortaufteilung, 3. wird an den Basisnamen, den Readlink und das Echo in einer Position übergeben, die es ermöglicht, ihn als Befehlszeilenschalter zu behandeln . Ich schlage stattdessen me=$(basename -- "$0")oder viel effizienter auf Kosten der Lesbarkeit vor me=${0##*/}. Für Symlinks, me=$(basename -- "$(readlink -f -- "$0")")vorausgesetzt gnu utils, wird es sonst ein sehr langes Skript sein, das ich hier nicht schreiben werde.
Score_Under
246
# ------------- SCRIPT ------------- # # ------------- CALLED ------ ------- ##!/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
# Beachten Sie, dass in der nächsten Zeile das erste Argument in doppelten # und einfachen Anführungszeichen aufgerufen wird , da es zwei Wörter enthält$ / misc / shell_scripts / check_root / show_parms . sh "'hallo da'" "'william'"# ------------- ERGEBNISSE ------------- ## Argumente aufgerufen mit ---> 'Hallo da' 'William' # $ 1 ----------------------> 'Hallo da' # $ 2 ---- ------------------> 'william' # Pfad zu mir --------------> / misc / shell_scripts / check_root / show_parms. sh # übergeordneter Pfad -------------> / misc / shell_scripts / check_root # mein Name -----------------> show_parms.sh# ------------- ENDE ------------- #
Wie bekomme ich nur show_paramsden Namen ohne optionale Erweiterung?
Acumenus
Funktioniert nicht, wenn das Skript aus einem anderen Ordner aufgerufen wird. Der Pfad ist in der enthalten ${0##*/}. Getestet mit GitBash.
AlikElzin-Kilaka
1
Das obige hat bei einem .bash_login-Skript nicht funktioniert, aber die Dimitre Radoulov-Lösung von $ BASH_SOURCE funktioniert hervorragend.
John
@ John Du meinst doch .bash_profile ? Oder alternativ .bashrc ? Sie sollten jedoch wissen, dass es einige subtile Unterschiede gibt, wie sie aufgerufen / bezogen werden. Ich bin todmüde, aber wenn ich richtig denke, wäre das höchstwahrscheinlich das Problem. Ein wichtiger Punkt ist, wenn Sie sich remote über z. B. ssh oder auf dem lokalen System bei einem System anmelden.
Pryftan
193
Mit bash> = 3 funktioniert folgendes:
$ ./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"
Großartig! Das ist die Antwort, die sowohl für ./scrip.sh als auch für source ./script.sh
zhaorufei
4
Dies ist, was ich will, und es ist einfach, " dirname $BASE_SOURCE" zu verwenden, um das Verzeichnis zu erhalten, in dem sich die Skripte befinden.
Larry Cai
2
Ich habe diesen Unterschied fast auf die harte Tour gelernt, als ich ein selbstlöschendes Skript geschrieben habe. Zum Glück war 'rm' auf 'rm -i' ausgerichtet :)
kervin
sowieso zum. ./s um den Namen zu bekommen ./s statt Bash? Ich habe festgestellt, dass $ 1 nicht immer auf ./s ...
David Mokon Bond festgelegt ist
1
Es ist in BASH_SOURCE , nicht wahr ?
Dimitre Radoulov
69
$BASH_SOURCE gibt die richtige Antwort bei der Beschaffung des Skripts.
Dies schließt jedoch den Pfad ein, um nur den Dateinamen des Skripts zu erhalten. Verwenden Sie:
Diese Antwort ist meiner Meinung nach die beste, da die Lösung selbstdokumentierenden Code verwendet. $ BASH_SOURCE ist völlig verständlich, ohne eine Dokumentation zu lesen, während zB $ {0 ## * /} nicht
Andreas M. Oberheim
Diese Antwort ist meiner Meinung nach von größerem Wert, denn wenn wir so laufen . <filename> [arguments], $0würden wir den Namen der Anrufer-Shell angeben. Na zumindest unter OSX sicher.
Mihir
68
Wenn der Skriptname Leerzeichen enthält, ist es robuster, "$0"oder "$(basename "$0")"- oder unter MacOS : "$(basename \"$0\")". Dies verhindert, dass der Name in irgendeiner Weise entstellt oder interpretiert wird. Im Allgemeinen empfiehlt es sich, Variablennamen in der Shell immer in doppelte Anführungszeichen zu setzen.
Und was ist, wenn ich es ohne optionale Dateierweiterung möchte?
Acumenus
Um eine Erweiterung zu entfernen, können Sie "$ {VARIABLE% .ext}" versuchen, wobei VARIABLE der Wert ist, den Sie von $ {0 ## * /} erhalten haben, und ".ext" die Erweiterung ist, die Sie entfernen möchten.
BrianV
19
Um Chris Conway zu antworten , würden Sie (zumindest) unter Linux Folgendes tun:
echo $(basename $(readlink -nf $0))
readlink druckt den Wert eines symbolischen Links aus. Wenn es sich nicht um einen symbolischen Link handelt, wird der Dateiname gedruckt. -n weist es an, keine neue Zeile zu drucken. -f weist ihn an, dem Link vollständig zu folgen (wenn ein symbolischer Link ein Link zu einem anderen Link wäre, würde dieser ebenfalls aufgelöst).
-n ist harmlos, aber nicht notwendig, da die Struktur $ (...) es trimmt.
Zhaorufei
16
Ich habe festgestellt, dass diese Zeile immer funktioniert, unabhängig davon, ob die Datei bezogen oder als Skript ausgeführt wird.
echo "${BASH_SOURCE[${#BASH_SOURCE[@]} - 1]}"
Wenn Sie Symlinks folgen möchten, verwenden readlinkSie den oben angegebenen Pfad rekursiv oder nicht rekursiv.
Der Grund, warum der Einzeiler funktioniert, wird durch die Verwendung der BASH_SOURCEUmgebungsvariablen und ihrer Zuordnung erklärt FUNCNAME.
BASH_SOURCE
Eine Array-Variable, deren Mitglieder die Quelldateinamen sind, in denen die entsprechenden Shell-Funktionsnamen in der Array-Variablen FUNCNAME definiert sind. Die Shell-Funktion $ {FUNCNAME [$ i]} ist in der Datei $ {BASH_SOURCE [$ i]} definiert und wird von $ {BASH_SOURCE [$ i + 1]} aufgerufen.
FUNCNAME
Eine Array-Variable, die die Namen aller Shell-Funktionen enthält, die sich derzeit im Ausführungsaufrufstapel befinden. Das Element mit dem Index 0 ist der Name einer aktuell ausgeführten Shell-Funktion. Das unterste Element (das mit dem höchsten Index) ist "main". Diese Variable existiert nur, wenn eine Shell-Funktion ausgeführt wird. Zuweisungen zu FUNCNAME haben keine Auswirkung und geben einen Fehlerstatus zurück. Wenn FUNCNAME nicht gesetzt ist, verliert es seine besonderen Eigenschaften, auch wenn es anschließend zurückgesetzt wird.
Diese Variable kann mit BASH_LINENO und BASH_SOURCE verwendet werden. Jedes Element von FUNCNAME verfügt über entsprechende Elemente in BASH_LINENO und BASH_SOURCE, um den Aufrufstapel zu beschreiben. Zum Beispiel wurde $ {FUNCNAME [$ i]} aus der Datei $ {BASH_SOURCE [$ i + 1]} unter der Zeilennummer $ {BASH_LINENO [$ i]} aufgerufen. Der integrierte Anrufer zeigt anhand dieser Informationen den aktuellen Aufrufstapel an.
Es funktioniert, wenn Sie eine Datei aus einer interaktiven Sitzung beziehen a (vorausgesetzt a, der Inhalt stammt echo "${BASH_SOURCE[${#BASH_SOURCE[@]} - 1]}") - dann erhalten Sie aden Pfad. Aber wenn Sie ein Skript schreiben , bmit source adrin und laufen ./b, es wird zurückkehren b‚s Weg.
PSkocik
12
Diese Antworten sind für die angegebenen Fälle korrekt, es besteht jedoch weiterhin ein Problem, wenn Sie das Skript von einem anderen Skript mit dem Schlüsselwort 'source' ausführen (sodass es in derselben Shell ausgeführt wird). In diesem Fall erhalten Sie die $ 0 des aufrufenden Skripts. Und in diesem Fall glaube ich nicht, dass es möglich ist, den Namen des Skripts selbst zu erhalten.
Dies ist ein Randfall und sollte nicht zu ernst genommen werden. Wenn Sie das Skript direkt von einem anderen Skript aus ausführen (ohne 'Quelle'), funktioniert die Verwendung von $ 0.
Ich bin damit einverstanden, dass es verwirrend wäre, Symlinks zu dereferenzieren, wenn Ihr Ziel darin besteht, dem Benutzer Feedback zu geben, aber es gibt Fälle, in denen Sie den kanonischen Namen für ein Skript oder eine andere Datei benötigen, und dies ist der beste Weg, imo.
Sie können $ 0 verwenden, um Ihren Skriptnamen (mit vollständigem Pfad) zu bestimmen. Um nur den Skriptnamen zu erhalten, können Sie diese Variable mit kürzen
Dadurch werden symbolische Links aufgelöst (realpath erledigt das), Leerzeichen werden behandelt (doppelte Anführungszeichen tun dies) und der aktuelle Skriptname wird auch dann gefunden, wenn er von anderen Skripten bezogen (./Myscript) oder aufgerufen wird ($ BASH_SOURCE behandelt dies). Nach all dem ist es gut, dies in einer Umgebungsvariablen zur Wiederverwendung oder zum einfachen Kopieren an eine andere Stelle zu speichern (this =) ...
Zu realpathIhrer Information ist kein integrierter BASH-Befehl. Es ist eine eigenständige ausführbare Datei, die nur in bestimmten Distributionen verfügbar ist
StvnW
4
In bashkönnen Sie den Namen der Skriptdatei mit abrufen $0. Im Allgemeinen $1, $2etc. sind für den Zugriff CLI Argumente. Ebenso $0ist der Zugriff auf den Namen, der das Skript auslöst (Skriptdateiname).
#!/bin/bash
echo "You are running $0"......
Wenn Sie das Skript mit Pfad wie aufrufen /path/to/script.shdann $0auch den Dateinamen geben mit Pfad. In diesem Fall muss $(basename $0)nur der Name der Skriptdatei verwendet werden.
Wie kommt man ./linktoscriptzum Ausdrucken script.sh?
[EDIT] Per @ephemient in den obigen Kommentaren, obwohl die symbolische Verknüpfung erfunden zu sein scheint, ist es möglich, damit herumzuspielen $0, dass sie keine Dateisystemressource darstellt. Das OP ist etwas mehrdeutig in Bezug auf das, was er wollte.
Ich habe meiner obigen Antwort eine Antwort hinzugefügt, aber ich bin nicht der Meinung, dass dies wirklich erwünscht ist (oder dass Sie es wollen sollten, abgesehen von einigen mildernden Umständen, die ich derzeit nicht ergründen kann)
Tanktalus
2
Ich denke, dass das Drucken von Linktoscript anstelle von script.sh eine Funktion ist, kein Fehler. Einige Unix-Befehle verwenden ihren Namen, um ihr Verhalten zu ändern. Ein Beispiel ist vi / ex / view.
Mouviciel
@Tanktalus: Es gibt Fälle, in denen sich das Verhalten basierend auf dem tatsächlichen Speicherort der Datei ändern soll. In einem früheren Projekt hatte ich ein Skript "Aktiver Zweig", das mehrere Tools aus dem Zweig, in dem ich mich befand, mit dem Pfad verknüpft arbeiten an; Diese Tools könnten dann herausfinden, in welches Verzeichnis sie eingecheckt wurden, um sie mit den richtigen Dateien auszuführen.
Andrew Aylett
2
Info danke an Bill Hernandez. Ich habe einige Einstellungen hinzugefügt, die ich übernehme.
#!/bin/bashfunctionUsage(){
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
}
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
fiif[ $ans = n -o $ans = N -o $ans = no -o $ans =No-o $ans = NO ]then
echo "'no'"fireturn $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 Unknownif[-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 rentalcase $val in"trash") start_trash ;;"help") start_help ;;"www") firefox $homepage ;;*) echo "Sorry, I can not get a $val for you!";;esac# Case stop
-1, beantwortet die Frage nicht (zeigt nicht, wie der Name des Skripts gefunden wird) und ist ein verwirrendes Beispiel in einem sehr fehlerhaften Skript.
Antworten:
Versuchen Sie Folgendes , um einen Symlink 1 zu lesen , der normalerweise nicht Ihren Wünschen entspricht (Sie möchten den Benutzer normalerweise nicht auf diese Weise verwirren):
IMO, das wird verwirrende Ausgabe erzeugen. "Ich habe foo.sh ausgeführt, aber es heißt, ich habe bar.sh ausgeführt!? Muss ein Fehler sein!" Außerdem besteht einer der Zwecke von Symlinks mit unterschiedlichen Namen darin, unterschiedliche Funktionen bereitzustellen, die auf dem Namen basieren, als den sie bezeichnet werden (denken Sie auf einigen Plattformen an gzip und gunzip).
1 Das heißt, um Symlinks so aufzulösen, dass Sie beim Ausführen des Benutzers,
foo.sh
der tatsächlich ein Symlink istbar.sh
, den aufgelösten Namenbar.sh
anstelle von verwenden möchtenfoo.sh
.quelle
$0
im ersten Beispiel unterliegt die$0
Wortaufteilung, 3. wird an den Basisnamen, den Readlink und das Echo in einer Position übergeben, die es ermöglicht, ihn als Befehlszeilenschalter zu behandeln . Ich schlage stattdessenme=$(basename -- "$0")
oder viel effizienter auf Kosten der Lesbarkeit vorme=${0##*/}
. Für Symlinks,me=$(basename -- "$(readlink -f -- "$0")")
vorausgesetzt gnu utils, wird es sonst ein sehr langes Skript sein, das ich hier nicht schreiben werde.quelle
show_params
den Namen ohne optionale Erweiterung?${0##*/}
. Getestet mit GitBash.Mit bash> = 3 funktioniert folgendes:
quelle
dirname $BASE_SOURCE
" zu verwenden, um das Verzeichnis zu erhalten, in dem sich die Skripte befinden.$BASH_SOURCE
gibt die richtige Antwort bei der Beschaffung des Skripts.Dies schließt jedoch den Pfad ein, um nur den Dateinamen des Skripts zu erhalten. Verwenden Sie:
quelle
. <filename> [arguments]
,$0
würden wir den Namen der Anrufer-Shell angeben. Na zumindest unter OSX sicher.Wenn der Skriptname Leerzeichen enthält, ist es robuster,
"$0"
oder"$(basename "$0")"
- oder unter MacOS :"$(basename \"$0\")"
. Dies verhindert, dass der Name in irgendeiner Weise entstellt oder interpretiert wird. Im Allgemeinen empfiehlt es sich, Variablennamen in der Shell immer in doppelte Anführungszeichen zu setzen.quelle
Wenn Sie es ohne den Pfad wollen, würden Sie verwenden
${0##*/}
quelle
Um Chris Conway zu antworten , würden Sie (zumindest) unter Linux Folgendes tun:
readlink druckt den Wert eines symbolischen Links aus. Wenn es sich nicht um einen symbolischen Link handelt, wird der Dateiname gedruckt. -n weist es an, keine neue Zeile zu drucken. -f weist ihn an, dem Link vollständig zu folgen (wenn ein symbolischer Link ein Link zu einem anderen Link wäre, würde dieser ebenfalls aufgelöst).
quelle
Ich habe festgestellt, dass diese Zeile immer funktioniert, unabhängig davon, ob die Datei bezogen oder als Skript ausgeführt wird.
Wenn Sie Symlinks folgen möchten, verwenden
readlink
Sie den oben angegebenen Pfad rekursiv oder nicht rekursiv.Der Grund, warum der Einzeiler funktioniert, wird durch die Verwendung der
BASH_SOURCE
Umgebungsvariablen und ihrer Zuordnung erklärtFUNCNAME
.[Quelle: Bash-Handbuch]
quelle
a
(vorausgesetzta
, der Inhalt stammtecho "${BASH_SOURCE[${#BASH_SOURCE[@]} - 1]}"
) - dann erhalten Siea
den Pfad. Aber wenn Sie ein Skript schreiben ,b
mitsource a
drin und laufen./b
, es wird zurückkehrenb
‚s Weg.Diese Antworten sind für die angegebenen Fälle korrekt, es besteht jedoch weiterhin ein Problem, wenn Sie das Skript von einem anderen Skript mit dem Schlüsselwort 'source' ausführen (sodass es in derselben Shell ausgeführt wird). In diesem Fall erhalten Sie die $ 0 des aufrufenden Skripts. Und in diesem Fall glaube ich nicht, dass es möglich ist, den Namen des Skripts selbst zu erhalten.
Dies ist ein Randfall und sollte nicht zu ernst genommen werden. Wenn Sie das Skript direkt von einem anderen Skript aus ausführen (ohne 'Quelle'), funktioniert die Verwendung von $ 0.
quelle
Da einige Kommentare nach dem Dateinamen ohne Erweiterung gefragt haben, ist hier ein Beispiel, wie dies erreicht werden kann:
Genießen!
quelle
Betreff: Tanktalus '(akzeptierte) Antwort oben, ein etwas saubererer Weg ist:
Wenn Ihr Skript aus einem anderen Bash-Skript stammt, können Sie Folgendes verwenden:
Ich bin damit einverstanden, dass es verwirrend wäre, Symlinks zu dereferenzieren, wenn Ihr Ziel darin besteht, dem Benutzer Feedback zu geben, aber es gibt Fälle, in denen Sie den kanonischen Namen für ein Skript oder eine andere Datei benötigen, und dies ist der beste Weg, imo.
quelle
source
Skriptnamen.Sie können $ 0 verwenden, um Ihren Skriptnamen (mit vollständigem Pfad) zu bestimmen. Um nur den Skriptnamen zu erhalten, können Sie diese Variable mit kürzen
quelle
wenn Ihr Aufruf Shell-Skript wie
$ 0 ist der vollständige Name
Basisname $ 0 erhält den Basisdateinamen
und Sie müssen diesen grundlegenden Namen in eine Variable wie setzen
und fügen Sie Ihren zusätzlichen Text hinzu
so mögen deine skripte
quelle
Dadurch werden symbolische Links aufgelöst (realpath erledigt das), Leerzeichen werden behandelt (doppelte Anführungszeichen tun dies) und der aktuelle Skriptname wird auch dann gefunden, wenn er von anderen Skripten bezogen (./Myscript) oder aufgerufen wird ($ BASH_SOURCE behandelt dies). Nach all dem ist es gut, dies in einer Umgebungsvariablen zur Wiederverwendung oder zum einfachen Kopieren an eine andere Stelle zu speichern (this =) ...
quelle
realpath
Ihrer Information ist kein integrierter BASH-Befehl. Es ist eine eigenständige ausführbare Datei, die nur in bestimmten Distributionen verfügbar istIn
bash
können Sie den Namen der Skriptdatei mit abrufen$0
. Im Allgemeinen$1
,$2
etc. sind für den Zugriff CLI Argumente. Ebenso$0
ist der Zugriff auf den Namen, der das Skript auslöst (Skriptdateiname).Wenn Sie das Skript mit Pfad wie aufrufen
/path/to/script.sh
dann$0
auch den Dateinamen geben mit Pfad. In diesem Fall muss$(basename $0)
nur der Name der Skriptdatei verwendet werden.quelle
quelle
$0
beantwortet die Frage nicht (so wie ich es verstehe). Eine Demonstration:Wie kommt man
./linktoscript
zum Ausdruckenscript.sh
?[EDIT] Per @ephemient in den obigen Kommentaren, obwohl die symbolische Verknüpfung erfunden zu sein scheint, ist es möglich, damit herumzuspielen
$0
, dass sie keine Dateisystemressource darstellt. Das OP ist etwas mehrdeutig in Bezug auf das, was er wollte.quelle
Info danke an Bill Hernandez. Ich habe einige Einstellungen hinzugefügt, die ich übernehme.
Prost
quelle
Kurz, klar und einfach
my_script.sh
Ausgabe:
quelle
Ich habe das Obige aus einer anderen Frage zum Stapelüberlauf erhalten. Kann ein Bash-Skript feststellen, in welchem Verzeichnis es gespeichert ist? , aber ich denke, es ist auch für dieses Thema nützlich.
quelle
Folgendes habe ich mir ausgedacht, inspiriert von Dimitre Radoulovs Antwort (die ich übrigens positiv bewertet habe ) .
unabhängig davon, wie Sie Ihr Skript aufrufen
oder
quelle
echo "Du hast $ 0"
quelle
so etwas?
quelle