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?

Ma99uS
quelle
3
Ähnlich [Kann ein Bash-Skript sagen, in welchem ​​Verzeichnis es gespeichert ist?] (An stackoverflow.com/questions/59895/… )
Rodrigue
Informationen zum Verzeichnis finden Sie unter: Abrufen des Quellverzeichnisses eines Bash-Skripts von innen .
Kenorb

Antworten:

630
me=`basename "$0"`

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):

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

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.

Tanktalus
quelle
40
$ 0 gibt den Namen an, über den das Skript aufgerufen wurde, nicht den tatsächlichen Pfad der eigentlichen Skriptdatei.
Chris Conway
4
Es funktioniert nur, wenn Sie über Symlink angerufen werden. Aber selbst dann ist es normalerweise sowieso das, was Sie wollen, IME.
Tanktalus
78
Funktioniert nicht für Skripte, die nicht aufgerufen, sondern bezogen werden.
Charles Duffy
1
@ Charles Duffy: Siehe Dimitre Radoulovs Antwort unten .
Serge Wautier
8
-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 ------------- #
Bill Hernandez
quelle
11
@ NickC siehe Teilstring-Entfernung
Cychoi
1
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"
Dimitre Radoulov
quelle
24
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:

$(basename $BASH_SOURCE) 
Zainka
quelle
2
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.

Josh Lee
quelle
2
+1 für direkte Antwort + prägnant. Wenn Sie Symlink-Funktionen benötigen, empfehle ich Folgendes: Travis B. Hartwells Antwort.
Trevor Boyd Smith
26

Wenn Sie es ohne den Pfad wollen, würden Sie verwenden ${0##*/}

Herr Bisamratte
quelle
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).

Travis B. Hartwell
quelle
2
-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.

[Quelle: Bash-Handbuch]

gkb0986
quelle
2
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.

Jim Dodd
quelle
2
Sie haben einen sehr guten Punkt. Kein Randfall IMO. Es gibt jedoch eine Lösung: Siehe Dimitre Radoulovs Antwort oben
Serge Wautier
10

Da einige Kommentare nach dem Dateinamen ohne Erweiterung gefragt haben, ist hier ein Beispiel, wie dies erreicht werden kann:

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

Genießen!

Simon Mattes
quelle
9

Betreff: Tanktalus '(akzeptierte) Antwort oben, ein etwas saubererer Weg ist:

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

Wenn Ihr Skript aus einem anderen Bash-Skript stammt, können Sie Folgendes verwenden:

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

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.

Simon
quelle
1
Danke für die sourceSkriptnamen.
Fredrick Gauss
8

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

basename $0
VolkA
quelle
8

wenn Ihr Aufruf Shell-Skript wie

/home/mike/runme.sh

$ 0 ist der vollständige Name

 /home/mike/runme.sh

Basisname $ 0 erhält den Basisdateinamen

 runme.sh

und Sie müssen diesen grundlegenden Namen in eine Variable wie setzen

filename=$(basename $0)

und fügen Sie Ihren zusätzlichen Text hinzu

echo "You are running $filename"

so mögen deine skripte

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

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 =) ...

jcalfee314
quelle
3
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.

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

$0beantwortet die Frage nicht (so wie ich es verstehe). Eine Demonstration:

$ cat script.sh
#! / bin / sh
echo `basename $ 0`
$ ./script.sh 
script.sh
$ ln script.sh linktoscript
$ ./linktoscript 
Linktoskript

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.

Chris Conway
quelle
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/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
}

Prost

linxuser
quelle
1

Kurz, klar und einfach my_script.sh

#!/bin/bash

running_file_name=$(basename "$0")

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

Ausgabe:

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

Folgendes habe ich mir ausgedacht, inspiriert von Dimitre Radoulovs Antwort (die ich übrigens positiv bewertet habe ) .

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

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

unabhängig davon, wie Sie Ihr Skript aufrufen

. path/to/script.sh

oder

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

echo "Du hast $ 0"

mmacaulay
quelle
Ist kein Bash-Skript, ist voller Pfad.
E-Info128
-6

so etwas?

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
quelle
5
-1, beantwortet die Frage nicht (zeigt nicht, wie der Name des Skripts gefunden wird) und ist ein verwirrendes Beispiel in einem sehr fehlerhaften Skript.
Score_Under