web-dev-qa-db-fra.com

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 se présente comme suit:

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 insignifiante?

30
Ivan Balashov

Une autre option (aussi plutôt moche):

ABSPATH=$(cd "$(dirname "$0")"; pwd)
63
Gordon Davisson

Obtenir le chemin absolu du script Shell

Enlever quelques anciens scripts de mon .bashrc, et mettre à jour un peu la syntaxe, ajouter une suite de tests.

Les soutiens

  • source ./script (Lorsqu'il est appelé par le . opérateur point)
  • Chemin absolu/chemin/vers/script
  • Chemin relatif comme ./script
  • /path/dir1/../dir2/dir3/../script
  • En cas d'appel depuis un lien symbolique
  • Lorsque le lien symbolique est imbriqué, par exemple) foo->dir1/dir2/bar bar->./../doe doe->script
  • Lorsque l'appelant change le nom des scripts

Il a été testé et utilisé avec succès dans de vrais projets, mais il peut y avoir des cas d'angle que je ne connais pas.
Si vous avez pu trouver une telle situation, faites-le moi savoir.
(D'une part, 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 PIPE, cela ne fonctionnera pas

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

Techniquement parlant, ce n'est pas défini.
Pratiquement parlant, il n'y a aucun 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
GreenFox

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

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 les espaces ou les caractères spéciaux si vous mettez les guillemets.

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

3
Lynch

Notez également que homebrew ( http://brew.sh ) coreutils le package comprend realpath (lien créé dans/opt/local/bin).

$ realpath bin
/Users/nhed/bin
3
nhed

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

echo $(pwd)/"$0"

Dans ma machine, cela montre:

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

qui est le nom de chemin absolu du script Shell.

1
Barun

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

 ABSPATH = $ (Perl -MCwd = realpath -e "print realpath '$ 0'") 
1
William Pursell

J'ai trouvé que c'était utile pour les liens symboliques/liens dynamiques - fonctionne avec GNU readlink seulement cependant (à cause de l'indicateur -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
1
Yoshua Wuyts

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
# This was re-worked on 2018-10-26 after der@build correctly
# observed that the previous version did not work.

# Works on both linux and Mac OS X.
# The "pwd -P" re-interprets all symlinks.
function read-link() {
    local path=$1
    if [ -d $path ] ; then
        local abspath=$(cd $path; pwd -P)
    else
        local prefix=$(cd $(dirname -- $path) ; pwd -P)
        local suffix=$(basename $path)
        local abspath="$prefix/$suffix"
    fi
    if [ -e $abspath ] ; then
        echo $abspath
    else
        echo 'error: does not exist'
    fi
}

# Example usage.
while (( $# )) ; do
    printf '%-24s - ' "$1"
    read-link $1
    shift
done

Voici la sortie de certaines cibles Mac OS X courantes:

$ ./example.sh /usr/bin/which /bin/which /etc/racoon ~/Downloads
/usr/bin/which           - /usr/bin/which
/bin/which               - error: does not exist
/etc/racoon              - /private/etc/racoon
/Users/jlinoff/Downloads - /Users/jlinoff/Downloads

C'est la sortie de certaines cibles Linux.

$ ./example.sh /usr/bin/which /bin/whichx /etc/init.d ~/Downloads
/usr/bin/which           - /usr/bin/which
/bin/whichx              - error: does not exist
/etc/init.d              - /etc/init.d
/home/jlinoff/Downloads  - /home/jlinoff/Downloads
0
Joe Linoff

Cela fonctionne tant qu'il ne s'agit pas d'un lien symbolique et est peut-être légèrement moins laid:

ABSPATH=$(dirname $(pwd -P $0)/${0#\.\/})
0
Magnus

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

0
Nathan Weeks

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 n'importe quel fichier - et de malédiction, vous pouvez simplement l'invoquer comme abspath ${0}

Le premier cas traite des chemins relatifs en se connectant 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 n'essaye PAS d'annuler les liens symboliques!

0
nhed