find le directory absolu

Voici le scénario:

foo/ > data/ > stuff/ > scripts/ -> /.../generic/scripts 

Lorsque foo/scripts/bar.sh partir de n'importe quel location (autre dossier ou à l'intérieur du dossier), j'aimerais que le script utilise foo/data et foo/stuff

Cependant, si j'utilise simplement foo_dir='dirname $0'/.. alors il va descendre dans le directory pointé par le lien symbolique! (Et obtenez generic au lieu de foo !)

Si j'utilise un model pour manger jusqu'à la fin de $0 , alors ça ne fonctionne pas non plus puisque vous pouvez aussi appeler le script depuis l'intérieur du directory.

Donc, à la fin, la seule chose que j'ai trouvé est ce truc très laide:

 dir=`dirname $0` cd $dir dir=`pwd | sed -r 's_/[^/]+$__'` cd - 

… mais je suis sûr qu'il y a une meilleure façon, n'est-ce pas?

Ce que j'ai utilisé:

 dir=$( dirname $( cd `dirname $0`; pwd ) ) 

Je ne sais pas si c'est parfait mais il semble se comporter comme prévu.

Désolé de ne pas lire votre question plus attentivement la première fois.

Au lieu de:

 foo_dir = $(dirname $0)/.. 

que diriez-vous:

 foo_dir = $(dirname $(dirname $0)) 

Cependant, cela ne fonctionne que s'il y a au less un composant de directory dans $ 0. Avez-vous cette garantie?

Vous pouvez utiliser le one-liner suivant:

 DIR="$(cd "$( dirname "$0" )" && pwd )" 

Oui, .. en présence de liens symboliques est un vrai problème. Rob Pike a écrit un article entier sur ce problème .

Je vous suggère d'éviter .. en utilisant dirname avec un path d'access absolu, ainsi:

 case $0 in /*) where="$0" ;; *) where="$(pwd)/$0" ;; esac # postcondition: $where is an absolute pathname for script # NB $where = .../foo/scripts/command base="$(dirname "$(dirname "$where")")" # postcondition: $base = .../foo resource="$base/data" 

J'utilise en fait le premier idiome (récupère un nom de path absolu à partir d'un path relatif) que j'ai un script juste pour cela dans mon directory ~/bin .

Une fois que vous avez des paths absolus, dirname se comporte de manière sensée.

Ok, alors ce que vous voulez, c'est que le script soit accessible via le lien symbolique pour pouvoir utiliser des files relatifs au lien symbolique, n'est-ce pas? Et pour que cela fonctionne même si le path utilisé est absolu, relatif, relatif à l'intérieur du même directory, relatif d'un sous-directory, etc.?

Ce n'est en effet pas simple. Étonnamment peu simple, en fait – de nombreux langages de programmation ont une fonction «abspath» qui résoudrait ce problème en un éclair. Cependant, je ne crois pas qu'il existe une telle fonction dans le toolkit de script shell standard.

Cependant, vous pouvez en build un tout simplement, avec un one-liner sed:

 abspath() { { [[ "$1" =~ ^/ ]] && echo "$1" || echo "$(pwd)/$1"; } | sed -r ':. s#(/|^)\./#\1#g; t .; :: s#[^/]{1,}/\.\./##; t :' } foo_dir=$(dirname $(abspath $0)) 

Je crois que c'est tout à fait général, et travaillera pour les paths contenant n'importe quelle quantité de ./ et ../ .

Il utilise des expressions régulières un peu, cependant,

Votre message est un peu décousu et certaines des déclarations semblent contradictoires, mais si ma compréhension est correcte, vous avez un $0 de la forme foo/scripts/bar.sh , et vous essayez d'extraire la partie foo . Vous garantissez en outre qu'il y aura toujours au less deux niveaux de directorys dans ce path (si vous n'avez que bar.sh , vous êtes toast). Ensuite, vous pouvez simplement supprimer les deux derniers composants du path:

 DIR=${0%/*/*} 

Vous pouvez appeler votre script par scripts/bar.sh , cependant, auquel cas le rlocation simple du model ne fonctionnerait pas. Sans oublier d'appeler le script via $PATH . Vous pouvez le rendre conditionnel:

 case $0 in */*/*) DIR=${0%/*/*};; ?*/*) DIR=.;; /*) echo 1>&2 "Storing $0 in the root directory is not supported, aborting." exit 125;; *) # The script was called through the `PATH` IFS=:; set -f; unset DIR for d in $PATH; do if [ -x "$d/$0" ]; then # If you put relative directories in your PATH, you get what you deserve. DIR=${d%/*};; fi done if [ -z "$DIR" ]; then echo 1>&2 "$0: Fatal error: I can't find myself." exit 125 fi unset IFS; set +f;; esac 

Il suffit d'appeler à Perl:

 abspath=$(perl -MCwd -MFile::Basename -e 'print abs_path(dirname($ARGV[0]))' $0) 

Notez que abs_path(dirname(...)) peut ne pas être le même que dirname(abs_path(...)) .


(Edit: Ajouté -MFile::Basename )