Référez-vous à un fichier situé dans le même répertoire que le script trouvé dans $ PATH

33

J'ai un fichier de script bash, qui est placé dans un répertoire ajouté à $ PATH afin que je puisse appeler le script à partir de n'importe quel répertoire.

Il existe un autre fichier texte dans le même répertoire que le script. Je me demande comment faire référence au fichier texte dans le script?

Par exemple, si le script doit uniquement générer le contenu du fichier texte, cat textfilecela ne fonctionnera pas, car lorsqu’il appelle le script depuis un autre répertoire, le fichier texte n’est pas trouvé.

Tim
la source
Un problème connexe a récemment été soulevé ici: Obtenir le chemin du script actuel lorsqu'il est exécuté via un lien symbolique
Caleb
Cette question explique comment obtenir de manière fiable un chemin de fichiers de script bash, que j'ai ajouté à mon chemin: stackoverflow.com/q/4774054/1695680
ThorSummoner

Réponses:

24

Celles-ci devraient fonctionner de la même manière, tant qu'il n'y a pas de lien symbolique (dans l'extension du chemin ou le script lui-même):

  • MYDIR="$(dirname "$(realpath "$0")")"

  • MYDIR="$(dirname "$(which "$0")")"

  • Une version en deux étapes de tout ce qui précède:

    MYSELF="$(realpath "$0")"

    MYDIR="${MYSELF%/*}"

S'il y a un lien symbolique sur le chemin de votre script, il whichfournira une réponse n'incluant pas la résolution de ce lien. Si realpathn'est pas installé par défaut sur votre système, vous pouvez le trouver ici .

[EDIT]: Comme il ne semble y realpathavoir aucun avantage par rapport à ce que readlink -f suggère Caleb , il est probablement préférable d’utiliser ce dernier. Mes tests de chronométrage indiquent qu'il est effectivement plus rapide.

rozcietrzewiacz
la source
Aucun problème. Au fait, d'où realpathvient votre système? (Pour les autres qui ne l'ont pas, vous pouvez utiliserreadlink -f
Caleb
@Caleb En fait, je pensais que cela appartenait à l'ensemble des utilitaires GNU standard (coreutils), mais je peux voir maintenant qu'il s'agit d'un paquet séparé .
rozcietrzewiacz
@rozcietrzewiacz realpathremonte à avant readlink -f(et même readlink, IIRC) était dans GNU coreutils (il existait plusieurs outils similaires autour. est readlink -ffinalement devenu le standard de facto); realpathn'est conservé que pour des raisons de compatibilité avec les scripts qui l'utilisent encore.
Gilles, arrête de faire le mal '
Quel est l'avantage de $(dirname "$(which "$0")")sur $(dirname $0)où le whichn'est plus présent? N'est-ce pas pareil?
UlfR
readlink -fne semble pas fonctionner sur Mac OS X 10.11.6, mais realpathfonctionne tout de suite.
Grav
9

Mes systèmes n'ont pas realpathcomme suggéré par rozcietrzewiacz .

Vous pouvez accomplir cela en utilisant la readlinkcommande. L'avantage d'utiliser cette méthode par rapport à l'analyse syntaxique whichou à d'autres solutions est que, même si une partie du chemin ou du nom de fichier exécuté était un lien symbolique, vous seriez en mesure de trouver le répertoire dans lequel se trouvait le fichier réel.

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

Votre fichier texte pourrait alors être lu dans une variable comme celle-ci:

TEXTFILE="$(<$MYDIR/textfile)"
Caleb
la source
@rozcietrzewiacz: En fait, je ne parlais pas seulement de votre whichsuggestion. La solution normale à cela implique soit simplement, dirnamesoit une combinaison de cdet pwddans un sous-shell. Readlink a l'avantage ici. realpathsemble à peu près être juste un wrapper pour de readlink -ftoute façon.
Caleb
Je ne sais pas en quoi realpathc'est différent de readlink -f. Je peux seulement voir que cela me donne les mêmes résultats (par opposition à which).
rozcietrzewiacz
Gardez à l'esprit que readlink -f(de GNU coreutils) ne nécessite PAS que le dernier élément du chemin existe, existe readlink -e, mais n'est pas pris en charge busybox readlink, ce qui imite le comportement de -eleur -foption.
dragon788
8

$0dans le script sera le chemin complet du script, et dirnameprendra un chemin complet et ne vous donnera que le répertoire afin que vous puissiez le faire pour cat textfile:

$ cat "$(dirname -- "$0")/textfile"
Michael Mrozek
la source
Bien que cela semble fonctionner sans realpath $0, vous avez tort de dire que " $0dans le script sera le chemin complet du script".
rozcietrzewiacz
@roz De quelle manière?
Michael Mrozek
1
$0est la commande telle qu'elle a été exécutée, ce qui peut être par exemple ../script.sh.
rozcietrzewiacz le
Donc, $(dirname "$0")renvoie en fait le chemin relatif au script, dans le cadre de la commande appelée - et non le chemin absolu. Cela peut entraîner des problèmes dans les scripts qui changent de répertoire en cours d’exécution.
rozcietrzewiacz le
@roz Ah, intéressant. Donc, je suppose que cela ne poserait pas de problème ici puisqu'il appelle quelque chose sur le chemin par son nom, mais cela casserait d'autres choses. Merci
Michael Mrozek
4

Vous pouvez mettre ceci en haut de votre script:

cd "${BASH_SOURCE%/*}" || exit

La variable bash interne BASH_SOURCE est en réalité un tableau de noms de chemins. Si vous le développez sous la forme d'une simple chaîne, par exemple "$ BASH_SOURCE", vous obtiendrez le premier élément, qui est le chemin d'accès de la fonction ou du script en cours d'exécution.

Source: http://mywiki.wooledge.org/BashFAQ/028

David Kennedy
la source
2

J'essayais cela et realpath ne fonctionnait pas pour moi. La solution que j'ai choisie est la suivante:

SCRIPTDIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )

Ce qui a bien fonctionné jusqu'à présent. J'aimerais savoir s'il y a des problèmes potentiels avec l'approche.

Brettski
la source
1
Bien, mais ne suit pas les liens symboliques. Essayez ceci:SCRIPT_DIR="$( cd "$(dirname "$( readlink -f ${BASH_SOURCE[0]} )")" >/dev/null 2>&1 && pwd)"
OronNavon
1

J'utilise:

#! /bin/sh -
dir=$(cd -P -- "$(dirname -- "$0")" && pwd -P) || exit
dosomethingwith "${dir%/}/some-file"

Ce qui est POSIX et devrait fonctionner tant que le nom du répertoire $0ne se termine pas par des caractères de nouvelle ligne, n’est pas -et $CDPATHn’est pas défini (et éventuellement quelques autres cas de coin si le script n’a pas été consulté $PATH).

Stéphane Chazelas
la source
0

J'utilise toujours whichpour trouver le chemin complet d'un exécutable à partir de PATH. Par exemple:

which python

Si vous combinez cela avec la dirnamecommande, vous obtenez:

wp=`which python`
dn=`dirname $wp`
ls $dn
Michael Dillon
la source