La façon dont vous incluez normalement un script est avec "source"
par exemple:
main.sh:
#!/bin/bash
source incl.sh
echo "The main script"
y compris sh:
echo "The included script"
Le résultat de l'exécution de "./main.sh" est:
The included script
The main script
... Maintenant, si vous essayez d'exécuter ce script shell à partir d'un autre emplacement, il ne peut pas trouver l'inclusion à moins qu'il ne se trouve sur votre chemin.
Quelle est la bonne façon de s'assurer que votre script peut trouver le script include, surtout si, par exemple, le script doit être portable?
Réponses:
J'ai tendance à faire en sorte que mes scripts soient tous relatifs les uns aux autres. De cette façon, je peux utiliser dirname:
la source
which $0
sera utileBASH_SOURCE
tableau et comment le premier élément de ce tableau pointe toujours vers la source actuelle.Je sais que je suis en retard à la fête, mais cela devrait fonctionner, peu importe la façon dont vous démarrez le script et utilisez exclusivement les commandes intégrées:
.
La commande (dot) est un alias desource
,$PWD
est le chemin d'accès au répertoire de travail,BASH_SOURCE
est une variable de tableau dont les membres sont les noms de fichiers source,${string%substring}
supprime la correspondance la plus courte de $ substring à l'arrière de $ stringla source
if [[ ! -d "$DIR" ]]; then DIR="$PWD"; fi
nécessaire? Je peux en trouver le besoin si les commandes sont collées dans une invite bash à exécuter. Cependant, si je cours dans un contexte de fichier de script, je ne vois pas la nécessité de le faire ...source
s (je veux dire, si voussource
un script qui ensource
est un autre dans un autre répertoire et ainsi de suite, cela fonctionne toujours).${BASH_SOURCE[0]}
comme vous ne voulez que le dernier appelé? De plus, l'utilisationDIR=$(dirname ${BASH_SOURCE[0]})
vous permettra de vous débarrasser de la conditionUne alternative à:
est:
.. l'avantage étant de ne pas avoir la dépendance de dirname, qui n'est pas une commande intégrée (et pas toujours disponible dans les émulateurs)
la source
basePath=$(dirname $0)
m'a donné une valeur vide lorsque le fichier de script contenant est d'origine.S'il se trouve dans le même répertoire, vous pouvez utiliser
dirname $0
:la source
$0
is./t.sh
et dirname retournent.
; 2) aprèscd bin
le retour.
n'est pas correct.$BASH_SOURCE
n'est pas mieux.source "$(dirname $0)/incl.sh"
fonctionne pour ces casJe pense que la meilleure façon de le faire est d'utiliser la méthode de Chris Boran, MAIS vous devriez calculer MY_DIR de cette façon:
Pour citer les pages de manuel de readlink:
Je n'ai jamais rencontré de cas d'utilisation où il
MY_DIR
n'est pas correctement calculé. Si vous accédez à votre script via un lien symbolique dans votre,$PATH
cela fonctionne.la source
$0
directement?/home/you/script.sh
Vous pouvezcd /home
et exécutez votre script à partir de là car./you/script.sh
dans ce cas,dirname $0
il reviendra./you
et l'inclusion d'un autre script échoueraMY_DIR=$(dirname $(readlink -f $0)); source $MY_DIR/incl.sh
Une combinaison des réponses à cette question fournit la solution la plus robuste.
Cela a fonctionné pour nous dans les scripts de production avec un grand support des dépendances et de la structure des répertoires:
La méthode prend en charge tous ces éléments:
readlink
)${BASH_SOURCE[0]}
est plus robuste que$0
la source
readlink -f "${BASH_SOURCE[0]}" 2>/dev/null||echo $0
si votre lien de lecture est BusyBox v1.01DIR=$(dirname $(readlink -f "${BASH_SOURCE[0]}" 2>/dev/null||echo $0)) # https://stackoverflow.com/a/34208365/
la source
./incl.sh
résout le même chemin quecd
+pwd
. Alors, quel est l'avantage de changer de répertoire?Cela fonctionne même si le script provient:
la source
BASH_SOURCE
est un tableau de chemins, correspondant à une pile d'appels. Le premier élément correspond au script le plus récent de la pile, qui est le script en cours d'exécution. En fait,$BASH_SOURCE
appelé comme variable se développe par défaut vers son premier élément, il[0]
n'est donc pas nécessaire ici. Voir ce lien pour plus de détails.1. Le plus net
J'ai exploré presque toutes les suggestions et voici la plus intéressante qui a fonctionné pour moi:
script_root=$(dirname $(readlink -f $0))
Cela fonctionne même lorsque le script est lié à un
$PATH
répertoire.Voyez-le en action ici: https://github.com/pendashteh/hcagent/blob/master/bin/hcagent
2. Le plus cool
C'est en fait d'une autre réponse sur cette même page, mais je l'ajoute aussi à ma réponse!
2. Le plus fiable
Alternativement, dans les rares cas où ceux-ci n'ont pas fonctionné, voici l'approche à l'épreuve des balles:
Vous pouvez le voir en action dans la
taskrunner
source: https://github.com/pendashteh/taskrunner/blob/master/bin/taskrunnerJ'espère que cela aidera quelqu'un là-bas :)
Veuillez également le laisser en commentaire si l'un ne vous convient pas et mentionner votre système d'exploitation et votre émulateur. Merci!
la source
Vous devez spécifier l'emplacement des autres scripts, il n'y a pas d'autre moyen de le contourner. Je recommanderais une variable configurable en haut de votre script:
Alternativement, vous pouvez insister pour que l'utilisateur maintienne une variable d'environnement indiquant où se trouve la page d'accueil de votre programme, comme PROG_HOME ou somesuch. Cela peut être fourni automatiquement à l'utilisateur en créant un script avec ces informations dans /etc/profile.d/, qui seront obtenues à chaque fois qu'un utilisateur se connecte.
la source
Je vous suggère de créer un script setenv dont le seul but est de fournir des emplacements pour divers composants sur votre système.
Tous les autres scripts fourniraient alors ce script afin que tous les emplacements soient communs à tous les scripts utilisant le script setenv.
Ceci est très utile lors de l'exécution de cronjobs. Vous obtenez un environnement minimal lors de l'exécution de cron, mais si vous faites d'abord en sorte que tous les scripts cron incluent le script setenv, vous pouvez contrôler et synchroniser l'environnement dans lequel vous voulez que les cronjobs s'exécutent.
Nous avons utilisé une telle technique sur notre singe de construction qui a été utilisée pour une intégration continue à travers un projet d'environ 2 000 kSLOC.
la source
La réponse de Steve est certainement la bonne technique mais elle devrait être refactorisée afin que votre variable installpath soit dans un script d'environnement séparé où toutes ces déclarations sont faites.
Ensuite, tous les scripts source ce script et doivent changer de chemin d'installation, il vous suffit de le changer dans un seul emplacement. Rend les choses plus, euh, à l'épreuve du temps. Dieu que je déteste ce mot! (-:
BTW Vous devriez vraiment vous référer à la variable en utilisant $ {installpath} lorsque vous l'utilisez de la manière indiquée dans votre exemple:
Si les accolades sont omises, certains shells essaieront et développeront la variable "installpath / incl.sh"!
la source
Shell Script Loader est ma solution pour cela.
Il fournit une fonction nommée include () qui peut être appelée plusieurs fois dans de nombreux scripts pour faire référence à un seul script mais ne chargera le script qu'une seule fois. La fonction peut accepter des chemins complets ou des chemins partiels (le script est recherché dans un chemin de recherche). Une fonction similaire nommée load () est également fournie qui chargera les scripts sans condition.
Il fonctionne pour bash , ksh , pd ksh et zsh avec des scripts optimisés pour chacun d'eux; et d'autres shells qui sont génériquement compatibles avec le sh d'origine comme ash , dash , heirloom sh , etc., grâce à un script universel qui optimise automatiquement ses fonctions en fonction des fonctionnalités que le shell peut fournir.
[Exemple présenté]
start.sh
Il s'agit d'un script de démarrage facultatif. Placer les méthodes de démarrage ici n'est qu'une commodité et peut être placé à la place dans le script principal. Ce script n'est également pas nécessaire si les scripts doivent être compilés.
main.sh
cendre
b.sh
production:
Ce qui est le mieux, c'est que les scripts basés dessus peuvent également être compilés pour former un seul script avec le compilateur disponible.
Voici un projet qui l'utilise: http://sourceforge.net/p/playshell/code/ci/master/tree/ . Il peut s'exécuter de manière portable avec ou sans compilation des scripts. La compilation pour produire un seul script peut également se produire et est utile lors de l'installation.
J'ai également créé un prototype plus simple pour toute partie conservatrice qui voudrait avoir une brève idée du fonctionnement d'un script d'implémentation: https://sourceforge.net/p/loader/code/ci/base/tree/loader-include-prototype .bash . Il est petit et n'importe qui peut simplement inclure le code dans son script principal s'il le souhaite si son code est destiné à fonctionner avec Bash 4.0 ou plus récent, et il ne l'utilise pas non plus
eval
.la source
eval
code ed pour charger les dépendances. Oucheval
blocs près du bas est toujours exécuté. Donc, que ce soit nécessaire ou non, il utilise certainementeval
.eval
c'est du mal pur, et ne sait pas comment en faire bon usage à la place.eval
c'est mal.Mettez personnellement toutes les bibliothèques dans un
lib
dossier et utilisez uneimport
fonction pour les charger.structure des dossiers
script.sh
ContenuNotez que cette fonction d'importation devrait être au début de votre script et que vous pouvez ensuite facilement importer vos bibliothèques comme ceci:
Ajoutez une seule ligne en haut de chaque bibliothèque (ie utils.sh):
Vous avez maintenant accès aux fonctions à l'intérieur
utils.sh
et àrequirements.sh
partir descript.sh
TODO: Écrivez un éditeur de liens pour créer un seul
sh
fichierla source
Utiliser source ou $ 0 ne vous donnera pas le vrai chemin de votre script. Vous pouvez utiliser l'ID de processus du script pour récupérer son vrai chemin
J'utilise ce script et il m'a toujours bien servi :)
la source
J'ai mis tous mes scripts de démarrage dans un répertoire .bashrc.d. Il s'agit d'une technique courante dans des endroits tels que /etc/profile.d, etc.
Le problème avec la solution utilisant le globbing ...
... vous avez peut-être une liste de fichiers "trop longue". Une approche comme ...
... s'exécute mais ne change pas l'environnement comme vous le souhaitez.
la source
Bien sûr, à chacun, mais je pense que le bloc ci-dessous est assez solide. Je crois que cela implique la "meilleure" façon de trouver un répertoire et la "meilleure" façon d'appeler un autre script bash:
C'est donc peut-être la "meilleure" façon d'inclure d'autres scripts. Ceci est basé sur une autre "meilleure" réponse qui indique à un script bash où il est stocké
la source
Cela devrait fonctionner de manière fiable:
la source
nous avons juste besoin de trouver le dossier où nos incl.sh et main.sh sont stockés; changez simplement votre main.sh avec ceci:
main.sh
la source
echo
et utilisation incorrecte desed
l'g
option de. -1.SCRIPT_DIR=$(echo "$0" | sed "s/${SCRIPT_NAME}//")
puissource "${SCRIPT_DIR}incl.sh"
L'
man hier
endroit approprié pour le script comprend/usr/local/lib/
Personnellement, je préfère les
/usr/local/lib/bash/includes
inclus. Il existe une bibliothèque bash-helper pour inclure des bibliothèques de cette manière:la source
Vous pouvez aussi utiliser:
la source