6176 Stimmen

Wie kann ich das Quellverzeichnis eines Bash-Skripts aus dem Skript selbst abrufen?

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

Ich möchte ein Bash-Skript als Launcher für eine andere Anwendung verwenden. Ich möchte das Arbeitsverzeichnis in das Verzeichnis ändern, in dem sich das Bash-Skript befindet, damit ich mit den Dateien in diesem Verzeichnis arbeiten kann:

$ ./application

91 Stimmen

Keine der derzeitigen Lösungen funktioniert, wenn es irgendwelche Zeilenumbrüche am Ende des Verzeichnisnamens - Sie werden von der Befehlssubstitution entfernt. Um dies zu umgehen, können Sie ein Nicht-Neuzeilen-Zeichen innerhalb der Befehlsersetzung anhängen - DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd && echo x)" - und entfernen Sie es ohne Befehlssubstitution - DIR="${DIR%x}" .

94 Stimmen

@jpmc26 Es gibt zwei sehr häufige Situationen: Unfälle und Sabotage. Ein Skript sollte nicht auf unvorhersehbare Weise versagen, nur weil jemand, irgendwo, eine mkdir $'\n' .

36 Stimmen

Wer Leute sein System auf diese Weise sabotieren lässt, sollte es nicht der Bash überlassen, solche Probleme zu erkennen... und schon gar nicht Leute einstellen, die zu solchen Fehlern fähig sind. Ich habe in den 25 Jahren, in denen ich die Bash benutze, noch nie erlebt, dass so etwas irgendwo passiert.... Deshalb gibt es Dinge wie Perl und Praktiken wie Taint Checking (ich werde wahrscheinlich dafür geflamed werden, dass ich das sage :)

-1voto

ideawu Punkte 2189
cur_dir=`old=\`pwd\`; cd \`dirname $0\`; echo \`pwd\`; cd $old;`

-1voto

mproffitt Punkte 2203

Ich füge in der Regel das Folgende am Anfang meiner Skripte ein, was in den meisten Fällen funktioniert:

[ "$(dirname $0)" = '.' ] && SOURCE_DIR=$(pwd) || SOURCE_DIR=$(dirname $0);
ls -l $0 | grep -q ^l && SOURCE_DIR=$(ls -l $0 | awk '{print $NF}');

Die erste Zeile weist die Quelle auf der Grundlage des Wertes von pwd wenn es vom aktuellen Pfad aus ausgeführt wird oder Verzeichnisname wenn sie von anderswo aufgerufen werden.

Die zweite Zeile prüft den Pfad, um festzustellen, ob es sich um einen Symlink handelt, und wenn ja, wird SOURCE_DIR auf den Ort des Links selbst aktualisiert.

Wahrscheinlich gibt es bessere Lösungen, aber das ist die sauberste, die ich mir selbst ausgedacht habe.

-1voto

AsymLabs Punkte 863

Versuchen Sie so etwas:

function get_realpath() {

if [[ -f "$1" ]]
then
    # The file *must* exist
    if cd "$(echo "${1%/*}")" &>/dev/null
    then
        # The file *may* not be local.
        # The exception is ./file.ext
        # tTry 'cd .; cd -;' *works!*
        local tmppwd="$PWD"
        cd - &>/dev/null
    else
        # file *must* be local
        local tmppwd="$PWD"
    fi
else
    # The file *cannot* exist
    return 1 # Failure
fi

# Reassemble realpath
echo "$tmppwd"/"${1##*/}"
return 0 # Success

}

function get_dirname(){

local realpath="$(get_realpath "$1")"
if (( $? )) # True when non-zero.
then
    return $? # Failure
fi
echo "${realpath%/*}"
return 0 # Success

}

# Then from the top level:
get_dirname './script.sh'

# Or within a script:
get_dirname "$0"

# Can even test the outcome!
if (( $? )) # True when non-zero.
then
    exit 1 # Failure
fi

Diese Funktionen und die dazugehörigen Tools sind Teil unseres Produkts, das der Community kostenlos zur Verfügung gestellt wird und auf GitHub unter folgender Adresse zu finden ist realpath-lib . Es ist einfach, sauber und gut dokumentiert (ideal zum Lernen), reine Bash und hat keine Abhängigkeiten. Auch für den plattformübergreifenden Einsatz gut geeignet. Für das obige Beispiel könnten Sie also innerhalb eines Skripts einfach:

source '/path/to/realpath-lib'

get_dirname "$0"

if (( $? )) # True when non-zero.
then
    exit 1 # Failure
fi

-1voto

Jay jargot Punkte 2417

Sehen Sie sich den Test unten mit den seltsamen Verzeichnisnamen an.

Um das Arbeitsverzeichnis in das Verzeichnis zu ändern, in dem sich das Bash-Skript befindet, sollten Sie Folgendes versuchen, getestet und überprüft mit Shellcheck Lösung:

#!/bin/bash --
cd "$(dirname "${0}")"/. || exit 2

Der Test:

$ ls 
application
$ mkdir "$(printf "\1\2\3\4\5\6\7\10\11\12\13\14\15\16\17\20\21\22\23\24\25\26\27\30\31\32\33\34\35\36\37\40\41\42\43\44\45\46\47testdir" "")"
$ mv application *testdir
$ ln -s *testdir "$(printf "\1\2\3\4\5\6\7\10\11\12\13\14\15\16\17\20\21\22\23\24\25\26\27\30\31\32\33\34\35\36\37\40\41\42\43\44\45\46\47symlink" "")"
$ ls -lb
total 4
lrwxrwxrwx 1 jay stacko   46 Mar 30 20:44 \001\002\003\004\005\006\a\b\t\n\v\f\r\016\017\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037\ !"#$%&'symlink -> \001\002\003\004\005\006\a\b\t\n\v\f\r\016\017\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037\ !"#$%&'testdir
drwxr-xr-x 2 jay stacko 4096 Mar 30 20:44 \001\002\003\004\005\006\a\b\t\n\v\f\r\016\017\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037\ !"#$%&'testdir
$ *testdir/application && printf "SUCCESS\n" ""
SUCCESS
$ *symlink/application && printf "SUCCESS\n" ""
SUCCESS

-1voto

michaeljt Punkte 1046

Der entscheidende Teil ist, dass ich den Umfang des Problems reduziere: Ich verbiete die indirekte Ausführung des Skripts über den Pfad (wie in /bin/sh [script path relative to path component] ).

Dies kann festgestellt werden, weil $0 wird ein relativer Pfad sein, der nicht auf eine Datei relativ zum aktuellen Ordner verweist. Ich glaube, dass die direkte Ausführung mit der Option #! Mechanismus führt immer zu einer absoluten $0 auch wenn das Skript im Pfad gefunden wird.

Ich verlange auch, dass der Pfadname und alle Pfadnamen entlang einer Kette von symbolischen Links nur eine vernünftige Teilmenge von Zeichen enthalten, insbesondere nicht \n , > , * o ? . Dies ist für die Parsing-Logik erforderlich.

Es gibt noch einige weitere implizite Erwartungen, auf die ich nicht eingehen werde (siehe diese Antwort ), und ich versuche nicht, die vorsätzliche Sabotage von $0 (berücksichtigen Sie also alle Sicherheitsaspekte). Ich erwarte, dass dies auf fast jedem Unix-ähnlichen System mit einem Bourne-ähnlichen /bin/sh .

#!/bin/sh
(
    path="${0}"
    while test -n "${path}"; do
        # Make sure we have at least one slash and no leading dash.
        expr "${path}" : / > /dev/null || path="./${path}"
        # Filter out bad characters in the path name.
        expr "${path}" : ".*[*?<>\\]" > /dev/null && exit 1
        # Catch embedded new-lines and non-existing (or path-relative) files.
        # $0 should always be absolute when scripts are invoked through "#!".
        test "`ls -l -d "${path}" 2> /dev/null | wc -l`" -eq 1 || exit 1
        # Change to the folder containing the file to resolve relative links.
        folder=`expr "${path}" : "\(.*/\)[^/][^/]*/*$"` || exit 1
        path=`expr "x\`ls -l -d "${path}"\`" : "[^>]* -> \(.*\)"`
        cd "${folder}"
        # If the last path was not a link then we are in the target folder.
        test -n "${path}" || pwd
    done
)

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X