459 Stimmen

Wie lassen sich andere Skripte am besten einbinden?

Normalerweise würde man ein Skript mit "source" einbinden

z. B:

main.sh:

#!/bin/bash

source incl.sh

echo "The main script"

incl.sh:

echo "The included script"

Die Ausgabe der Ausführung von "./main.sh" ist:

The included script
The main script

... Wenn Sie nun versuchen, das Shell-Skript von einem anderen Ort aus auszuführen, kann es das Include nicht finden, es sei denn, es befindet sich in Ihrem Pfad.

Wie kann man sicherstellen, dass das Skript das Include-Skript findet, insbesondere wenn das Skript beispielsweise portabel sein muss?

7voto

Rob Wells Punkte 35303

Ich würde vorschlagen, dass Sie ein setenv-Skript erstellen, dessen einziger Zweck es ist, die Speicherorte für verschiedene Komponenten in Ihrem System bereitzustellen.

Alle anderen Skripte würden dann dieses Skript als Quelle verwenden, so dass alle Speicherorte für alle Skripte mit dem Skript setenv gleich sind.

Dies ist sehr nützlich bei der Ausführung von Cronjobs. Sie erhalten eine minimale Umgebung, wenn Sie cron ausführen, aber wenn Sie alle Cron-Skripte zuerst das setenv-Skript einbinden, können Sie die Umgebung, in der die Cron-Jobs ausgeführt werden sollen, kontrollieren und synchronisieren.

Wir haben eine solche Technik bei unserem Build-Monkey eingesetzt, der für die kontinuierliche Integration eines Projekts von etwa 2.000 kSLOC verwendet wurde.

4voto

konsolebox Punkte 66082

Shell-Skript-Lader ist meine Lösung für dieses Problem.

Es stellt eine Funktion namens include() zur Verfügung, die in vielen Skripten mehrfach aufgerufen werden kann, um ein einzelnes Skript zu referenzieren, das Skript aber nur einmal lädt. Die Funktion kann vollständige Pfade oder Teilpfade akzeptieren (das Skript wird in einem Suchpfad gesucht). Eine ähnliche Funktion namens load() wird ebenfalls bereitgestellt, die die Skripte bedingungslos lädt.

Es funktioniert für bash , ksh , pd ksh y zsh mit optimierte Skripte für jede von ihnen; und andere Shells, die allgemein mit dem ursprünglichen sh kompatibel sind, wie Asche , Bindestrich , Erbstück sh usw. durch ein universelles Skript, das seine Funktionen automatisch in Abhängigkeit von den Funktionen optimiert, die die Shell bieten kann.

[Beispiel gefällig]

start.sh

Dies ist ein optionales Startskript. Die Startmethoden werden hier nur aus Bequemlichkeit platziert und können stattdessen in das Hauptskript eingefügt werden. Dieses Skript wird auch nicht benötigt, wenn die Skripte kompiliert werden sollen.

#!/bin/sh

# load loader.sh
. loader.sh

# include directories to search path
loader_addpath /usr/lib/sh deps source

# load main script
load main.sh

main.sh

include a.sh
include b.sh

echo '---- main.sh ----'

# remove loader from shellspace since
# we no longer need it
loader_finish

# main procedures go from here

# ...

a.sh

include main.sh
include a.sh
include b.sh

echo '---- a.sh ----'

b.sh

include main.sh
include a.sh
include b.sh

echo '---- b.sh ----'

Ausgabe:

---- b.sh ----
---- a.sh ----
---- main.sh ----

Das Beste ist, dass darauf basierende Skripte auch mit dem verfügbaren Compiler zu einem einzigen Skript 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 Kompilierung der Skripte ausgeführt werden. Das Kompilieren, um ein einzelnes Skript zu erzeugen, kann ebenfalls erfolgen und ist bei der Installation hilfreich.

Ich habe auch einen einfacheren Prototyp für alle konservativen Parteien erstellt, die eine kurze Vorstellung davon haben möchten, 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 einbinden, wenn er möchte, dass sein Code mit Bash 4.0 oder neuer läuft, und es benutzt auch nicht eval .

3voto

Rob Wells Punkte 35303

Steves Antwort ist definitiv die richtige Technik, aber sie sollte so umgestaltet werden, dass die installpath-Variable in einem separaten Umgebungsskript steht, in dem alle derartigen Deklarationen vorgenommen werden.

Dann wird dieses Skript von allen Skripten verwendet, und wenn sich der Installationspfad ändert, müssen Sie ihn nur an einer Stelle ändern. Das macht die Sache, äh, zukunftssicherer. Gott, ich hasse dieses Wort! (-:

BTW Sie sollten die Variable wirklich mit ${installpath} bezeichnen, wenn Sie sie so wie in Ihrem Beispiel verwenden:

. ${installpath}/incl.sh

Wenn die geschweiften Klammern weggelassen werden, versuchen einige Shells, die Variable "installpath/incl.sh" zu erweitern!

2voto

phreed Punkte 1709

Ich lege alle meine Startskripte in einem .bashrc.d-Verzeichnis ab. Dies ist eine gängige Technik in Verzeichnissen wie /etc/profile.d usw.

while read file; do source "${file}"; done <<HERE
$(find ${HOME}/.bashrc.d -type f)
HERE

Das Problem bei der Lösung mit Globbing...

for file in ${HOME}/.bashrc.d/*.sh; do source ${file};done

...ist, dass Sie möglicherweise eine "zu lange" Dateiliste haben. Ein Ansatz wie...

find ${HOME}/.bashrc.d -type f | while read file; do source ${file}; done

...läuft, aber verändert die Umgebung nicht wie gewünscht.

2voto

PSkocik Punkte 54603

Dies sollte zuverlässig funktionieren:

source_relative() {
 local dir="${BASH_SOURCE%/*}"
 [[ -z "$dir" ]] && dir="$PWD"
 source "$dir/$1"
}

source_relative incl.sh

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