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?
Une autre option (aussi plutôt moche):
ABSPATH=$(cd "$(dirname "$0")"; pwd)
Enlever quelques anciens scripts de mon .bashrc, et mettre à jour un peu la syntaxe, ajouter une suite de tests.
.
opérateur point)foo->dir1/dir2/bar bar->./../doe doe->script
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)
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`]"
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.
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
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.
Notez également que homebrew
( http://brew.sh ) coreutils
le package comprend realpath
(lien créé dans/opt/local/bin
).
$ realpath bin
/Users/nhed/bin
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.
Si cela ne vous dérange pas d'utiliser Perl:
ABSPATH = $ (Perl -MCwd = realpath -e "print realpath '$ 0'")
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
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
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#\.\/})
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%/*}
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!