So rufen Sie das Quellverzeichnis eines Bash-Skripts aus dem Skript selbst ab


4950

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

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

$ ./application

69
Keine der aktuellen Lösungen funktioniert, wenn am Ende des Verzeichnisnamens Zeilenumbrüche stehen. Diese werden durch die Befehlsersetzung entfernt. Um dies zu umgehen, können Sie ein Nicht-Zeilenumbruchzeichen in die Befehlsersetzung einfügen DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd && echo x)"- und es ohne Befehlsersetzung entfernen - DIR="${DIR%x}".
10b0

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

24
Jeder, der es zulässt, dass Leute sein System auf diese Weise sabotieren, sollte es nicht überlassen, solche Probleme zu erkennen ... geschweige denn Leute einzustellen, die in der Lage sind, solche Fehler zu machen. Ich habe in den 25 Jahren, in denen ich Bash verwendet habe, noch nie gesehen, dass so etwas irgendwo passiert. Deshalb haben wir Dinge wie Perl und Praktiken wie das Überprüfen von Verschmutzungen (ich werde wahrscheinlich dafür in Flammen stehen :)
Osirisgothra

3
@ l0b0 Bedenken Sie, dass Sie denselben Schutz benötigen dirnameund dass das Verzeichnis mit einem -(z --help. B. ) beginnen könnte. DIR=$(reldir=$(dirname -- "$0"; echo x); reldir=${reldir%?x}; cd -- "$reldir" && pwd && echo x); DIR=${DIR%?x}. Vielleicht ist das übertrieben?
Score_Under

56
Ich empfehle dringend, diese Bash-FAQ zu diesem Thema zu lesen .
Rany Albeg Wein

Antworten:


6569
#!/bin/bash

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

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

Dies funktioniert so lange, wie die letzte Komponente des Pfads, mit der das Skript gefunden wird, kein Symlink ist (Verzeichnisverknüpfungen sind in Ordnung). Wenn Sie auch Links zum Skript selbst auflösen möchten, benötigen Sie eine mehrzeilige Lösung:

#!/bin/bash

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 2>&1 && 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 2>&1 && pwd )"

Das letzte wird mit einer beliebigen Kombination von Alias - Namen arbeiten, source, bash -c, Symlinks, usw.

Achtung: Wenn Sie cdsich vor dem Ausführen dieses Snippets in einem anderen Verzeichnis befinden, ist das Ergebnis möglicherweise falsch!

$CDPATHAchten Sie auch auf Fallstricke und Nebenwirkungen bei der Ausgabe von stderr, wenn der Benutzer die CD intelligent überschrieben hat, um die Ausgabe stattdessen an stderr umzuleiten (einschließlich Escape-Sequenzen, z. B. beim Aufrufen update_terminal_cwd >&2auf einem Mac). Durch Hinzufügen >/dev/null 2>&1am Ende Ihres cdBefehls werden beide Möglichkeiten berücksichtigt.

Versuchen Sie, diese ausführlichere Form auszuführen, um zu verstehen, wie es funktioniert:

#!/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" )" >/dev/null 2>&1 && pwd )"
if [ "$DIR" != "$RDIR" ]; then
  echo "DIR '$RDIR' resolves to '$DIR'"
fi
echo "DIR is '$DIR'"

Und es wird so etwas wie gedruckt:

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'

27
Sie können diesen Ansatz mit der Antwort von user25866 verschmelzen, um eine Lösung zu erhalten, die mit source <script>und funktioniert bash <script>: DIR="$(cd -P "$(dirname "${BASH_SOURCE[0]}")" && pwd)".
Dan Moulding

21
cdDruckt manchmal etwas an STDOUT! Zum Beispiel, wenn Ihr $CDPATHhat .. Um diesen Fall abzudecken, verwenden SieDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" > /dev/null && pwd )"
user716468

182
Diese akzeptierte Antwort ist nicht in Ordnung, funktioniert nicht mit Symlinks und ist zu komplex. dirname $(readlink -f $0)ist der richtige Befehl. Siehe gist.github.com/tvlooy/cbfbdb111a4ebad8b93e für einen Testfall
tvlooy

167
@tvlooy IMO Ihre Antwort ist auch nicht genau in Ordnung, da sie fehlschlägt, wenn ein Leerzeichen im Pfad vorhanden ist. Im Gegensatz zu einem Zeilenumbruch ist dies nicht unwahrscheinlich oder sogar ungewöhnlich. dirname "$(readlink -f "$0")"erhöht die Komplexität nicht und ist bei minimalem Aufwand angemessen robuster.
Adrian Günter

10
@tvlooy Ihr Kommentar ist nicht macOS (oder wahrscheinlich BSD im Allgemeinen) kompatibel, während die akzeptierte Antwort ist. readlink -f $0gibt readlink: illegal option -- f.
Alexander Ljungberg

875

Verwendung 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 pwdalleinige Verwendung funktioniert nicht, wenn Sie das Skript nicht in dem Verzeichnis ausführen, in dem es enthalten ist.

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

25
Für eine Portabilität über Bash hinaus reicht $ 0 möglicherweise nicht immer aus. Möglicherweise müssen Sie "type -p $ 0" ersetzen, damit dies funktioniert, wenn der Befehl im Pfad gefunden wurde.
Darron

10
@Darron: Sie können nur verwenden, type -pwenn das Skript ausführbar ist. Dies kann auch eine subtile Lücke öffnen, wenn das Skript mit ausgeführt wird bash test2.shund ein anderes Skript mit demselben Namen an einer anderen Stelle ausführbar ist.
D. Shawley

90
@Darron: aber da die Frage markiert ist bashund die Hash-Bang-Zeile ausdrücklich erwähnt, /bin/bashwürde ich sagen, dass es ziemlich sicher ist, sich auf Bashismen zu verlassen.
Joachim Sauer

34
+1, aber das Problem bei der Verwendung dirname $0ist, dass Sie erhalten, wenn das Verzeichnis das aktuelle Verzeichnis ist .. Das ist in Ordnung, es sei denn, Sie ändern die Verzeichnisse im Skript und erwarten, dass Sie den Pfad verwenden, von dem Sie ihn erhalten haben, dirname $0als wäre er absolut. Um den absoluten Pfad: pushd `dirname $0` > /dev/null, SCRIPTPATH=`pwd`, popd > /dev/null: pastie.org/1489386 (Aber sicher gibt es einen besseren Weg , diesen Weg zu erweitern?)
TJ Crowder

9
@TJ Crowder Ich bin mir nicht sicher, ob dirname $0es ein Problem ist, wenn Sie es einer Variablen zuweisen und dann ein Skript wie dieses starten $dir/script.sh. Ich würde mir vorstellen, dass dies in 90% der Fälle der Anwendungsfall für diese Art von Dingen ist. ./script.shwürde gut funktionieren.
Matt B

515

Der dirnameBefehl ist der einfachste und analysiert einfach den Pfad bis zum Dateinamen aus der $0Variablen (Skriptname):

dirname "$0"

Wie matt b hervorhob, ist der zurückgegebene Pfad jedoch unterschiedlich, je nachdem, wie das Skript aufgerufen wird. pwdführt den Job nicht aus, da dies nur angibt, in welchem ​​Verzeichnis sich das aktuelle Verzeichnis befindet und nicht in welchem ​​Verzeichnis sich das Skript befindet. Wenn außerdem eine symbolische Verknüpfung zu einem Skript ausgeführt wird, erhalten Sie einen (wahrscheinlich relativen) Pfad zu Wo sich der Link befindet, nicht das eigentliche Skript.

Einige andere haben den readlinkBefehl erwähnt, aber im einfachsten Fall können Sie Folgendes verwenden:

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

readlinklöst den Skriptpfad in einen absoluten Pfad vom Stammverzeichnis des Dateisystems auf. Alle Pfade, die einfache oder doppelte Punkte, Tildes und / oder symbolische Links enthalten, werden in einen vollständigen Pfad aufgelöst.

Hier ist ein Skript, das jedes dieser Elemente demonstriert whatdir.sh:

#!/bin/bash
echo "pwd: `pwd`"
echo "\$0: $0"
echo "basename: `basename $0`"
echo "dirname: `dirname $0`"
echo "dirname/readlink: $(dirname $(readlink -f $0))"

Ausführen dieses Skripts in meinem Ausgangsverzeichnis unter Verwendung eines relativen Pfads:

>>>$ ./whatdir.sh 
pwd: /Users/phatblat
$0: ./whatdir.sh
basename: whatdir.sh
dirname: .
dirname/readlink: /Users/phatblat

Nochmals, aber unter Verwendung des vollständigen Pfads zum Skript:

>>>$ /Users/phatblat/whatdir.sh 
pwd: /Users/phatblat
$0: /Users/phatblat/whatdir.sh
basename: whatdir.sh
dirname: /Users/phatblat
dirname/readlink: /Users/phatblat

Jetzt Verzeichnisse wechseln:

>>>$ cd /tmp
>>>$ ~/whatdir.sh 
pwd: /tmp
$0: /Users/phatblat/whatdir.sh
basename: whatdir.sh
dirname: /Users/phatblat
dirname/readlink: /Users/phatblat

Und schließlich einen symbolischen Link verwenden, um das Skript auszuführen:

>>>$ ln -s ~/whatdir.sh whatdirlink.sh
>>>$ ./whatdirlink.sh 
pwd: /tmp
$0: ./whatdirlink.sh
basename: whatdirlink.sh
dirname: .
dirname/readlink: /Users/phatblat

13
readlinkwird in einigen Plattformen in der Standardinstallation nicht verfügbar sein. Versuchen Sie, es zu vermeiden, wenn Sie können
TL

43
export SCRIPT_DIR="$(dirname "$(readlink -f "$0")")"
Achten Sie

13
In OSX wird Yosemite 10.10.1 -fnicht als Option für erkannt readlink. Verwenden Sie stat -fstattdessen den Job. Danke
cucu8

11
In OSX gibt es greadlinkim Grunde das, was readlinkwir alle kennen. Hier ist eine plattformunabhängige Version:dir=`greadlink -f ${BASH_SOURCE[0]} || readlink -f ${BASH_SOURCE[0]}`
Robert

6
Guter Anruf, @robert. Zu greadlinkbrew install coreutils
Ihrer Information

184
pushd . > /dev/null
SCRIPT_PATH="${BASH_SOURCE[0]}"
if ([ -h "${SCRIPT_PATH}" ]); then
  while([ -h "${SCRIPT_PATH}" ]); do cd `dirname "$SCRIPT_PATH"`; 
  SCRIPT_PATH=`readlink "${SCRIPT_PATH}"`; done
fi
cd `dirname ${SCRIPT_PATH}` > /dev/null
SCRIPT_PATH=`pwd`;
popd  > /dev/null

Funktioniert für alle Versionen, einschließlich

  • Wenn über einen Softlink mit mehreren Tiefen aufgerufen,
  • wenn die Datei es
  • Wenn das Skript vom Befehl " source" aka .(Punkt-) Operator aufgerufen wird .
  • wenn arg $0vom Aufrufer geändert wird.
  • "./script"
  • "/full/path/to/script"
  • "/some/path/../../another/path/script"
  • "./some/folder/script"

Wenn das Bash-Skript selbst ein relativer Symlink ist , möchten Sie ihm alternativ folgen und den vollständigen Pfad des verknüpften Skripts zurückgeben:

pushd . > /dev/null
SCRIPT_PATH="${BASH_SOURCE[0]}";
if ([ -h "${SCRIPT_PATH}" ]) then
  while([ -h "${SCRIPT_PATH}" ]) do cd `dirname "$SCRIPT_PATH"`; SCRIPT_PATH=`readlink "${SCRIPT_PATH}"`; done
fi
cd `dirname ${SCRIPT_PATH}` > /dev/null
SCRIPT_PATH=`pwd`;
popd  > /dev/null

SCRIPT_PATHwird im vollen Pfad gegeben, egal wie es heißt.
Stellen Sie einfach sicher, dass Sie dies am Anfang des Skripts finden.

Dieser Kommentar und Code Copyleft, auswählbare Lizenz unter der GPL2.0 oder höher oder CC-SA 3.0 (CreativeCommons Share Alike) oder höher. (c) 2008. Alle Rechte vorbehalten. Keine Garantie jeglicher Art. Du wurdest gewarnt.
http://www.gnu.org/licenses/gpl-2.0.txt
http://creativecommons.org/licenses/by-sa/3.0/
18eedfe1c99df68dc94d4a94712a71aaa8e1e9e36cacf421b9463dd2bbaa02906d06


4
Nett! Könnte kürzer gemacht werden, indem "pushd [...] popd / dev / null" durch SCRIPT_PATH = ersetzt wird readlink -f $(dirname "${VIRTUAL_ENV}");
E-Satis

5
Und anstatt pushd zu verwenden ...; Wäre es nicht besser, $ (cd dirname "${SCRIPT_PATH}"&& pwd) zu verwenden? Aber trotzdem tolles Drehbuch!
Ovanes

6
Es ist gefährlich, dass ein Skript cdsein aktuelles Verzeichnis cdverlässt, in der Hoffnung, später wieder zurück zu kommen: Das Skript hat möglicherweise nicht die Berechtigung, das Verzeichnis wieder in das Verzeichnis zu ändern, das beim Aufrufen aktuell war. (Gleiches gilt für pushd / popd)
Adrian Pronk

7
readlink -fist GNU-spezifisch. BSD readlinkhat diese Option nicht.
Kara Brightwell

3
Was ist mit all den unnötigen Unterschalen? ([ ... ])ist weniger effizient als [ ... ]und es wird kein Vorteil aus der Isolation gezogen, die als Gegenleistung für diesen hier erzielten Leistungseinbruch angeboten wird.
Charles Duffy

110

Kurze Antwort:

`dirname $0`

oder ( vorzugsweise ):

$(dirname "$0")

17
Es wird nicht funktionieren, wenn Sie das Skript als Quelle verwenden. "source my / script.sh"
Arunprasad Rajkumar

Ich verwende dies die ganze Zeit in meinen Bash-Skripten, die Dinge automatisieren und oft andere Skripte im selben Verzeichnis aufrufen. Ich würde diese nie verwenden sourceund cd $(dirname $0)ist leicht zu merken.
kqw

16
@vidstige: ${BASH_SOURCE[0]}anstatt mit zu $0arbeitensource my/script.sh
Timothy Jones

@TimothyJones, die 100% der Zeit versagen, wenn sie aus einer anderen Shell als Bash stammen. ${BASH_SOURCE[0]}ist überhaupt nicht zufriedenstellend. ${BASH_SOURCE:-0}ist viel besser.
Mathieu CAROFF

106

Sie können verwenden $BASH_SOURCE:

#!/bin/bash

scriptdir=`dirname "$BASH_SOURCE"`

Beachten Sie, dass Sie verwenden müssen #!/bin/bashund nicht, #!/bin/shda es sich um eine Bash-Erweiterung handelt.


14
Wenn ich das tue ./foo/script, dann $(dirname $BASH_SOURCE)ist es ./foo.
Bis zum

1
@ Bis, In diesem Fall können wir den realpathBefehl verwenden, um den vollständigen Pfad von ./foo/script zu erhalten. So dirname $(realpath ./foo/script) wird der Pfad des Skripts geben.
Purushothaman Poovai

73

Dies sollte es tun:

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

Dies funktioniert mit Symlinks und Leerzeichen im Pfad.

Siehe die Manpages für dirnameund readlink.

Aus der Kommentarspur geht hervor, dass es unter Mac OS nicht funktioniert. Ich habe keine Ahnung warum das so ist. Irgendwelche Vorschläge?


6
Mit Ihrer Lösung rufen Sie das Skript wie ./script.shShows .anstelle des vollständigen Verzeichnispfads auf
Bruno Negrão Zica

5
Unter MacOS gibt es keine Option -f für Readlink. Verwenden Sie statstattdessen. Trotzdem zeigt es, .ob Sie sich in diesem Verzeichnis befinden.
Denis die Bedrohung

2
Sie müssen coreutilsvon Homebrew installieren und verwenden greadlink, um die -fOption unter MacOS zu erhalten, da es sich um * BSD unter der Decke und nicht um Linux handelt.
Dragon788

Sie sollten doppelte Anführungszeichen auf der gesamten rechten Seite hinzufügen:DIR="$(dirname "$(readlink -f "$0")")"
Hagello

60

pwdkann verwendet werden, um das aktuelle Arbeitsverzeichnis und dirnamedas Verzeichnis einer bestimmten Datei zu finden (Befehl, der ausgeführt wurde, ist $0, dirname $0sollte Ihnen also das Verzeichnis des aktuellen Skripts geben).

Allerdings dirnamegibt genau das Verzeichnis Teil des Dateinamens, die eher als nicht auf das aktuelle Arbeitsverzeichnis ist relativ wird. Wenn Ihr Skript aus irgendeinem Grund das Verzeichnis wechseln muss, ist die Ausgabe von dirnamebedeutungslos.

Ich schlage folgendes vor:

#!/bin/bash

reldir=`dirname $0`
cd $reldir
directory=`pwd`

echo "Directory is $directory"

Auf diese Weise erhalten Sie ein absolutes und kein relatives Verzeichnis.

Da das Skript in einer separaten Bash-Instanz ausgeführt wird, muss das Arbeitsverzeichnis anschließend nicht wiederhergestellt werden. Wenn Sie jedoch aus irgendeinem Grund wieder Änderungen an Ihrem Skript vornehmen möchten, können Sie den Wert von problemlos pwdeiner Variablen vor Ihnen zuweisen Verzeichnis ändern, für zukünftige Verwendung.

Obwohl nur

cd `dirname $0`

Löst das spezifische Szenario in der Frage, finde ich den absoluten Weg allgemein nützlicher.


9
Sie können alles in einer Zeile wie folgt tun: DIRECTORY = $ (cd dirname $0&& pwd)
dogbane

Dies funktioniert nicht, wenn das Skript ein anderes Skript erstellt und Sie dessen Namen wissen möchten.
Reinierpost

52

Ich bin es leid, immer wieder auf diese Seite zu kommen, um den Einzeiler in die akzeptierte Antwort einzufügen. Das Problem dabei ist, dass es nicht leicht zu verstehen und sich zu erinnern ist.

Hier ist ein leicht zu merkendes Skript:

DIR="$(dirname "${BASH_SOURCE[0]}")"  # get the directory name
DIR="$(realpath "${DIR}")"    # resolve its full path if need be

2
Oder, dunkler, in einer Zeile: DIR=$(realpath "$(dirname "${BASH_SOURCE[0]}")")
Agc

Warum ist das nicht die akzeptierte Antwort? Gibt es einen Unterschied zum realpath"manuellen" Auflösen mit einer Schleife von readlink? Sogar die readlinkManpage sagtNote realpath(1) is the preferred command to use for canonicalization functionality.
User9123

1
Und sollten wir uns übrigens nicht realpathvorher dirnameund nicht nachher bewerben ? Wenn die Skriptdatei selbst ein Symlink ist ... würde es so etwas geben DIR="$(dirname "$(realpath "${BASH_SOURCE[0]}")")". Eigentlich sehr nah an der Antwort von Simon.
User9123

@ User9123 Ich denke, die Akzeptanz ist der Versuch, mit allen gängigen Shell / Distribution kompatibel zu sein. Je nachdem, was Sie tun möchten, möchten die Benutzer in den meisten Fällen das Verzeichnis abrufen, in dem sich der Symlink befindet, anstelle des Verzeichnisses der tatsächlichen Quelle.
Wang

37

Ich denke nicht, dass dies so einfach ist, wie andere es sich vorgestellt haben. pwdfunktioniert nicht, da das aktuelle Verzeichnis nicht unbedingt das Verzeichnis mit dem Skript ist. $0hat auch nicht immer die Informationen. Betrachten Sie die folgenden drei Möglichkeiten, um ein Skript aufzurufen:

./script

/usr/bin/script

script

Auf die erste und dritte Weise $0verfügt nicht über die vollständigen Pfadinformationen. Im zweiten und dritten pwdfunktioniert nicht. Die einzige Möglichkeit, das Verzeichnis auf die dritte Weise abzurufen, besteht darin, den Pfad zu durchlaufen und die Datei mit der richtigen Übereinstimmung zu finden. Grundsätzlich müsste der Code wiederholen, was das Betriebssystem tut.

Eine Möglichkeit, das zu tun, was Sie verlangen, besteht darin, die Daten im /usr/shareVerzeichnis einfach fest zu codieren und auf ihren vollständigen Pfad zu verweisen. Daten sollten sich /usr/binsowieso nicht im Verzeichnis befinden, daher ist dies wahrscheinlich die richtige Vorgehensweise .


9
Wenn Sie seinen Kommentar widerlegen möchten, beweisen Sie, dass ein Skript mit einem Codebeispiel darauf zugreifen kann, wo es gespeichert ist.
Richard Duerr

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

Dies ist viel kürzer als die gewählte Antwort. Und scheint genauso gut zu funktionieren. Dies verdient 1000 Stimmen, nur damit die Leute es nicht übersehen.
Patrick

2
Wie viele der vorherigen Antworten ausführlich erläutern, sind je nach Aufruf des Skripts weder die richtigen Informationen garantiert $0noch pwdgarantiert.
IMSoP

34
$(dirname "$(readlink -f "$BASH_SOURCE")")

Ich bevorzuge $BASH_SOURCEes $0, weil es explizit ist, selbst für Leser, die sich mit Bash nicht auskennen. $(dirname -- "$(readlink -f -- "$BASH_SOURCE")")
Blobmaster

32

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

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

27

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

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

1
Es ist auch bash-spezifisch, aber vielleicht hat sich das Verhalten von bash geändert? /proc/fd/$$/255scheint auf das tty zu verweisen, nicht auf ein Verzeichnis. In meiner aktuellen Anmeldeshell beziehen sich beispielsweise die Dateideskriptoren 0, 1, 2 und 255 auf /dev/pts/4. In jedem Fall wird im Bash-Handbuch fd 255 nicht erwähnt, daher ist es wahrscheinlich unklug, sich auf dieses Verhalten zu verlassen. \
Keith Thompson

2
Interaktive Shell! = Skript. Auf jeden realpath ${BASH_SOURCE[0]};Fall scheint der beste Weg zu sein.
Steve Baker

23

Hier ist ein POSIX-kompatibler Einzeiler:

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

# test
echo $SCRIPT_PATH

4
Ich hatte Erfolg damit, wenn ich ein Skript alleine oder mit sudo ausführte, aber nicht beim Aufrufen der Quelle ./script.sh
Michael R

Und es schlägt fehl, wenn cdkonfiguriert ist, den neuen Pfadnamen zu drucken.
Aaron Digulla

18

Ich habe all dies versucht und keines hat funktioniert. Einer war sehr nah dran, hatte aber einen winzigen Fehler, der ihn schwer brach; Sie haben vergessen, den Pfad in Anführungszeichen zu setzen.

Viele Leute gehen auch davon aus, dass Sie das Skript über eine Shell ausführen, sodass sie vergessen, wenn Sie ein neues Skript öffnen, das standardmäßig zu Hause verwendet wird.

Probieren Sie dieses Verzeichnis für die Größe an:

/var/No one/Thought/About Spaces Being/In a Directory/Name/And Here's your file.text

Dies macht es richtig, unabhängig davon, 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 tatsächlich nützlich zu machen, gehen Sie wie folgt in das Verzeichnis des laufenden Skripts:

cd "`dirname "$0"`"

4
Funktioniert nicht, wenn das Skript von einem anderen Skript bezogen wird.
Reinierpost

Dies funktioniert nicht, wenn der letzte Teil von $ 0 ein symbolischer Link ist, der auf einen Eintrag eines anderen Verzeichnisses verweist ( ln -s ../bin64/foo /usr/bin/foo).
Hagello

17

Hier ist der einfache, richtige Weg:

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

Erläuterung:

  • ${BASH_SOURCE[0]}- den vollständigen Pfad zum Skript. Der Wert ist auch dann korrekt, wenn das Skript bezogen wird, z. B. Bashsource <(echo 'echo $0') druckt. Wenn Sie es durch ersetzen, wird der vollständige Pfad des Skripts gedruckt. (Dies setzt natürlich voraus, dass Sie eine Abhängigkeit von Bash haben.)${BASH_SOURCE[0]}

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

  • Und natürlich dirnamebekommt das übergeordnete Verzeichnis des Pfades.


1
greadlink -fLeider funktioniert es nicht effektiv, wenn sourcedas Skript auf dem Mac ausgeführt wird :(
Gabe Kopley

17

Der kürzeste und eleganteste Weg, dies zu tun, ist:

#!/bin/bash
DIRECTORY=$(cd `dirname $0` && pwd)
echo $DIRECTORY

Dies würde auf allen Plattformen funktionieren und ist super sauber.

Weitere Details finden Sie unter "In welchem ​​Verzeichnis befindet sich das Bash-Skript? ".


Tolle saubere Lösung, aber das funktioniert nicht, wenn die Datei symlinkiert ist.
Ruuter

16

Ich würde so etwas verwenden:

# retrieve the full pathname of the called script
scriptPath=$(which $0)

# check whether the path is a link or not
if [ -L $scriptPath ]; then

    # it is a link then retrieve the target path and get the directory name
    sourceDir=$(dirname $(readlink -f $scriptPath))

else

    # otherwise just get the directory name of the script path
    sourceDir=$(dirname $scriptPath)

fi

Das ist der echte! Funktioniert auch mit einfach sh! Problem mit einfachen dirname "$0"Lösungen: Wenn sich das Skript im befindet $PATHund ohne Pfad aufgerufen wird, führen sie zu einem falschen Ergebnis.
Notinlist

@Notinlist Nicht so. Wenn das Skript über das gefunden wird PATH, $0wird die absoluten Dateinamen enthalten. Wenn das Skript mit einem relativen oder absoluten Dateinamen aufgerufen wird , enthalten ein /, $0wird diese enthalten.
Neil Mayhew

Funktioniert nicht für ein Sourcing-Skript.
Amit Naidu

16

Dies ist eine geringfügige Überarbeitung der Lösung e-satis und 3bcdnlklvc04a, auf die in ihrer Antwort hingewiesen wurde :

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.

Dies wird popdnach einem pushdFehlschlag dank konsolebox verhindert.


Dies funktioniert perfekt, um den "echten" Dirnamen zu erhalten und nicht nur den Namen eines Symlinks. Vielen Dank!
Jay Taylor

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

@konsolebox, gegen was versuchst du dich zu verteidigen? Ich bin im Allgemeinen ein Fan von Inlining logischer Bedingungen, aber was war der spezifische Fehler, den Sie im Pushd gesehen haben? Ich würde lieber einen Weg finden, um direkt damit umzugehen, anstatt ein leeres SCRIPT_DIR zurückzugeben.
Fuwjax

@Fuwjax Natürliche Praxis, um dies popdin Fällen (auch in seltenen Fällen) zu vermeiden, in denen dies pushdfehlschlägt. Und falls dies pushdfehlschlägt, was sollte Ihrer Meinung nach der Wert von sein SCRIPT_DIR? Die Aktion kann variieren, je nachdem, was logisch erscheint oder was ein Benutzer bevorzugen könnte, aber das ist sicherlich popdfalsch.
konsolebox

All diese pushd popdGefahren könnten vermieden werden, indem man sie einfach fallen lässt und stattdessen cd+ pwdin einer Befehlssubstitution verwendet. SCRIPT_DIR=$(...)
Amit Naidu

16

Für Systeme mit GNU-Coreutils readlink(z. B. Linux):

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

Es ist nicht erforderlich, BASH_SOURCEwenn $0das Skript den Dateinamen enthält.


2
es sei denn, das Skript wurde mit bezogen. oder 'source'. In diesem Fall handelt es sich immer noch um das Skript, aus dem es stammt, oder, falls über die Befehlszeile, '-bash' (tty login) oder 'bash' (aufgerufen über 'bash -l') oder '/ bin / bash '(wird als interaktive Nicht-Login-Shell aufgerufen)
osirisgothra

Ich habe ein zweites Anführungszeichen um den dirnameAnruf hinzugefügt . Wird benötigt, wenn der Verzeichnispfad Leerzeichen enthält.
user1338062

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

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

scriptdir=`dirname "$PRG"`

Ich habe es nicht systemübergreifend getestet. Aber diese Lösung funktioniert für mich zumindest unter Ubuntu sofort!
Natus Drew

$0funktioniert nicht für ein Sourcing-Skript
Amit Naidu

13

$_ist als Alternative zu erwähnenswert $0. Wenn Sie ein Skript von Bash ausführen, kann die akzeptierte Antwort wie folgt gekürzt werden:

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

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


4
Es bricht, wenn Sie sourceoder .das Skript. In diesen Situationen $_würde der letzte Parameter des letzten Befehls enthalten sein, den Sie vor dem ausgeführt haben .. $BASH_SOURCEfunktioniert jedes Mal.
Clacke

11

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

  • Absolute Pfade oder relative Pfade
  • Datei- und Verzeichnis-Softlinks
  • Invocation wie script, bash script, bash -c script, source script, oder. script
  • Leerzeichen, Tabulatoren, Zeilenumbrüche, Unicode usw. in Verzeichnissen und / oder Dateinamen
  • Dateinamen, die mit einem Bindestrich beginnen

Wenn Sie unter Linux arbeiten, ist die Verwendung des procHandles anscheinend die beste Lösung, um die vollständig aufgelöste Quelle des aktuell ausgeführten Skripts zu finden (in einer interaktiven Sitzung verweist der Link auf das jeweilige /dev/pts/X):

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

Dies hat ein wenig Hässlichkeit, aber die Lösung ist kompakt und leicht zu verstehen. Wir verwenden nicht nur Bash-Grundelemente, aber ich bin damit einverstanden, da dies readlinkdie Aufgabe erheblich vereinfacht. Das echo Xfügt Xam Ende der Variablenzeichenfolge ein hinzu, damit nachfolgende Leerzeichen im Dateinamen nicht gegessen werden und die Parametersubstitution ${VAR%X}am Ende der Zeile das entfernt X. Da readlinkeine eigene neue Zeile hinzugefügt wird (die normalerweise in der Befehlsersetzung verwendet wird, wenn nicht für unsere vorherigen Tricks), müssen wir auch diese entfernen. Dies lässt sich am einfachsten mit dem $''Anführungszeichen erreichen, mit dem Escape-Sequenzen wie z\n um Zeilenumbrüche darzustellen (auf diese Weise können Sie auch leicht falsch benannte Verzeichnisse und Dateien erstellen).

Das Obige sollte Ihre Anforderungen zum Auffinden des aktuell ausgeführten Skripts unter Linux abdecken. Wenn Sie jedoch nicht über das procDateisystem verfügen oder versuchen, den vollständig aufgelösten Pfad einer anderen Datei zu finden , werden Sie dies möglicherweise tun Finden Sie den folgenden Code hilfreich. Es ist nur eine geringfügige Änderung gegenüber dem obigen Einzeiler. Wenn Sie mit seltsamen Verzeichnis- / Dateinamen herumspielen, überprüfen Sie die Ausgabe mit beiden lsund readlinksind informativ, ebenso lswie "vereinfachte" Pfade, die beispielsweise ?Zeilenumbrüche ersetzen.

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"

Ich bekomme /dev/pts/30mit Bash auf Ubuntu 14.10 Desktop.
Dan Dascalescu

@DanDascalescu Verwenden Sie den Einzeiler? Oder das vollständige Code-Snippet unten? Und hast du ihm irgendwelche kniffligen Pfadnamen gegeben?
Billyjmc

Die eine Linie plus weiteren Zeilen echo $resolved, ich rettete sie als d, chmod +x d, ./d.
Dan Dascalescu

@ DanDascalescu Die erste Zeile in Ihrem Skript muss sein#!/bin/bash
billyjmc

10

Versuchen Sie es mit:

real=$(realpath $(dirname $0))

1
Ich möchte nur wissen, warum dieser Weg nicht gut ist. Es schien mir nicht schlecht und richtig zu sein. Könnte jemand erklären, warum es herabgestimmt ist?
Shou Ya

7
realpath ist kein Standarddienstprogramm.
Steve Bennett

2
Unter Linux ist realpath ein Standarddienstprogramm (Teil des GNU-Coreutils-Pakets), aber kein integriertes Bash (dh eine von Bash selbst bereitgestellte Funktion). Wenn Sie Linux laufen lassen , wird diese Methode wahrscheinlich Arbeit, obwohl ich das würde Ersatz $0für ${BASH_SOURCE[0]}so dass diese Methode überall arbeiten, in einer Funktion einschließlich.
Doug Richardson

3
Die Reihenfolge der Operationen in dieser Antwort ist falsch. Sie müssen zuerst den Symlink auflösen und dann , dirnameda der letzte Teil von $0möglicherweise ein Symlink ist, der auf eine Datei verweist, die sich nicht im selben Verzeichnis wie der Symlink selbst befindet. Die in dieser Antwort beschriebene Lösung erhält nur den Pfad des Verzeichnisses, in dem der Symlink gespeichert ist, und nicht das Verzeichnis des Ziels. Darüber hinaus fehlt dieser Lösung das Zitieren. Es funktioniert nicht, wenn der Pfad Sonderzeichen enthält.
Hagello

3
dir="$(realpath "$(dirname "${BASH_SOURCE[0]}")")"
Kostiantyn Ponomarenko

9

Probieren Sie die folgende kreuzkompatible Lösung aus:

CWD="$(cd -P -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)"

da die Befehle wie realpathoder readlinknicht verfügbar sein könnten (je nach Betriebssystem).

Hinweis: In Bash wird empfohlen, ${BASH_SOURCE[0]}anstelle von zu verwenden $0, da sonst der Pfad bei der Beschaffung der Datei ( source/ .) unterbrochen werden kann .

Alternativ können Sie die 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 ihn so wie er ist, andernfalls drucken Sie das $PWDArgument Variable + Dateiname (ohne ./Präfix).

Verbunden:


Bitte erläutern Sie mehr über die Realpath-Funktion.
Chris

1
Die @ Chris- realpathFunktion akzeptiert 1 Argument. Wenn das Argument bereits einen absoluten Pfad hat, drucken Sie ihn so wie er ist, andernfalls drucken Sie $PWD+ Dateiname (ohne ./Präfix).
Kenorb

Ihre kompatible Lösung funktioniert nicht, wenn das Skript verknüpft ist.
Jakub Jirutka

9

Ich glaube, ich habe diesen. Ich bin zu spät zur Party, aber ich denke, einige werden es zu schätzen wissen, hier zu sein, wenn sie auf diesen Thread stoßen. Die Kommentare sollten erklären:

#!/bin/sh # dash bash ksh # !zsh (issues). G. Nixon, 12/2013. Public domain.

## 'linkread' or 'fullpath' or (you choose) is a little tool to recursively
## dereference symbolic links (ala 'readlink') until the originating file
## is found. This is effectively the same function provided in stdlib.h as
## 'realpath' and on the command line in GNU 'readlink -f'.

## Neither of these tools, however, are particularly accessible on the many
## systems that do not have the GNU implementation of readlink, nor ship
## with a system compiler (not to mention the requisite knowledge of C).

## This script is written with portability and (to the extent possible, speed)
## in mind, hence the use of printf for echo and case statements where they
## can be substituded for test, though I've had to scale back a bit on that.

## It is (to the best of my knowledge) written in standard POSIX shell, and
## has been tested with bash-as-bin-sh, dash, and ksh93. zsh seems to have
## issues with it, though I'm not sure why; so probably best to avoid for now.

## Particularly useful (in fact, the reason I wrote this) is the fact that
## it can be used within a shell script to find the path of the script itself.
## (I am sure the shell knows this already; but most likely for the sake of
## security it is not made readily available. The implementation of "$0"
## specificies that the $0 must be the location of **last** symbolic link in
## a chain, or wherever it resides in the path.) This can be used for some
## ...interesting things, like self-duplicating and self-modifiying scripts.

## Currently supported are three errors: whether the file specified exists
## (ala ENOENT), whether its target exists/is accessible; and the special
## case of when a sybolic link references itself "foo -> foo": a common error
## for beginners, since 'ln' does not produce an error if the order of link
## and target are reversed on the command line. (See POSIX signal ELOOP.)

## It would probably be rather simple to write to use this as a basis for
## a pure shell implementation of the 'symlinks' util included with Linux.

## As an aside, the amount of code below **completely** belies the amount
## effort it took to get this right -- but I guess that's coding for you.

##===-------------------------------------------------------------------===##

for argv; do :; done # Last parameter on command line, for options parsing.

## Error messages. Use functions so that we can sub in when the error occurs.

recurses(){ printf "Self-referential:\n\t$argv ->\n\t$argv\n" ;}
dangling(){ printf "Broken symlink:\n\t$argv ->\n\t"$(readlink "$argv")"\n" ;}
errnoent(){ printf "No such file: "$@"\n" ;} # Borrow a horrible signal name.

# Probably best not to install as 'pathfull', if you can avoid it.

pathfull(){ cd "$(dirname "$@")"; link="$(readlink "$(basename "$@")")"

## 'test and 'ls' report different status for bad symlinks, so we use this.

 if [ ! -e "$@" ]; then if $(ls -d "$@" 2>/dev/null) 2>/dev/null;  then
    errnoent 1>&2; exit 1; elif [ ! -e "$@" -a "$link" = "$@" ];   then
    recurses 1>&2; exit 1; elif [ ! -e "$@" ] && [ ! -z "$link" ]; then
    dangling 1>&2; exit 1; fi
 fi

## Not a link, but there might be one in the path, so 'cd' and 'pwd'.

 if [ -z "$link" ]; then if [ "$(dirname "$@" | cut -c1)" = '/' ]; then
   printf "$@\n"; exit 0; else printf "$(pwd)/$(basename "$@")\n"; fi; exit 0
 fi

## Walk the symlinks back to the origin. Calls itself recursivly as needed.

 while [ "$link" ]; do
   cd "$(dirname "$link")"; newlink="$(readlink "$(basename "$link")")"
   case "$newlink" in
    "$link") dangling 1>&2 && exit 1                                       ;;
         '') printf "$(pwd)/$(basename "$link")\n"; exit 0                 ;;
          *) link="$newlink" && pathfull "$link"                           ;;
   esac
 done
 printf "$(pwd)/$(basename "$newlink")\n"
}

## Demo. Install somewhere deep in the filesystem, then symlink somewhere 
## else, symlink again (maybe with a different name) elsewhere, and link
## back into the directory you started in (or something.) The absolute path
## of the script will always be reported in the usage, along with "$0".

if [ -z "$argv" ]; then scriptname="$(pathfull "$0")"

# Yay ANSI l33t codes! Fancy.
 printf "\n\033[3mfrom/as: \033[4m$0\033[0m\n\n\033[1mUSAGE:\033[0m   "
 printf "\033[4m$scriptname\033[24m [ link | file | dir ]\n\n         "
 printf "Recursive readlink for the authoritative file, symlink after "
 printf "symlink.\n\n\n         \033[4m$scriptname\033[24m\n\n        "
 printf " From within an invocation of a script, locate the script's "
 printf "own file\n         (no matter where it has been linked or "
 printf "from where it is being called).\n\n"

else pathfull "$@"
fi

8

Dies sind kurze Möglichkeiten, um Skriptinformationen abzurufen:

Ordner und Dateien:

    Script: "/tmp/src dir/test.sh"
    Calling folder: "/tmp/src dir/other"

Verwenden Sie diese Befehle:

    echo Script-Dir : `dirname "$(realpath $0)"`
    echo Script-Dir : $( cd ${0%/*} && pwd -P )
    echo Script-Dir : $(dirname "$(readlink -f "$0")")
    echo
    echo Script-Name : `basename "$(realpath $0)"`
    echo Script-Name : `basename $0`
    echo
    echo Script-Dir-Relative : `dirname "$BASH_SOURCE"`
    echo Script-Dir-Relative : `dirname $0`
    echo
    echo Calling-Dir : `pwd`

Und ich habe diese Ausgabe bekommen:

     Script-Dir : /tmp/src dir
     Script-Dir : /tmp/src dir
     Script-Dir : /tmp/src dir

     Script-Name : test.sh
     Script-Name : test.sh

     Script-Dir-Relative : ..
     Script-Dir-Relative : ..

     Calling-Dir : /tmp/src dir/other

Siehe auch: https://pastebin.com/J8KjxrPF



Ich denke, meine Antwort ist in Ordnung, weil es schwierig ist, eine einfache Arbeitsausgabe zu finden. Hier können Sie den gewünschten Code verwenden, z. B. cd + pwd, dirname + realpath oder dirname + readlink. Ich bin nicht sicher, ob alle Teile vorher existieren und die meisten Antworten sind komplex und überladen. Hier können Sie den Code auswählen, den Sie verwenden möchten. Zumindest bitte nicht entfernen, wie ich es in Zukunft brauche: D
User8461

8

Dies funktioniert in bash-3.2:

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

Wenn Sie ein ~/binVerzeichnis in Ihrem Verzeichnis haben $PATH, befinden Sie sich Ain diesem Verzeichnis. Es bezieht das Skript ~/bin/lib/B. Sie wissen, wo sich das enthaltene Skript relativ zum ursprünglichen Skript im libUnterverzeichnis befindet, aber nicht, wo es sich relativ zum aktuellen Verzeichnis des Benutzers befindet.

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

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

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


3
Der Punkt whichist sehr umstritten. type, hashUnd andere builtins das Gleiche tun , was besser in bash. whichist irgendwie tragbarer, obwohl es wirklich nicht dasselbe ist, whichdas in anderen Shells wie tcsh verwendet wird, das es als eingebautes hat.
Setzen Sie Monica bitte

"Immer"? Ganz und gar nicht. whichAls externes Tool haben Sie keinen Grund zu der Annahme, dass es sich identisch mit der übergeordneten Shell verhält.
Charles Duffy

7

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

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

Es gibt kein Vertrauen in etwas anderes als Bash. Die Verwendung von dirname, readlinkund basenamewird schließlich zu Kompatibilitätsproblemen führen, so dass sie überhaupt möglich am besten vermieden werden , wenn.


2
Sie sollten dem wahrscheinlich einen Schrägstrich hinzufügen : "$( cd "$( echo "${BASH_SOURCE[0]%/*}/" )"; pwd )". Sie hätten Probleme mit dem Stammverzeichnis, wenn Sie dies nicht tun. Auch warum musst du überhaupt Echo verwenden?
konsolebox

dirnameund basenamesind POSIX standardisiert? Warum sollten Sie sie nicht verwenden? Links : dirname,basename
Myrdd

Das Verhindern von zwei zusätzlichen Prozessgabeln und das Festhalten an eingebauten Schalen kann ein Grund dafür sein.
Amit Naidu
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.