Une façon fiable pour un script bash d'obtenir le chemin complet vers lui-même? [dupliquer]
cette question a déjà une réponse ici:
- obtenir le répertoire source D'un script Bash de l'intérieur 51 réponses
j'ai un script bash qui a besoin de connaître son chemin complet. J'essaie de trouver une façon largement compatible de faire ça sans finir avec relative ou funky chemins. J'ai seulement besoin de soutien bash, pas de sh, csh, etc.
Ce que j'ai trouvé jusqu'à présent:
-
Les accepté de répondre à " le répertoire source d'un script Bash de l'intérieur " les adresses obtenir le chemin d'accès du script via
dirname "151910920"
, ce qui est bien, mais que peut retourner un relative chemin "(comme.
), qui est un problème si vous voulez modifier les répertoires dans le script et le chemin reste du script d'annuaire. Pourtant,dirname
fera partie du puzzle. -
Les accepté de répondre à " script Bash chemin absolu avec OSX " (OS X, mais la réponse fonctionne indépendamment) donne une fonction qui permettra de tester pour voir si
"151940920"
semble relative, et si tel est pré-pend$PWD
. Mais le résultat peut par exemple, si le script estt
dans le répertoire/usr/bin
et que vous êtes dans/usr
et que vous tapezbin/../bin/t
pour l'exécuter (Oui, c'est alambiqué), vous finissez avec/usr/bin/../bin
comme chemin de répertoire du script. Qui fonctionne , mais... -
la
readlink
solution sur cette page , qui ressemble à ceci:# Absolute path to this script. /home/user/bin/foo.sh SCRIPT=$(readlink -f "151900920") # Absolute path this script is in. /home/user/bin SCRIPTPATH=`dirname $SCRIPT`
mais
readlink
n'est pas POSIX et apparemment la solution s'appuie sur GNUreadlink
où BSD ne fonctionnera pas pour une raison quelconque (je n'ai pas accès à un système de type BSD à vérifier).
donc, différentes façons de le faire, mais ils ont tous leurs caveats.
quelle meilleure façon? Où "Meilleur" signifie:
- me donne le chemin absolu.
- élimine les bits funky même lorsqu'ils sont invoqués d'une manière alambiquée (voir le commentaire sur le N ° 2 ci-dessus). (Par exemple, au moins modérément canonique le chemin.)
- S'appuie uniquement sur des bash-ismes ou des choses qui sont presque certains d'être sur les saveurs les plus populaires des systèmes *nix (GNU/Linux, BSD et des systèmes similaires à BSD comme OS X, etc.).
- évite d'appeler des programmes externes si possible (par exemple, préfère bash built-ins).
- ( mise à Jour , merci pour le heads up, qui ) N'a pas à résoudre les liens symboliques (en fait, je ferais en sorte de le préfèrent les a laissés seuls, mais ce n'est pas une obligation).
23 réponses
voici ce que j'ai trouvé (edit: plus quelques tweaks fournis par sfstewman , levigroker , Kyle Strand , et Rob Kennedy ), qui semble correspondre principalement à mes" meilleurs "critères:
SCRIPTPATH="$( cd "$(dirname ""151900920"")" ; pwd -P )"
cette ligne SCRIPTPATH
semble particulièrement rond-point, mais nous en avons besoin plutôt que SCRIPTPATH=`pwd`
afin de gérer correctement les espaces et les liens symboliques.
Notez aussi que les situations ésotériques, telles que l'exécution d'un script qui ne vient pas du tout d'un fichier dans un système de fichiers accessible (ce qui est parfaitement possible), n'y sont pas traitées (ou dans aucune des autres réponses que j'ai vues).
je suis surpris que la commande realpath
n'ait pas été mentionnée ici. Je crois comprendre qu'il est largement portable / porté.
votre solution initiale devient:
SCRIPT=`realpath "151900920"`
SCRIPTPATH=`dirname $SCRIPT`
et de laisser les liens symboliques non résolus selon votre préférence:
SCRIPT=`realpath -s "151910920"`
SCRIPTPATH=`dirname $SCRIPT`
le moyen le plus simple que j'ai trouvé pour obtenir un chemin canonique complet à bash est d'utiliser cd
et pwd
:
ABSOLUTE_PATH="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/$(basename "${BASH_SOURCE[0]}")"
utilisant ${BASH_SOURCE[0]}
au lieu de "151940920"
produit le même comportement indépendamment du fait que le script soit invoqué comme <name>
ou source <name>
j'ai juste dû revisiter cette question aujourd'hui et j'ai trouvé https://stackoverflow.com/a/246128/1034080 . Il élabore sur une solution que j'ai utilisé dans le passé ainsi .
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
il y a plus de variantes à la réponse liée, par exemple pour le cas où le script lui-même est un lien symbolique.
obtenir le chemin absolu du script shell
N'utilise pas l'option -f
dans readlink, donc devrait fonctionner dans bsd / mac-osx
prend en charge
- de la source ./ script (Lorsqu'il est appelé par
.
dot operator) - chemin Absolu /chemin/vers/le / script
- chemin relatif comme ./ script
- /chemin/dir1/../ dir2/dir3/../ script
- quand il est appelé de symlink
- quand le lien symbolique est imbriqué eg)
foo->dir1/dir2/bar bar->./../doe doe->script
- lorsque l'appelant change le nom du script
je cherche des cas en coin où ce code ne fonctionne pas . S'il vous plaît laissez-moi savoir.
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`]"
connu issuse
Script doit être sur le disque quelque part , permettez d'être sur un réseau. si vous essayez d'exécuter ce script à partir D'une PIPE, il ne fonctionnera pas
wget -o /dev/null -O - http://host.domain/dir/script.sh |bash
Techniquement parlant, il n'est pas défini.
Pratiquement parlant, il n'y a aucun moyen sensé de le détecter. (co-processus ne peut pas accéder env de parent)
Qu'en est-il de l'utilisation:
SCRIPT_PATH=$(dirname `which "151900920"`)
which
imprime pour stdout le chemin complet de l'exécutable qui aurait été exécuté lorsque l'argument passé avait été entré à l'invite de shell (ce qui est ce que 0 $contient)
dirname
supprime le suffixe Non répertoire du nom de fichier
par conséquent, ce que vous obtenez est le chemin complet vers le script, peu importe si le chemin a été spécifié ou non.
comme realpath n'est pas installé par défaut sur mon système Linux les travaux suivants pour moi:
SCRIPT="$(readlink --canonicalize-existing ""151900920"")"
SCRIPTPATH="$(dirname "$SCRIPT")"
$SCRIPT
contiendra le chemin réel du fichier vers le script et $SCRIPTPATH
le chemin réel du répertoire contenant le script.
avant d'utiliser ceci lire les commentaires de cette réponse .
répondre à cette question très tard, mais j'utilise:
SCRIPT=$( readlink -m $( type -p "151900920" )) # Full path to script
BASE_DIR=`dirname ${SCRIPT}` # Directory script is run in
NAME=`basename ${SCRIPT}` # Actual name of script even if linked
nous avons placé notre propre produit realpath-lib sur GitHub pour un usage communautaire libre et gratuit.
bouchon honteux mais avec cette bibliothèque de Bash vous pouvez:
get_realpath <absolute|relative|symlink|local file>
cette fonction est au cœur de la bibliothèque:
function get_realpath() {
if [[ -f "" ]]
then
# file *must* exist
if cd "$(echo "${1%/*}")" &>/dev/null
then
# file *may* not be local
# exception is ./file.ext
# try 'cd .; cd -;' *works!*
local tmppwd="$PWD"
cd - &>/dev/null
else
# file *must* be local
local tmppwd="$PWD"
fi
else
# file *cannot* exist
return 1 # failure
fi
# reassemble realpath
echo "$tmppwd"/"${1##*/}"
return 0 # success
}
il ne nécessite pas de dépendances externes, juste Bash 4+. Contient également des fonctions pour get_dirname
, get_filename
, get_stemname
et validate_path validate_realpath
. Il est gratuit, propre, simple et bien documenté, donc il peut être utilisé à des fins d'apprentissage aussi, et sans doute peut être améliorée. Essayez à travers les plates-formes.
mise à jour: après quelques révisions et essais, nous avons remplacé la fonction ci-dessus par quelque chose qui permet d'obtenir le même résultat (sans utiliser dirname, seulement Pure Bash) mais avec une meilleure efficacité:
function get_realpath() {
[[ ! -f "" ]] && return 1 # failure : file does not exist.
[[ -n "$no_symlinks" ]] && local pwdp='pwd -P' || local pwdp='pwd' # do symlinks.
echo "$( cd "$( echo "${1%/*}" )" 2>/dev/null; $pwdp )"/"${1##*/}" # echo result.
return 0 # success
}
comprend également un environnement no_symlinks
qui fournit la capacité de résoudre des liens symboliques au système physique. Par défaut, il maintient les liens symboliques intacts.
vous pouvez essayer de définir la variable suivante:
CWD="$(cd -P -- "$(dirname -- ""151900920"")" && pwd -P)"
ou vous pouvez essayer la fonction suivante dans bash:
realpath () {
[[ = /* ]] && echo "" || echo "$PWD/${1#./}"
}
cette fonction prend 1 argument. Si l'argument a déjà le chemin absolu, imprimez-le tel quel, sinon Imprimez $PWD
variable + argument filename (sans préfixe ./
).
Related:
voie conforme sh:
SCRIPT_HOME=`dirname "151900920" | while read a; do cd $a && pwd && break; done`
simplement: BASEDIR=$(readlink -f "151900920" | xargs dirname)
pas de fantaisie, les opérateurs
HIH.
peut-être que la réponse acceptée à la question suivante peut être utile.
Comment puis-je obtenir le comportement de GNU readlink-f sur un Mac?
étant donné que vous voulez juste canoniser le nom que vous obtenez de concaténer $PWD et 0 $(en supposant que 0 $n'est pas absolu pour commencer) il suffit d'utiliser une série de remplacements regex le long de la ligne de abs_dir=${abs_dir//\/.\//\/}
et tel.
Oui, je sais que c'est horrible mais ça va marcher et c'est de la purée.
considérant cette question encore une fois: il ya une solution très populaire qui est référencé dans ce fil qui a son origine ici :
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
je suis resté à l'écart de cette solution en raison de l'utilisation de dirname - il peut présenter des difficultés multiplateformes, en particulier si un script doit être verrouillé pour des raisons de sécurité. Mais comme une alternative Pure Bash, Que diriez-vous d'utiliser:
DIR="$( cd "$( echo "${BASH_SOURCE[0]%/*}" )" && pwd )"
est-ce être une option?
facile à lire? alternative. Ignore symlinks
#!/bin/bash
currentDir=$(
cd $(dirname ""151900920"")
pwd
)
echo -n "current "
pwd
echo script $currentDir
La solution retenue a l'inconvénient (pour moi) de ne pas être "source":
si vous appelez à partir d'un " source ../../yourScript
", "151920920"
serait " bash
"!
la fonction suivante (pour bash >= 3.0) me donne le bon chemin, cependant le script peut être appelé (directement ou par source
, avec un chemin absolu ou relatif):
(par" droit chemin", je veux dire le chemin absolu complet du script étant appelé , même lorsqu'il est appelé d'un autre chemin, directement ou avec source
")
#!/bin/bash
echo "151900920" executed
function bashscriptpath() {
local _sp=
local ascript=""151900920""
local asp="$(dirname "151900920")"
#echo "b1 asp '$asp', b1 ascript '$ascript'"
if [[ "$asp" == "." && "$ascript" != "bash" && "$ascript" != "./.bashrc" ]] ; then asp="${BASH_SOURCE[0]%/*}"
elif [[ "$asp" == "." && "$ascript" == "./.bashrc" ]] ; then asp=$(pwd)
else
if [[ "$ascript" == "bash" ]] ; then
ascript=${BASH_SOURCE[0]}
asp="$(dirname $ascript)"
fi
#echo "b2 asp '$asp', b2 ascript '$ascript'"
if [[ "${ascript#/}" != "$ascript" ]]; then asp=$asp ;
elif [[ "${ascript#../}" != "$ascript" ]]; then
asp=$(pwd)
while [[ "${ascript#../}" != "$ascript" ]]; do
asp=${asp%/*}
ascript=${ascript#../}
done
elif [[ "${ascript#*/}" != "$ascript" ]]; then
if [[ "$asp" == "." ]] ; then asp=$(pwd) ; else asp="$(pwd)/${asp}"; fi
fi
fi
eval $_sp="'$asp'"
}
bashscriptpath H
export H=${H}
la clé est de détecter le " source
"case et d'utiliser ${BASH_SOURCE[0]}
pour récupérer le script actuel.
si nous utilisons Bash je crois que c'est le moyen le plus commode car il ne nécessite pas d'appels à des commandes externes:
THIS_PATH="${BASH_SOURCE[0]}";
THIS_DIR=$(dirname $THIS_PATH)
essayez ceci:
cd $(dirname $([ -L "151900920" ] && readlink -f "151900920" || echo "151900920"))
juste pour l'enfer de l'enfer que j'ai fait un peu de piratage sur un script qui fait des choses purement textuellement, purement en bash. J'espère que j'ai attrapé tous les cas de bord. Notez que le ${var//pat/repl}
que j'ai mentionné dans l'autre réponse ne fonctionne pas car vous ne pouvez pas le faire remplacer seulement la correspondance la plus courte possible, ce qui est un problème pour remplacer /foo/../
comme par exemple /*/../
prendra tout avant lui, pas une seule entrée. Et puisque ces modèles ne sont pas vraiment des regexes Je ne vois pas comment qui peut être fait pour travailler. Voici donc la solution joliment alambiquée que j'ai trouvée, enjoy. ;)
au fait, prévenez-moi si vous trouvez des caisses de bord non fermées.
#!/bin/bash
canonicalize_path() {
local path=""
OIFS="$IFS"
IFS=$'/'
read -a parts < <(echo "$path")
IFS="$OIFS"
local i=${#parts[@]}
local j=0
local back=0
local -a rev_canon
while (($i > 0)); do
((i--))
case "${parts[$i]}" in
""|.) ;;
..) ((back++));;
*) if (($back > 0)); then
((back--))
else
rev_canon[j]="${parts[$i]}"
((j++))
fi;;
esac
done
while (($j > 0)); do
((j--))
echo -n "/${rev_canon[$j]}"
done
echo
}
canonicalize_path "/.././..////../foo/./bar//foo/bar/.././bar/../foo/bar/./../..//../foo///bar/"
j'ai utilisé l'approche suivante avec succès depuis un certain temps (pas sur OSX cependant) et il utilise seulement shell intégré et manipule le 'source foobar.sh' cas aussi loin que j'ai vu.
un problème avec le code d'exemple (rapidement assemblé) ci-dessous est que la fonction utilise $PWD qui peut ou ne peut pas être correct au moment de l'appel de fonction. Donc qui doit être traité.
#!/bin/bash
function canonical_path() {
# Handle realtive vs absolute path
[ ${1:0:1} == '/' ] && x= || x=$PWD/
# Change to dirname of x
cd ${x%/*}
# Combine new pwd with basename of x
echo $(pwd -P)/${x##*/}
cd $OLDPWD
}
echo $(canonical_path "${BASH_SOURCE[0]}")
type [
type cd
type echo
type pwd
encore une autre façon de le faire:
shopt -s extglob
selfpath="151900920"
selfdir=${selfpath%%+([!/])}
while [[ -L "$selfpath" ]];do
selfpath=$(readlink "$selfpath")
if [[ ! "$selfpath" =~ ^/ ]];then
selfpath=${selfdir}${selfpath}
fi
selfdir=${selfpath%%+([!/])}
done
echo $selfpath $selfdir
Plus simplement, c'est ce qui fonctionne pour moi:
MY_DIR=`dirname "151900920"`
source $MY_DIR/_inc_db.sh