real - bash script path




Abrufen des Quellverzeichnisses eines Bash-Skripts von innen (20)

Wie bekomme ich den Pfad des Verzeichnisses, in dem sich ein Bash Skript befindet, innerhalb dieses Skripts?

Angenommen, ich möchte ein Bash-Skript als Startprogramm für eine andere Anwendung verwenden. Ich möchte das Arbeitsverzeichnis in das Verzeichnis ändern, in dem sich das Bash-Skript befindet, sodass ich die Dateien in diesem Verzeichnis wie folgt bearbeiten kann:

$ ./application

Dadurch wird das aktuelle Arbeitsverzeichnis unter Mac OS X 10.6.6 abgerufen:

DIR=$(cd "$(dirname "$0")"; pwd)

Das sollte es tun:

DIR=$(dirname "$(readlink -f "$0")")

Funktioniert mit Symlinks und Leerzeichen im Pfad. Siehe Manpages für Verzeichnisname und Readlink .

Bearbeiten:

Aus dem Kommentartitel scheint es nicht mit Mac OS zu funktionieren. Ich habe keine Ahnung, warum das so ist. Irgendwelche Vorschläge?


Dies ist Linux-spezifisch, Sie könnten jedoch Folgendes verwenden:

SELF=$(readlink /proc/$$/fd/255)

Eine leichte Überarbeitung der Lösung e-satis und 3bcdnlklvc04a wies in ihrer Antwort darauf hin

SCRIPT_DIR=''
pushd "$(dirname "$(readlink -f "$BASH_SOURCE")")" > /dev/null && {
    SCRIPT_DIR="$PWD"
    popd > /dev/null
}    

Dies sollte in allen aufgeführten Fällen weiterhin funktionieren.

BEARBEITEN: Popd nach fehlgeschlagenem Pushd dank konsolebox verhindern


Hier ist der einfache und korrekte Weg:

actual_path=$(readlink -f "${BASH_SOURCE[0]}")
script_dir=$(dirname "$actual_path")

Erläuterung:

  • ${BASH_SOURCE[0]} - der vollständige Pfad zum Skript. Der Wert von this ist auch dann korrekt, wenn das Skript als source <(echo 'echo $0') wird. Beispielsweise gibt source <(echo 'echo $0') bash aus , während es durch ${BASH_SOURCE[0]} wird, um den gesamten Pfad des Skripts ${BASH_SOURCE[0]} . (Dies setzt natürlich voraus, dass Sie eine Abhängigkeit von Bash haben.)

  • readlink -f - readlink -f alle readlink -f im angegebenen Pfad rekursiv auf. Dies ist eine GNU-Erweiterung und nicht auf (zum Beispiel) BSD-Systemen verfügbar. Wenn Sie einen Mac verwenden, können Sie GNU coreutils mit Homebrew installieren und durch greadlink -f .

  • Und natürlich erhält dirname das übergeordnete Verzeichnis des Pfads.


Hier ist ein POSIX-kompatibler One-Liner:

SCRIPT_PATH=`dirname "$0"`; SCRIPT_PATH=`eval "cd \"$SCRIPT_PATH\" && pwd"`

# test
echo $SCRIPT_PATH

Ich habe jeden ausprobiert und keiner hat funktioniert. Einer war sehr nahe, hatte aber einen winzigen Fehler, durch den er schwer beschädigt wurde. Sie vergaßen, den Pfad in Anführungszeichen zu setzen.

Viele Leute gehen auch davon aus, dass Sie das Skript von einer Shell aus ausführen. Vergessen Sie also nicht, wenn Sie ein neues Skript öffnen, das standardmäßig in Ihrem Haus gespeichert ist.

Versuchen Sie dieses Verzeichnis für Größe:

/ var / Niemand / Gedanke / Informationen zu Räumen / In einem Verzeichnis / Name / Und hier ist Ihre Datei.text

Dies macht es richtig, egal wie oder wo Sie es ausführen.

#!/bin/bash
echo "pwd: `pwd`"
echo "\$0: $0"
echo "basename: `basename "$0"`"
echo "dirname: `dirname "$0"`"

Um es wirklich nützlich zu machen, gehen Sie wie folgt vor, um zum Verzeichnis des laufenden Skripts zu wechseln:

cd "`dirname "$0"`"

hoffentlich hilft das


Ich habe viele der Antworten verglichen und ein paar kompaktere Lösungen gefunden. Diese scheinen alle verrückten Fälle zu handhaben, die sich aus Ihrer Lieblingskombination ergeben:

  • Absolute Pfade oder relative Pfade
  • Softlinks für Dateien und Verzeichnisse
  • Aufruf als script , bash script , bash -c script , source script oder . script . script
  • Leerzeichen, Tabulatoren, Zeilenumbrüche, Unicode usw. in Verzeichnissen und / oder Dateinamen
  • Dateinamen, die mit einem Bindestrich beginnen

Wenn Sie unter Linux laufen, scheint die Verwendung des proc Handle die beste Lösung zu sein, um die vollständig aufgelöste Quelle des aktuell ausgeführten Skripts zu ermitteln (in einer interaktiven Sitzung zeigt der Link auf das entsprechende /dev/pts/X ):

resolved="$(readlink /proc/$$/fd/255 && echo X)" && resolved="${resolved%$'\nX'}"

Das hat ein wenig Häßlichkeit, aber die Lösung ist kompakt und leicht verständlich. Wir verwenden nicht nur Bash- readlink , aber ich bin damit readlink , weil readlink die Aufgabe erheblich vereinfacht. Das echo X fügt am Ende der Variablenzeichenfolge ein echo X hinzu, damit nachgestellte Leerzeichen im Dateinamen nicht belegt werden und die Parametersubstitution ${VAR%X} am Ende der Zeile das X . Da readlink eine eigene neue readlink hinzufügt (die normalerweise in der Befehlsersetzung gegessen würde, wenn es nicht für unsere vorherigen Tricksereien wäre), müssen wir diese auch beseitigen. Dies lässt sich am einfachsten mit dem Zitierungsschema $'' , das es erlaubt, Escape-Sequenzen wie \n zur Darstellung von Zeilenumbrüchen zu verwenden (so können Sie auch leicht benannte Verzeichnisse und Dateien erstellen).

Das obige sollte Ihre Anforderungen zum Suchen des aktuell ausgeführten Skripts unter Linux abdecken. Wenn Sie jedoch das proc Dateisystem nicht zur Verfügung haben oder wenn Sie versuchen, den vollständig aufgelösten Pfad einer anderen Datei zu finden, dann ' Ich finde den Code unten hilfreich. Es ist nur eine geringfügige Modifikation des obigen Einliners. Wenn Sie mit seltsamen Verzeichnis- / Dateinamen readlink ist das Überprüfen der Ausgabe sowohl mit ls als auch mit readlink informativ, da ls "vereinfachte" Pfade ausgibt ? für Dinge wie Zeilenumbrüche.

absolute_path=$(readlink -e -- "${BASH_SOURCE[0]}" && echo x) && absolute_path=${absolute_path%?x}
dir=$(dirname -- "$absolute_path" && echo x) && dir=${dir%?x}
file=$(basename -- "$absolute_path" && echo x) && file=${file%?x}

ls -l -- "$dir/$file"
printf '$absolute_path: "%s"\n' "$absolute_path"

Kurze Antwort:

`dirname $0`

oder ( preferably ):

$(dirname "$0")

Sie können $ BASH_SOURCE verwenden

#!/bin/bash

scriptdir=`dirname "$BASH_SOURCE"`

Beachten Sie, dass Sie #! / Bin / bash und nicht #! / Bin / sh verwenden müssen, da es sich um eine bash-Erweiterung handelt


Verwende dirname "$0" :

#!/bin/bash
echo "The script you are running has basename `basename "$0"`, dirname `dirname "$0"`"
echo "The present working directory is `pwd`"

Die Verwendung von pwd allein funktioniert nicht, wenn Sie das Skript nicht in dem Verzeichnis ausführen, in dem es enthalten ist.

[[email protected] ~]$ pwd
/home/matt
[[email protected] ~]$ ./test2.sh
The script you are running has basename test2.sh, dirname .
The present working directory is /home/matt
[[email protected] ~]$ cd /tmp
[[email protected] tmp]$ ~/test2.sh
The script you are running has basename test2.sh, dirname /home/matt
The present working directory is /tmp

$_ ist eine Alternative zu $ ​​0. Wenn Sie ein Skript von bash ausführen, kann die akzeptierte Antwort folgendermaßen verkürzt werden:

DIR="$( dirname "$_" )"

Beachten Sie, dass dies die erste Anweisung in Ihrem Skript sein muss.


Das funktioniert in bash-3.2:

path="$( dirname "$( which "$0" )" )"

Hier ist ein Beispiel für seine Verwendung:

Angenommen, Sie haben ein ~ / bin- Verzeichnis, das sich in Ihrem $ PATH befindet . In diesem Verzeichnis befindet sich das Skript A. Es Quelle s Skript ~ / bin / lib / B . Sie wissen, wo sich das enthaltene Skript relativ zum ursprünglichen (das Unterverzeichnis lib ) befindet, nicht aber, wo es sich auf das aktuelle Verzeichnis des Benutzers bezieht.

Dies wird durch Folgendes (in A ) gelöst :

source "$( dirname "$( which "$0" )" )/lib/B"

Es spielt keine Rolle, wo sich der Benutzer befindet oder wie er das Skript aufruft, dies funktioniert immer.


Die beste kompakte Lösung wäre aus meiner Sicht:

"$( cd "$( echo "${BASH_SOURCE[0]%/*}" )"; pwd )"

Man kann sich nicht auf etwas anderes als Bash verlassen. Die Verwendung von dirname, readlinkund basenamewird schließlich zu Kompatibilitätsproblemen führen, so dass sie am besten vermieden werden , wenn überhaupt möglich.


Nur so habe ich verlässlich sagen können:

SCRIPT_DIR=$(dirname $(cd "$(dirname "$BASH_SOURCE")"; pwd))

Versuchen Sie die folgende Cross-kompatible Lösung:

CWD="$(cd -P -- "$(dirname -- "$0")" && pwd -P)"

as realpathoder readlink-Befehle sind nicht immer verfügbar (je nach Betriebssystem) und ${BASH_SOURCE[0]}sind nur in der Bash-Shell verfügbar.

Alternativ können Sie folgende Funktion in bash ausprobieren:

realpath () {
  [[ $1 = /* ]] && echo "$1" || echo "$PWD/${1#./}"
}

Diese Funktion benötigt 1 Argument. Wenn das Argument bereits einen absoluten Pfad hat, drucken Sie es so, wie es ist, andernfalls drucken Sie das $PWDArgument Variable (ohne Dateinamen ./).

Verbunden:


Hmm, wenn im Pfad basename & dirname einfach nicht geschnitten wird und der Pfad zu laufen ist hart (was wäre, wenn Eltern nicht PATH exportieren würden!) Die Shell muss jedoch ein offenes Handle für ihr Skript haben, und in bash ist das Handle # 255.

SELF=`readlink /proc/$$/fd/255`

funktioniert bei mir.


#!/bin/sh
PRG="$0"

# need this for relative symlinks
while [ -h "$PRG" ] ; do
   PRG=`readlink "$PRG"`
done

scriptdir=`dirname "$PRG"`

DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )"

ist ein nützlicher Einzeiler, der Ihnen den vollständigen Verzeichnisnamen des Skripts gibt, unabhängig davon, von wo aus es aufgerufen wird.

Es funktioniert, solange die letzte Komponente des Pfads, die zum Suchen des Skripts verwendet wird, kein Symlink ist (Verzeichnislinks sind in Ordnung). Wenn Sie auch Links zum Skript selbst auflösen möchten, benötigen Sie eine mehrzeilige Lösung:

SOURCE="${BASH_SOURCE[0]}"
while [ -h "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink
  DIR="$( cd -P "$( dirname "$SOURCE" )" >/dev/null && pwd )"
  SOURCE="$(readlink "$SOURCE")"
  [[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE" # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located
done
DIR="$( cd -P "$( dirname "$SOURCE" )" >/dev/null && pwd )"

Letzteres funktioniert mit jeder Kombination von Aliasnamen, source , bash -c , Symlinks usw.

Achtung: Wenn Sie vor der Ausführung dieses Snippets in ein anderes Verzeichnis wechseln, kann das Ergebnis falsch sein! Achten Sie auch auf $CDPATH CDPATH-Schlagmuscheln .

Um zu verstehen, wie es funktioniert, führen Sie diese ausführlichere Form aus:

#!/bin/bash

SOURCE="${BASH_SOURCE[0]}"
while [ -h "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink
  TARGET="$(readlink "$SOURCE")"
  if [[ $TARGET == /* ]]; then
    echo "SOURCE '$SOURCE' is an absolute symlink to '$TARGET'"
    SOURCE="$TARGET"
  else
    DIR="$( dirname "$SOURCE" )"
    echo "SOURCE '$SOURCE' is a relative symlink to '$TARGET' (relative to '$DIR')"
    SOURCE="$DIR/$TARGET" # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located
  fi
done
echo "SOURCE is '$SOURCE'"
RDIR="$( dirname "$SOURCE" )"
DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )"
if [ "$DIR" != "$RDIR" ]; then
  echo "DIR '$RDIR' resolves to '$DIR'"
fi
echo "DIR is '$DIR'"

Und es wird so etwas drucken:

SOURCE './scriptdir.sh' is a relative symlink to 'sym2/scriptdir.sh' (relative to '.')
SOURCE is './sym2/scriptdir.sh'
DIR './sym2' resolves to '/home/ubuntu/dotfiles/fo fo/real/real1/real2'
DIR is '/home/ubuntu/dotfiles/fo fo/real/real1/real2'

SCRIPT_DIR=$( cd ${0%/*} && pwd -P )




directory