Existe-t-il une commande pour récupérer le chemin absolu étant donné le chemin relatif?
Par exemple, je veux que $ line contienne le chemin absolu de chaque fichier dans dir ./etc/
find ./ -type f | while read line; do
echo $line
done
Existe-t-il une commande pour récupérer le chemin absolu étant donné le chemin relatif?
Par exemple, je veux que $ line contienne le chemin absolu de chaque fichier dans dir ./etc/
find ./ -type f | while read line; do
echo $line
done
Réponses:
utilisation:
find "$(pwd)"/ -type f
pour obtenir tous les fichiers ou
echo "$(pwd)/$line"
pour afficher le chemin complet (si le chemin relatif est important)
$(pwd)
à la place de $PWD
? (oui, je sais que pwd
c'est un builtin)
Essayez realpath
.
~ $ sudo apt-get install realpath # may already be installed
~ $ realpath .bashrc
/home/username/.bashrc
Pour éviter de développer les liens symboliques, utilisez realpath -s
.
La réponse vient de " la commande bash / fish pour imprimer le chemin absolu vers un fichier ".
realpath
ne semble pas disponible sur Mac (OS X 10.11 "El Capitan"). :-(
realpath
ne semble pas non plus disponible sur CentOS 6
brew install coreutils
apporterarealpath
realpath
est déjà présent. Je n'ai pas eu à l'installer séparément.
realpath
est disponible sur Git pour Windows (pour moi, du moins).
Si vous avez installé le package coreutils, vous pouvez généralement l'utiliser readlink -f relative_file_name
pour récupérer le package absolu (avec tous les liens symboliques résolus)
brew install coreutils
. Cependant l'exécutable est précédé de ag:greadlink -f relative_file_name
#! /bin/sh
echo "$(cd "$(dirname "$1")"; pwd)/$(basename "$1")"
UPD Quelques explications
"$1"
dirname "$1"
cd "$(dirname "$1")
dans ce répertoire relatif et obtenons le chemin absolu pour celui-ci en exécutant la pwd
commande shell$(basename "$1")
echo
nerealpath
ou readlink -f
faire. Par exemple, cela ne fonctionne pas sur les chemins où le dernier composant est un lien symbolique.
-P
option pour pwd
commander:echo "$(cd "$(dirname "$1")"; pwd -P)/$(basename "$1")"
Pour ce que ça vaut, j'ai voté pour la réponse choisie, mais je voulais partager une solution. L'inconvénient est qu'il ne s'agit que de Linux - j'ai passé environ 5 minutes à essayer de trouver l'équivalent OSX avant d'arriver à Stack overflow. Je suis sûr que c'est là-bas cependant.
Sous Linux, vous pouvez utiliser readlink -e
en tandem avec dirname
.
$(dirname $(readlink -e ../../../../etc/passwd))
rendements
/etc/
Et puis vous utilisez dirname
la sœur de, basename
pour simplement obtenir le nom du fichier
$(basename ../../../../../passwd)
rendements
passwd
Mets le tout ensemble..
F=../../../../../etc/passwd
echo "$(dirname $(readlink -e $F))/$(basename $F)"
rendements
/etc/passwd
Vous êtes en sécurité si vous ciblez un répertoire, basename
ne retournera rien et vous vous retrouverez simplement avec des doubles barres obliques dans la sortie finale.
dirname
, readlink
et basename
. Cela m'a aidé à trouver le chemin absolu d'un lien symbolique - pas sa cible.
/home/GKFX
et que je tape touch newfile
, alors avant d'appuyer sur Entrée, vous pourriez comprendre que je veux dire "create / home / GKFX / newfile", qui est un chemin absolu vers un fichier qui n'existe pas encore.
Je pense que c'est le plus portable:
abspath() {
cd "$(dirname "$1")"
printf "%s/%s\n" "$(pwd)" "$(basename "$1")"
cd "$OLDPWD"
}
Cela échouera si le chemin n'existe pas.
dirname
c'est un utilitaire de base GNU, pas commun à tous les Unixen je crois.
dirname
est un utilitaire standard POSIX, voir ici: pubs.opengroup.org/onlinepubs/9699919799/utilities/dirname.html
${1##*/}
depuis un jour maintenant, et maintenant que j'ai remplacé cette poubelle par basename "$1"
elle semble enfin gérer correctement les chemins qui se terminent par /.
../..
realpath
est probablement le meilleur
Mais ...
La question initiale était très confuse au départ, avec un exemple mal lié à la question comme indiqué.
La réponse choisie répond en fait à l'exemple donné, et pas du tout à la question du titre. La première commande est cette réponse (est-ce vraiment? Je doute), et pourrait faire aussi bien sans le '/'. Et je ne vois pas ce que fait la deuxième commande.
Plusieurs problèmes sont mitigés:
changer un chemin relatif en chemin absolu, quoi qu'il dénote, peut-être rien. ( En règle générale, si vous exécutez une commande telle que touch foo/bar
, le chemin d'accès foo/bar
doit exister pour vous, et éventuellement être utilisé dans le calcul, avant que le fichier ne soit réellement créé. )
il peut y avoir plusieurs chemins absolus qui désignent le même fichier (ou fichier potentiel), notamment à cause de liens symboliques (symlinks) sur le chemin, mais éventuellement pour d'autres raisons (un périphérique peut être monté deux fois en lecture seule). On peut ou non vouloir résoudre explicitement ces liens symboliques.
arriver à la fin d'une chaîne de liens symboliques vers un fichier ou un nom sans lien symbolique. Cela peut ou non donner un nom de chemin absolu, selon la façon dont cela est fait. Et on peut, ou peut ne pas vouloir le résoudre en un chemin absolu.
La commande readlink foo
sans option donne une réponse uniquement si son argument foo
est un lien symbolique, et cette réponse est la valeur de ce lien symbolique. Aucun autre lien n'est suivi. La réponse peut être un chemin relatif: quelle que soit la valeur de l'argument du lien symbolique.
Cependant, readlink
a des options (-f -e ou -m) qui fonctionneront pour tous les fichiers, et donneront un chemin absolu (celui sans lien symbolique) au fichier réellement indiqué par l'argument.
Cela fonctionne bien pour tout ce qui n'est pas un lien symbolique, bien que l'on puisse souhaiter utiliser un chemin absolu sans résoudre les liens symboliques intermédiaires sur le chemin. Ceci est fait par la commanderealpath -s foo
Dans le cas d'un argument de lien symbolique, readlink
avec ses options résoudra à nouveau tous les liens symboliques sur le chemin absolu de l'argument, mais cela inclura également tous les liens symboliques qui peuvent être rencontrés en suivant la valeur de l'argument. Vous ne voudrez peut-être pas cela si vous souhaitez un chemin absolu vers le lien symbolique de l'argument lui-même, plutôt que vers celui auquel il peut être lié. Encore une fois, si foo
est un lien symbolique, realpath -s foo
obtiendra un chemin absolu sans résoudre les liens symboliques, y compris celui donné en argument.
Sans l' -s
option, realpath
fait à peu près la même chose que
readlink
, sauf pour simplement lire la valeur d'un lien, ainsi que plusieurs autres choses. Il n'est tout simplement pas clair pour moi pourquoi readlink
a ses options, créant apparemment une redondance indésirable avec
realpath
.
Explorer le Web ne dit pas grand chose de plus, sauf qu'il peut y avoir des variations entre les systèmes.
Conclusion: realpath
c'est la meilleure commande à utiliser, avec le plus de flexibilité, du moins pour l'utilisation demandée ici.
Ma solution préférée était celle de @EugenKonkov car elle n'impliquait pas la présence d'autres utilitaires (le package coreutils).
Mais il a échoué pour les chemins relatifs "." et "..", voici donc une version légèrement améliorée gérant ces cas particuliers.
cd
Cependant, cela échoue toujours si l'utilisateur n'a pas l'autorisation d' accéder au répertoire parent du chemin relatif.
#! /bin/sh
# Takes a path argument and returns it as an absolute path.
# No-op if the path is already absolute.
function to-abs-path {
local target="$1"
if [ "$target" == "." ]; then
echo "$(pwd)"
elif [ "$target" == ".." ]; then
echo "$(dirname "$(pwd)")"
else
echo "$(cd "$(dirname "$1")"; pwd)/$(basename "$1")"
fi
}
La réponse d'Eugen n'a pas vraiment fonctionné pour moi, mais cela a fonctionné:
absolute="$(cd $(dirname \"$file\"); pwd)/$(basename \"$file\")"
Remarque: votre répertoire de travail actuel n'est pas affecté.
La meilleure solution, à mon humble avis, est celle publiée ici: https://stackoverflow.com/a/3373298/9724628 .
Cela nécessite python pour fonctionner, mais il semble couvrir tous ou la plupart des cas marginaux et être une solution très portable.
python -c "import os,sys; print(os.path.realpath(sys.argv[1]))" path/to/file
python -c "import os,sys; print(os.path.abspath(sys.argv[1]))" path/to/file
Si vous utilisez bash sur Mac OS X qui n'a pas de realpath et que son readlink peut imprimer le chemin absolu, vous pouvez avoir le choix de coder votre propre version pour l'imprimer. Voici ma mise en œuvre:
(pure bash)
abspath(){
local thePath
if [[ ! "$1" =~ ^/ ]];then
thePath="$PWD/$1"
else
thePath="$1"
fi
echo "$thePath"|(
IFS=/
read -a parr
declare -a outp
for i in "${parr[@]}";do
case "$i" in
''|.) continue ;;
..)
len=${#outp[@]}
if ((len==0));then
continue
else
unset outp[$((len-1))]
fi
;;
*)
len=${#outp[@]}
outp[$len]="$i"
;;
esac
done
echo /"${outp[*]}"
)
}
(utilisez gawk)
abspath_gawk() {
if [[ -n "$1" ]];then
echo $1|gawk '{
if(substr($0,1,1) != "/"){
path = ENVIRON["PWD"]"/"$0
} else path = $0
split(path, a, "/")
n = asorti(a, b,"@ind_num_asc")
for(i in a){
if(a[i]=="" || a[i]=="."){
delete a[i]
}
}
n = asorti(a, b, "@ind_num_asc")
m = 0
while(m!=n){
m = n
for(i=1;i<=n;i++){
if(a[b[i]]==".."){
if(b[i-1] in a){
delete a[b[i-1]]
delete a[b[i]]
n = asorti(a, b, "@ind_num_asc")
break
} else exit 1
}
}
}
n = asorti(a, b, "@ind_num_asc")
if(n==0){
printf "/"
} else {
for(i=1;i<=n;i++){
printf "/"a[b[i]]
}
}
}'
fi
}
(pure bsd awk)
#!/usr/bin/env awk -f
function abspath(path, i,j,n,a,b,back,out){
if(substr(path,1,1) != "/"){
path = ENVIRON["PWD"]"/"path
}
split(path, a, "/")
n = length(a)
for(i=1;i<=n;i++){
if(a[i]==""||a[i]=="."){
continue
}
a[++j]=a[i]
}
for(i=j+1;i<=n;i++){
delete a[i]
}
j=0
for(i=length(a);i>=1;i--){
if(back==0){
if(a[i]==".."){
back++
continue
} else {
b[++j]=a[i]
}
} else {
if(a[i]==".."){
back++
continue
} else {
back--
continue
}
}
}
if(length(b)==0){
return "/"
} else {
for(i=length(b);i>=1;i--){
out=out"/"b[i]
}
return out
}
}
BEGIN{
if(ARGC>1){
for(k=1;k<ARGC;k++){
print abspath(ARGV[k])
}
exit
}
}
{
print abspath($0)
}
exemple:
$ abspath I/am/.//..//the/./god/../of///.././war
/Users/leon/I/the/war
Si le chemin relatif est un chemin de répertoire, essayez le mien, cela devrait être le meilleur:
absPath=$(pushd ../SOME_RELATIVE_PATH_TO_Directory > /dev/null && pwd && popd > /dev/null)
echo $absPath
echo "mydir/doc/ mydir/usoe ./mydir/usm" | awk '{ split($0,array," "); for(i in array){ system("cd "array[i]" && echo $PWD") } }'
Une amélioration de la version plutôt sympa de @ ernest-a:
absolute_path() {
cd "$(dirname "$1")"
case $(basename $1) in
..) echo "$(dirname $(pwd))";;
.) echo "$(pwd)";;
*) echo "$(pwd)/$(basename $1)";;
esac
}
Cela traite correctement le cas où se trouve le dernier élément du chemin ..
, auquel cas la "$(pwd)/$(basename "$1")"
réponse de in @ ernest-a apparaîtra comme accurate_sub_path/spurious_subdirectory/..
.
Si vous souhaitez transformer une variable contenant un chemin relatif en un chemin absolu, cela fonctionne:
dir=`cd "$dir"`
"cd" fait écho sans changer le répertoire de travail, car exécuté ici dans un sous-shell.
dir=`cd ".."` && echo $dir
Il s'agit d'une solution chaînée de toutes les autres, par exemple, en cas d' realpath
échec, soit parce qu'elle n'est pas installée, soit parce qu'elle se termine avec un code d'erreur, la solution suivante est tentée jusqu'à ce qu'elle obtienne le bon chemin.
#!/bin/bash
function getabsolutepath() {
local target;
local changedir;
local basedir;
local firstattempt;
target="${1}";
if [ "$target" == "." ];
then
printf "%s" "$(pwd)";
elif [ "$target" == ".." ];
then
printf "%s" "$(dirname "$(pwd)")";
else
changedir="$(dirname "${target}")" && basedir="$(basename "${target}")" && firstattempt="$(cd "${changedir}" && pwd)" && printf "%s/%s" "${firstattempt}" "${basedir}" && return 0;
firstattempt="$(readlink -f "${target}")" && printf "%s" "${firstattempt}" && return 0;
firstattempt="$(realpath "${target}")" && printf "%s" "${firstattempt}" && return 0;
# If everything fails... TRHOW PYTHON ON IT!!!
local fullpath;
local pythoninterpreter;
local pythonexecutables;
local pythonlocations;
pythoninterpreter="python";
declare -a pythonlocations=("/usr/bin" "/bin");
declare -a pythonexecutables=("python" "python2" "python3");
for path in "${pythonlocations[@]}";
do
for executable in "${pythonexecutables[@]}";
do
fullpath="${path}/${executable}";
if [[ -f "${fullpath}" ]];
then
# printf "Found ${fullpath}\\n";
pythoninterpreter="${fullpath}";
break;
fi;
done;
if [[ "${pythoninterpreter}" != "python" ]];
then
# printf "Breaking... ${pythoninterpreter}\\n"
break;
fi;
done;
firstattempt="$(${pythoninterpreter} -c "import os, sys; print( os.path.abspath( sys.argv[1] ) );" "${target}")" && printf "%s" "${firstattempt}" && return 0;
# printf "Error: Could not determine the absolute path!\\n";
return 1;
fi
}
printf "\\nResults:\\n%s\\nExit: %s\\n" "$(getabsolutepath "./asdfasdf/ asdfasdf")" "${?}"
Vous pouvez utiliser la substitution de chaîne bash pour n'importe quel chemin relatif $ line:
line=$(echo ${line/#..\//`cd ..; pwd`\/})
line=$(echo ${line/#.\//`pwd`\/})
echo $line
La substitution de base avant de chaîne suit la formule
$ {string / # substring / replacement}
qui est bien discutée ici: https://www.tldp.org/LDP/abs/html/string-manipulation.html
Le \
caractère annule le /
lorsque nous voulons qu'il fasse partie de la chaîne que nous trouvons / remplaçons.
Je n'ai pas pu trouver une solution parfaitement portable entre Mac OS Catalina, Ubuntu 16 et Centos 7, j'ai donc décidé de le faire avec python inline et cela a bien fonctionné pour mes scripts bash.
to_abs_path() {
python -c "import os; print os.path.abspath('$1')"
}
to_abs_path "/some_path/../secrets"