Normalerweise fügen Sie ein Skript mit "source" ein.
z.B:
main.sh:
#!/bin/bash
source incl.sh
echo "The main script"
inkl.sh:
echo "The included script"
Die Ausgabe der Ausführung von "./main.sh" lautet:
The included script
The main script
... Wenn Sie nun versuchen, dieses Shell-Skript von einem anderen Speicherort aus auszuführen, kann es das Include nur finden, wenn es sich in Ihrem Pfad befindet.
Was ist ein guter Weg, um sicherzustellen, dass Ihr Skript das Include-Skript findet, insbesondere wenn das Skript beispielsweise portabel sein muss?
Antworten:
Ich neige dazu, meine Skripte alle relativ zueinander zu machen. Auf diese Weise kann ich dirname verwenden:
quelle
which $0
wird nützlich seinBASH_SOURCE
Array gelernt und wie das erste Element in diesem Array immer auf die aktuelle Quelle verweist.Ich weiß, dass ich zu spät zur Party komme, aber das sollte funktionieren, egal wie Sie das Skript starten und ausschließlich integrierte Funktionen verwenden:
.
(dot) Befehl ist ein Alias fürsource
,$PWD
ist der Pfad für das Arbeitsverzeichnis,BASH_SOURCE
ist eine Array-Variable, deren Mitglieder die Quelldateinamen sind, entfernt die${string%substring}
kürzeste Übereinstimmung von $ substring von der Rückseite von $ stringquelle
if [[ ! -d "$DIR" ]]; then DIR="$PWD"; fi
benötigt wird? Ich kann eine Notwendigkeit dafür finden, wenn die Befehle in eine Bash-Eingabeaufforderung eingefügt werden, um ausgeführt zu werden. Wenn ich jedoch in einem Skriptdateikontext ausgeführt werde, kann ich keine Notwendigkeit dafür erkennen ...source
s hinweg wie erwartet funktioniert (ich meine, wenn Siesource
ein Skript haben, das sichsource
in einem anderen Verzeichnis befindet usw.), funktioniert es immer noch).${BASH_SOURCE[0]}
, dass Sie nur den neuesten Anruf wünschen? MitDIR=$(dirname ${BASH_SOURCE[0]})
können Sie auch die if-Bedingung loswerdenEine Alternative zu:
ist:
.. der Vorteil ist, dass Sie nicht von dirname abhängig sind, was kein integrierter Befehl ist (und in Emulatoren nicht immer verfügbar ist).
quelle
basePath=$(dirname $0)
gab mir einen leeren Wert, wenn die enthaltene Skriptdatei bezogen wurde.Wenn es sich im selben Verzeichnis befindet, können Sie Folgendes verwenden
dirname $0
:quelle
$0
is./t.sh
und dirname kehren zurück.
; 2) nachcd bin
der Rücksendung.
ist nicht korrekt.$BASH_SOURCE
ist nicht besser.source "$(dirname $0)/incl.sh"
funktioniert für diese FälleIch denke, der beste Weg, dies zu tun, besteht darin, den Weg von Chris Boran zu verwenden, ABER Sie sollten MY_DIR folgendermaßen berechnen:
So zitieren Sie die Manpages für readlink:
Ich bin noch nie auf einen Anwendungsfall gestoßen, bei dem die
MY_DIR
Berechnung nicht korrekt ist. Wenn Sie über einen Symlink in Ihrem Skript auf Ihr Skript zugreifen$PATH
, funktioniert es.quelle
$0
direkt zu verwenden?/home/you/script.sh
Sie könnencd /home
und führen Sie Ihr Skript von dort aus wie./you/script.sh
in diesem Falldirname $0
wird zurückkehren./you
und das Einschließen andererMY_DIR=$(dirname $(readlink -f $0)); source $MY_DIR/incl.sh
Eine Kombination der Antworten auf diese Frage bietet die robusteste Lösung.
Es funktionierte für uns in produktionsfähigen Skripten mit großer Unterstützung von Abhängigkeiten und Verzeichnisstrukturen:
Die Methode unterstützt alle diese:
readlink
)${BASH_SOURCE[0]}
ist robuster als$0
quelle
readlink -f "${BASH_SOURCE[0]}" 2>/dev/null||echo $0
wenn Ihr Readlink BusyBox v1.01 istDIR=$(dirname $(readlink -f "${BASH_SOURCE[0]}" 2>/dev/null||echo $0)) # https://stackoverflow.com/a/34208365/
quelle
./incl.sh
auf den gleichen Pfad wiecd
+ aufgelöstpwd
. Was ist der Vorteil eines Verzeichniswechsels?Dies funktioniert auch dann, wenn das Skript verwendet wird:
quelle
BASH_SOURCE
ist ein Array von Pfaden, die einem Aufrufstapel entsprechen. Das erste Element entspricht dem neuesten Skript im Stapel, dem aktuell ausgeführten Skript. Tatsächlich wird$BASH_SOURCE
eine als Variable aufgerufene Variable standardmäßig auf ihr erstes Element erweitert, sodass[0]
dies hier nicht erforderlich ist. Siehe diesen Link für Details.1. Am ordentlichsten
Ich habe fast jeden Vorschlag untersucht und hier ist der ordentlichste, der für mich funktioniert hat:
script_root=$(dirname $(readlink -f $0))
Es funktioniert auch, wenn das Skript mit einem
$PATH
Verzeichnis verknüpft ist .Sehen Sie es hier in Aktion: https://github.com/pendashteh/hcagent/blob/master/bin/hcagent
2. Das coolste
Dies ist eigentlich eine andere Antwort auf dieser Seite, aber ich füge sie auch meiner Antwort hinzu!
2. Das zuverlässigste
In dem seltenen Fall, dass diese nicht funktionierten, ist hier der kugelsichere Ansatz:
Sie können es in Aktion in der
taskrunner
Quelle sehen: https://github.com/pendashteh/taskrunner/blob/master/bin/taskrunnerHoffe das hilft jemandem da draußen :)
Bitte hinterlassen Sie es auch als Kommentar, wenn einer für Sie nicht funktioniert hat, und geben Sie Ihr Betriebssystem und Ihren Emulator an. Vielen Dank!
quelle
Sie müssen den Speicherort der anderen Skripte angeben, es gibt keinen anderen Weg, um dies zu umgehen. Ich würde eine konfigurierbare Variable oben in Ihrem Skript empfehlen:
Alternativ können Sie darauf bestehen, dass der Benutzer eine Umgebungsvariable verwaltet, die angibt, wo sich Ihr Programm zu Hause befindet, z. B. PROG_HOME oder ähnliches. Dies kann für den Benutzer automatisch bereitgestellt werden, indem in /etc/profile.d/ ein Skript mit diesen Informationen erstellt wird, das bei jeder Anmeldung eines Benutzers abgerufen wird.
quelle
Ich würde vorschlagen, dass Sie ein Setenv-Skript erstellen, dessen einziger Zweck darin besteht, Speicherorte für verschiedene Komponenten in Ihrem System bereitzustellen.
Alle anderen Skripte würden dann dieses Skript als Quelle verwenden, sodass alle Speicherorte für alle Skripte mit dem Setenv-Skript gleich sind.
Dies ist sehr nützlich, wenn Sie Cronjobs ausführen. Wenn Sie cron ausführen, erhalten Sie eine minimale Umgebung. Wenn Sie jedoch festlegen, dass alle cron-Skripte zuerst das setenv-Skript enthalten, können Sie die Umgebung steuern und synchronisieren, in der die cronjobs ausgeführt werden sollen.
Wir haben eine solche Technik bei unserem Build-Affen verwendet, die für die kontinuierliche Integration in ein Projekt von etwa 2.000 kSLOC verwendet wurde.
quelle
Steves Antwort ist definitiv die richtige Technik, sollte jedoch überarbeitet werden, damit sich Ihre Installationspfadvariable in einem separaten Umgebungsskript befindet, in dem alle derartigen Deklarationen abgegeben werden.
Dann beziehen alle Skripte dieses Skript und sollten sich der Installationspfad ändern, müssen Sie es nur an einem Ort ändern. Macht die Dinge zukunftssicherer. Gott, ich hasse dieses Wort! (-:
Übrigens: Sie sollten die Variable wirklich mit $ {installpath} referenzieren, wenn Sie sie wie in Ihrem Beispiel gezeigt verwenden:
Wenn die geschweiften Klammern weggelassen werden, versuchen einige Shells, die Variable "installpath / incl.sh" zu erweitern!
quelle
Shell Script Loader ist meine Lösung dafür.
Es bietet eine Funktion namens include (), die in vielen Skripten mehrmals aufgerufen werden kann, um auf ein einzelnes Skript zu verweisen, das Skript jedoch nur einmal lädt. Die Funktion kann vollständige oder teilweise Pfade akzeptieren (das Skript wird in einem Suchpfad durchsucht). Eine ähnliche Funktion namens load () wird ebenfalls bereitgestellt, mit der die Skripte bedingungslos geladen werden.
Es funktioniert für bash , ksh , pd ksh und zsh mit optimierten Skripten für jedes von ihnen; und andere Shells, die generisch mit dem Original-Sh kompatibel sind, wie Asche , Armaturenbrett , Erbstück Sh usw., über ein universelles Skript, das seine Funktionen abhängig von den Funktionen, die die Shell bieten kann, automatisch optimiert.
[Geflügeltes Beispiel]
start.sh
Dies ist ein optionales Starterskript. Das Platzieren der Startmethoden hier ist nur eine Annehmlichkeit und kann stattdessen im Hauptskript platziert werden. Dieses Skript wird auch nicht benötigt, wenn die Skripte kompiliert werden sollen.
main.sh
Asche
b.sh.
Ausgabe:
Das Beste daran ist, dass darauf basierende Skripte auch zu einem einzigen Skript mit dem verfügbaren Compiler kompiliert werden können.
Hier ist ein Projekt, das es verwendet: http://sourceforge.net/p/playshell/code/ci/master/tree/ . Es kann portabel mit oder ohne Kompilieren der Skripte ausgeführt werden. Das Kompilieren zur Erstellung eines einzelnen Skripts kann ebenfalls erfolgen und ist bei der Installation hilfreich.
Ich habe auch einen einfacheren Prototyp für jede konservative Partei erstellt, die eine kurze Vorstellung davon haben möchte, wie ein Implementierungsskript funktioniert: https://sourceforge.net/p/loader/code/ci/base/tree/loader-include-prototype .bash . Es ist klein und jeder kann den Code einfach in sein Hauptskript aufnehmen, wenn er möchte, wenn sein Code mit Bash 4.0 oder neuer ausgeführt werden soll, und er wird auch nicht verwendet
eval
.quelle
eval
ed-Code zum Laden von Abhängigkeiten. Autscheval
Blöcke in Bodennähe wird immer ausgeführt. Also, ob es benötigt wird oder nicht, es wird sicher verwendeteval
.eval
sei rein böse, und nicht wissen, wie sie es stattdessen gut gebrauchen sollen.eval
das böse ist.Legen Sie alle Bibliotheken persönlich in einem
lib
Ordner ab undimport
laden Sie sie mithilfe einer Funktion.Ordnerstruktur
script.sh
InhaltBeachten Sie, dass diese Importfunktion am Anfang Ihres Skripts stehen sollte und Sie Ihre Bibliotheken dann einfach wie folgt importieren können:
Fügen Sie oben in jeder Bibliothek eine einzelne Zeile hinzu (z. B. utils.sh):
Jetzt haben Sie Zugriff auf Funktionen innerhalb
utils.sh
undrequirements.sh
vonscript.sh
TODO: Schreiben Sie einen Linker, um eine einzelne
sh
Datei zu erstellenquelle
Wenn Sie source oder $ 0 verwenden, erhalten Sie nicht den tatsächlichen Pfad Ihres Skripts. Sie können die Prozess-ID des Skripts verwenden, um den tatsächlichen Pfad abzurufen
Ich benutze dieses Skript und es hat mir immer gut gedient :)
quelle
Ich habe alle meine Startskripte in einem .bashrc.d-Verzeichnis abgelegt. Dies ist eine übliche Technik an Orten wie /etc/profile.d usw.
Das Problem mit der Lösung mit Globbing ...
... ist, dass Sie möglicherweise eine Dateiliste haben, die "zu lang" ist. Ein Ansatz wie ...
... läuft, ändert aber nicht die gewünschte Umgebung.
quelle
Natürlich für jeden sein eigenes, aber ich denke, der Block unten ist ziemlich solide. Ich glaube, dies beinhaltet den "besten" Weg, ein Verzeichnis zu finden, und den "besten" Weg, ein anderes Bash-Skript aufzurufen:
Dies ist möglicherweise der "beste" Weg, um andere Skripte einzuschließen. Dies basiert auf einer anderen "besten" Antwort, die einem Bash-Skript mitteilt, wo es gespeichert ist
quelle
Dies sollte zuverlässig funktionieren:
quelle
wir müssen nur den Ordner herausfinden, in dem unsere inkl.sh und main.sh gespeichert sind; Ändern Sie einfach Ihre main.sh damit:
main.sh
quelle
echo
und falscher Verwendung vonsed
‚s -g
Option. -1.SCRIPT_DIR=$(echo "$0" | sed "s/${SCRIPT_NAME}//")
source "${SCRIPT_DIR}incl.sh"
Entsprechend
man hier
geeigneter Stelle für das Skript ist/usr/local/lib/
Persönlich bevorzuge ich
/usr/local/lib/bash/includes
für enthält. Es gibt eine Bash-Helfer- Bibliothek, um Bibliotheken auf diese Weise einzuschließen:quelle
Sie können auch verwenden:
quelle