Comment obtenir le nom de chemin absolu du script shell sur MacOS?

readlink -f n'existe pas sur MacOS. La seule solution de travail pour Mac OS que j'ai réussi à trouver sur le net va comme ceci:

if [[ $(echo $0 | awk '/^//') == $0 ]]; then
    ABSPATH=$(dirname $0)
else
    ABSPATH=$PWD/$(dirname $0)
fi

Quelqu'un peut-il suggérer quelque chose de plus élégant à cette tâche apparemment triviale?

28
demandé sur l0b0 2011-04-22 18:38:16

11 réponses

Une autre option (aussi plutôt laide):

ABSPATH=$(cd "$(dirname "$0")"; pwd)
61
répondu Gordon Davisson 2011-04-22 16:39:03

Récupère le chemin absolu du script shell

Creusé quelques vieux scripts de mon .bashrc, et mis à jour la syntaxe un peu, a ajouté une suite de tests.

Prend en charge

  • source ./ script (lorsqu'il est appelé par l'opérateur point .)
  • chemin absolu / chemin / vers / script
  • chemin relatif comme ./ script
  • /chemin/dir1/../dir2/dir3/../ script
  • lorsqu'il est appelé à partir du lien symbolique
  • lorsque le lien symbolique est imbriqué par exemple) foo->dir1/dir2/bar bar->./../doe doe->script
  • Lorsque l'appelant modifie les scripts nom

Il a été testé et utilisé dans de vrais projets avec succès, mais il peut y avoir des cas de coin dont je ne suis pas au courant.
Si vous étiez en mesure de trouver une telle situation, s'il vous plaît laissez-moi savoir.
(Pour un, je sais que cela ne fonctionne pas sur le shell sh)

Code

pushd . > /dev/null
SCRIPT_PATH="${BASH_SOURCE[0]}";
  while([ -h "${SCRIPT_PATH}" ]) do 
    cd "`dirname "${SCRIPT_PATH}"`"
    SCRIPT_PATH="$(readlink "`basename "${SCRIPT_PATH}"`")"; 
  done
cd "`dirname "${SCRIPT_PATH}"`" > /dev/null
SCRIPT_PATH="`pwd`";
popd  > /dev/null
echo "srcipt=[${SCRIPT_PATH}]"
echo "pwd   =[`pwd`]"

Issuse connu

Script doit être sur le disque quelque part, que ce soit sur un réseau. Si vous essayez d'exécuter ce script à partir d'un TUYAU, elle ne fonctionnera pas

wget -o /dev/null -O - http://host.domain/dir/script.sh |bash

Techniquement en parlant, elle n'est pas définie.
Pratiquement parlant, il n'y a pas de moyen sain de détecter cela.

Cas de Test utilisé

Et le cas de test actuel qui vérifie que cela fonctionne.

#!/bin/bash
# setup test enviroment
mkdir -p dir1/dir2
mkdir -p dir3/dir4
ln -s ./dir1/dir2/foo bar
ln -s ./../../dir3/dir4/test.sh dir1/dir2/foo
ln -s ./dir1/dir2/foo2 bar2
ln -s ./../../dir3/dir4/doe dir1/dir2/foo2
cp test.sh ./dir1/dir2/
cp test.sh ./dir3/dir4/
cp test.sh ./dir3/dir4/doe
P="`pwd`"
echo "--- 01"
echo "base  =[${P}]" && ./test.sh
echo "--- 02"
echo "base  =[${P}]" && `pwd`/test.sh
echo "--- 03"
echo "base  =[${P}]" && ./dir1/dir2/../../test.sh
echo "--- 04"
echo "base  =[${P}/dir3/dir4]" && ./bar
echo "--- 05"
echo "base  =[${P}/dir3/dir4]" && ./bar2
echo "--- 06"
echo "base  =[${P}/dir3/dir4]" && `pwd`/bar
echo "--- 07"
echo "base  =[${P}/dir3/dir4]" && `pwd`/bar2
echo "--- 08"
echo "base  =[${P}/dir1/dir2]" && `pwd`/dir3/dir4/../../dir1/dir2/test.sh
echo "--- 09"
echo "base  =[${P}/dir1/dir2]" && ./dir1/dir2/test.sh
echo "--- 10"
echo "base  =[${P}/dir3/dir4]" && ./dir3/dir4/doe
echo "--- 11"
echo "base  =[${P}/dir3/dir4]" && ./dir3/dir4/test.sh
echo "--- 12"
echo "base  =[${P}/dir3/dir4]" && `pwd`/dir3/dir4/doe
echo "--- 13"
echo "base  =[${P}/dir3/dir4]" && `pwd`/dir3/dir4/test.sh
echo "--- 14"
echo "base  =[${P}/dir3/dir4]" && `pwd`/dir1/dir2/../../dir3/dir4/doe
echo "--- 15"
echo "base  =[${P}/dir3/dir4]" && `pwd`/dir1/dir2/../../dir3/dir4/test.sh
echo "--- 16"
echo "base s=[${P}]" && source test.sh
echo "--- 17"
echo "base s=[${P}]" && source `pwd`/test.sh
echo "--- 18"
echo "base s=[${P}/dir1/dir2]" && source ./dir1/dir2/test.sh
echo "--- 19"
echo "base s=[${P}/dir3/dir4]" && source ./dir1/dir2/../../dir3/dir4/test.sh
echo "--- 20"
echo "base s=[${P}/dir3/dir4]" && source `pwd`/dir1/dir2/../../dir3/dir4/test.sh
echo "--- 21"
pushd . >/dev/null
cd ..
echo "base x=[${P}/dir3/dir4]"
./`basename "${P}"`/bar
popd  >/dev/null

PurpleFox alias GreenFox

9
répondu GreenFox 2012-08-30 12:48:47

Notez Également que homebrew's (http://brew.sh) coreutils forfait comprend realpath (lien créé dans/opt/local/bin).

$ realpath bin
/Users/nhed/bin
2
répondu nhed 2016-08-04 01:12:14

En utilisant bash je suggère cette approche. Vous commencez par cd dans le répertoire, puis vous prenez le répertoire actuel en utilisant pwd. Après cela, vous devez revenir à l'ancien répertoire pour assurer votre script ne crée pas d'effets secondaires à un autre script en l'appelant.

cd "$(dirname -- "$0")"
dir="$PWD"
echo "$dir"
cd - > /dev/null

Cette solution est sûre avec un chemin complexe. Vous n'aurez jamais de problèmes avec des espaces ou des charaters spéciaux si vous mettez les citations.

Note: le/dev / null est require ou "cd -" affiche le chemin vers lequel il retourne.

2
répondu Lynch 2016-08-04 03:27:18

Si cela ne vous dérange pas d'utiliser perl:

ABSPATH=$(perl -MCwd=realpath -e "print realpath '$0'")
1
répondu William Pursell 2011-04-22 15:58:34

Pouvez-vous essayer quelque chose comme ça dans votre script?

echo $(pwd)/"$0"

Dans ma machine, il montre:

/home/barun/codes/ns2/link_down/./test.sh

Qui est le nom de chemin absolu du script shell.

1
répondu Barun 2011-04-22 17:14:47

C'est ce que j'utilise, peut avoir besoin d'un tweak ici ou là

abspath () 
{ 
    case "${1}" in 
        [./]*)
            local ABSPATH="$(cd ${1%/*}; pwd)/${1##*/}"
            echo "${ABSPATH/\/\///}"
        ;;
        *)
            echo "${PWD}/${1}"
        ;;
    esac
}

C'est pour tout fichier et de malédiction, vous pouvez simplement appeler comme abspath ${0}

Le premier cas traite des chemins relatifs en cd - ing au chemin et en laissant pwd le comprendre

Le deuxième cas concerne le traitement d'un fichier local (où ${1##/} n'aurait pas fonctionné)

Cela ne tente pas d'annuler les liens symboliques!

0
répondu nhed 2011-04-22 18:19:09

Cela fonctionne tant que ce n'est pas un lien symbolique, et est peut-être légèrement moins moche:

ABSPATH=$(dirname $(pwd -P $0)/${0#\.\/})
0
répondu Magnus 2012-03-06 19:51:12

Si vous utilisez ksh, le paramètre ${.sh.file} est défini sur le chemin absolu du script. Pour obtenir le répertoire parent du script: ${.sh.file%/*}

0
répondu Nathan Weeks 2013-06-13 01:18:51

J'utilise la fonction ci-dessous pour émuler "readlink-f" pour les scripts qui doivent s'exécuter à la fois sur linux et Mac OS X.

#!/bin/bash

# Works on both linux and Mac OS X.
function read-link() {
    local path=$1
    if [ -d $path ] ; then
        local abspath=$(cd $path; pwd)
    else
        local prefix=$(cd $(dirname -- $path) ; pwd)
        local suffix=$(basename $path)
        local abspath="$prefix/$suffix"
    fi
    echo $abspath
}

# Example usage.
read-link $1
dirname -- $(read-link $1)
0
répondu Joe Linoff 2014-06-13 23:24:02

J'ai trouvé cela utile pour les liens symboliques / liens dynamiques-fonctionne avec GNU readlink seulement (à cause du drapeau-f):

# detect if GNU readlink is available on OS X
if [ "$(uname)" = "Darwin" ]; then
  which greadlink > /dev/null || {
    printf 'GNU readlink not found\n'
    exit 1
  }
  alias readlink="greadlink"
fi

# create a $dirname variable that contains the file dir
dirname=$(dirname "$(readlink -f "$0")")

# use $dirname to find a relative file
cat "$dirname"/foo/bar.txt
0
répondu Yoshua Wuyts 2015-12-08 01:24:32