Rechercher l'emplacement du script shell source

8

Est-il possible qu'un script shell d'origine connaisse son emplacement? J'ai lu chemin à la détermination script shell d' origine mais les réponses concentrer sur bashet tcshet échouer si un shell POSIX est utilisé. $0n'est pas non plus la solution et donne de mauvais résultats .

Une solution n'a pas besoin d'être fiable à 100%. Il est peu probable que le chemin contienne des liens durs ou des liens symboliques.

# sourcing the script should yield the absolute path to the script
. somedir/thescript

# within “thescript”
-> /tmp/foo/bar/somedir

Quelques informations: le script fait partie d'une application existante contenant des dizaines de fichiers binaires dans un binrépertoire à un emplacement connu (varie selon l'architecture) par rapport au script d'origine. Pour utiliser l'application, l'utilisateur source le script qui ajoute le binrépertoire au PATH, afin que les fichiers binaires de l'application puissent être facilement invoqués dans le shell actuel (quel que soit le shell qui pourrait être).

Marco
la source
S'il vous plaît une fois vérifier ce lien pourrait être ce que vous recherchez paste.ubuntu.com/6242655
Rahul Patil
@RahulPatil Cela ne fonctionne que sur bash et est très peu transférable.
Marco
1
Je suis sûr que la réponse est non. Vous n'avez trouvé que des méthodes spécifiques au shell car il n'y a pas de méthode portable. Pourquoi avez-vous besoin de cela? Pouvez-vous dire aux utilisateurs du script de faire quelque chose de différent de . /path/to/scripttel que MARCO_DIR=/path/to; . $MARCO_DIR/scriptou eval "$(/path/to/script)"? @RahulPatil BASH_SOURCEest évidemment spécifique à bash.
Gilles 'SO- arrête d'être méchant'
@Gilles Comme je l'ai dit, il n'a pas besoin d'être très robuste. Mais je n'ai pas trouvé un moyen qui fonctionne même à distance dans les cas les plus simples dans un shell POSIX. (Donc , la solution pour l'instant est que l'utilisation de cette application nécessite l' une des coques pris en charge.) Ne hésitez pas à poster « non ... » comme une réponse si c'est la réponse.
Marco
@Gilles Changer la configuration de l'application n'est pas une option. Cela casserait les installations. C'est pourquoi j'aurais aimé rendre le script plus robuste sans changer la façon dont il s'appelle. En fin de compte, il définit simplement le PATH, donc si le script échoue, le repli consiste à trouver les binaires et à ajouter leur chemin d'accès à l'environnement.
Marco

Réponses:

9

L'emplacement du script d'origine n'est pas disponible sauf si vous utilisez un shell qui offre des extensions à la spécification POSIX. Vous pouvez tester cela avec l'extrait de code suivant:

env -i PATH=/usr/bin:/bin sh -c '. ./included.sh' | grep included

included.shcontient

echo "$0"
set

En bash, le nom du script source est dans $BASH_SOURCE. En zsh (en mode de compatibilité zsh, pas en mode de compatibilité sh ou ksh), c'est dans $0(notez que dans une fonction, $0c'est le nom de la fonction à la place). Dans pdksh et dash, il n'est pas disponible. Dans ksh93, cette méthode ne révèle pas la solution, mais le chemin complet vers le script inclus est disponible en tant que ${.sh.file}.

Si nécessiter bash ou ksh93 ou zsh est suffisant, vous pouvez utiliser cet extrait:

if [ -n "$BASH_SOURCE" ]; then
  this_script=$BASH_SOURCE
elif [ -n "$ZSH_VERSION" ]; then
  setopt function_argzero
  this_script=$0
elif eval '[[ -n ${.sh.file} ]]' 2>/dev/null; then
  eval 'this_script=${.sh.file}'
else
  echo 1>&2 "Unsupported shell. Please use bash, ksh93 or zsh."
  exit 2
fi

Vous pouvez essayer de deviner l'emplacement du script en examinant les fichiers ouverts par le shell. Expérimentalement, cela semble fonctionner avec dash et pdksh mais pas avec bash ou ksh93 qui au moins pour un court script ont fermé le fichier de script au moment où ils se mettent à l'exécuter.

  open_file=$(lsof -F n -p $$ | sed -n '$s/^n//p')
  if [ -n "$open_file" ]; then
    # best guess: $open_file is this script
  fi

Le script peut ne pas être le fichier avec le descripteur le plus élevé si le script provient d'un script complexe qui a joué avec des redirections. Vous voudrez peut-être parcourir les fichiers ouverts. Ce n'est pas garanti de fonctionner de toute façon. La seule façon fiable de localiser un script source est d'utiliser bash, ksh93 ou zsh.


Si vous pouvez modifier l'interface, au lieu de rechercher votre script, demandez à votre script d'imprimer un extrait de shell à transmettre à evall'appelant. C'est ce que font généralement les scripts pour définir les variables d'environnement. Il permet à votre script d'être écrit indépendamment des aléas du shell de l'appelant et de la configuration du shell.

#!/bin/sh
FOO_DIR=$(dirname -- "$0")
cat <<EOF
FOO_DIR='$(printf %s "$FOO_DIR" | sed "s/'/'\\''/g")'
PATH="\$PATH:$FOO_DIR/bin";
export FOO_DIR PATH
EOF

Dans l'appelant: eval "`/path/to/setenv`"

Gilles 'SO- arrête d'être méchant'
la source
0

En ajoutant à la réponse de Gilles, vous POUVEZ être en mesure d'obtenir le script shell ( if $0 = ash) via le /proc/$$ interface. Je ne sais pas à quel point c'est précis, mais /proc/$$/fd/11semble toujours pointer vers this_script avec les cendres de BusyBox.

Fournir cela comme une réponse potentielle à ceux qui viennent sur cette page (comme moi).

La dernière réponse a été supprimée - ma réclamation pour ce fonctionnement est donc testée sur 4 plates-formes matérielles différentes (x86, ARM, XLP et powerpc). Tous donnent la même réponse de fd / 11. Un lien de lecture de /proc/$$/fd/11donne mon script.

Andy

A. Kennedy
la source