web-dev-qa-db-fra.com

Bash: récupérer le chemin absolu donné par rapport

Existe-t-il une commande permettant de 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
102
nubme

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)

44
mpapis

Essayez realpath

~ $ Sudo apt-get install realpath  # may already be installed
~ $ realpath .bashrc
/home/username/.bashrc

Pour éviter de développer des liens symboliques, utilisez realpath -s.

La réponse provient de la commande " bash/fish" pour imprimer le chemin absolu vers un fichier ".

111
epere4

Si vous avez le paquet coreutils installé, vous pouvez généralement utiliser readlink -f relative_file_name pour récupérer le fichier absolu (avec tous les liens symboliques résolus)

90
Moritz Bunkus
#! /bin/sh
echo "$(cd "$(dirname "$1")"; pwd)/$(basename "$1")"

UPDQuelques explications

  1. Ce script obtient un chemin relatif en tant qu'argument "$1"
  2. Ensuite, nous obtenons dirname une partie de ce chemin (vous pouvez passer soit dir, soit fichier à ce script): dirname "$1"
  3. Ensuite, nous cd "$(dirname "$1") dans ce répertoire relatif et obtenons son chemin absolu en exécutant la commande pwd Shell
  4. Après cela, nous ajoutons nom_base au chemin absolu: $(basename "$1")
  5. En dernière étape, nous echo il
40
Eugen Konkov

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 s’agit uniquement de Linux - j’ai passé environ 5 minutes à essayer de trouver l’équivalent OSX avant d’aborder le débordement de pile. 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))

les rendements

/etc/

Et vous utilisez ensuite la soeur de dirname, basename pour obtenir simplement Le nom du fichier.

$(basename ../../../../../passwd)

les rendements 

passwd

Mets le tout ensemble..

F=../../../../../etc/passwd
echo "$(dirname $(readlink -e $F))/$(basename $F)"

les rendements

/etc/passwd

Vous êtes en sécurité si vous ciblez un répertoire, basename ne renverra rien .__ et vous obtiendrez simplement une double barre oblique dans la sortie finale.

29
synthesizerpatel

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.

21
Ernest A

realpath est probablement le meilleur

Mais ...

La question initiale était très confuse au départ, avec un exemple mal lié à la question telle que formulée.

La réponse sélectionnée répond effectivement à l'exemple donné, et pas du tout À la question dans le titre. La première commande est cette réponse (est-ce Vraiment? Je doute), et pourrait faire aussi bien sans le '/'. Et je ne parviens pas à voir Ce que fait la deuxième commande.

Plusieurs problèmes sont mélangés:

  • changer un nom de chemin relatif en un nom absolu, peu importe ce que cela signifie dénote, éventuellement rien . ( Généralement, si vous exécutez une commande telle que touch foo/bar, le chemin. 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 indiquant le même fichier .__ (ou fichier potentiel), notamment en raison de liens symboliques (liens symboliques) 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 de tels liens symboliques.

  • arriver à la fin d'une chaîne de liens symboliques vers un fichier ou un nom non symlink Cela peut ou peut ne pas donner un chemin absolu, Selon la façon dont cela est fait. Et on peut ou ne peut pas vouloir Le résoudre en un chemin absolu.

La commande readlink foo sans option ne donne une réponse que si son Argument foo est un lien symbolique et que cette réponse est la valeur de ce Symlink. 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 liens symboliques) à Le fichier désigné 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 commande realpath -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 , Qui inclura également tous les liens symboliques pouvant être rencontrés par Après la valeur de l'argument . Si vous souhaitez un chemin Absolu vers l'argument symlink plutôt que vers quoi que ce soit, vous ne voudrez peut-être pas le faire. De nouveau, si foo est un lien symbolique, realpath -s foo obtiendra un chemin absolu sans résoudre les liens symboliques, y compris celui qui est donné comme argument.

Sans l'option -s, realpath fait à peu près la même chose que readlink, à l'exception de la simple lecture de la valeur d'un lien, ainsi que de plusieurs autres choses. Je ne comprends tout simplement pas pourquoi readlink a ses options , Ce qui crée apparemment une redondance indésirable avec realpath.

Explorer le Web n’en dit pas beaucoup plus, sauf qu’il peut y avoir quelques variations entre les systèmes.

Conclusion: realpath est la meilleure commande à utiliser, avec la plus grande souplesse, au moins pour l’utilisation demandée ici.

13
babou

La réponse d'Eugen n'a pas fonctionné pour moi, mais cela a fonctionné:

    absolute="$(cd $(dirname \"$file\"); pwd)/$(basename \"$file\")"

Remarque secondaire, votre répertoire de travail actuel n'est pas affecté.

5
biomiker

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 cela a échoué pour les chemins relatifs "." et "..", voici donc une version légèrement améliorée gérant ces cas particuliers.

Il échoue quand même si l'utilisateur n'a pas l'autorisation de cd dans le répertoire parent du chemin relatif, cependant.

#! /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
}
3
hashchange

Si vous utilisez bash sur Mac OS X alors que ni realpath ni readlink ne peuvent imprimer le chemin absolu, vous pouvez choisir de coder votre propre version pour l’imprimer .. ma mise en oeuvre:

(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[*]}"
)
}

(utiliser 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
2
Meow

Dans le cas de find, il est probablement plus facile de simplement donner le chemin absolu dans lequel effectuer la recherche, par exemple:

find /etc
find `pwd`/subdir_of_current_dir/ -type f
2
Arkku

Il s'agit d'une solution chaînée de toutes les autres, par exemple, lorsque realpath échoue, soit parce qu'il n'est pas installé, soit parce qu'il se termine avec un code d'erreur, puis la solution suivante est tentée jusqu'à ce que le chemin d'accès soit correct.

#!/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")" "${?}"
0
user
echo "mydir/doc/ mydir/usoe ./mydir/usm" |  awk '{ split($0,array," "); for(i in array){ system("cd "array[i]" && echo $PWD") } }'
0
Josh Barton

Similaire à la réponse de @ ernest-a mais sans affecter $OLDPWD ni définir une nouvelle fonction, vous pouvez déclencher un sous-shell (cd <path>; pwd)

$ pwd
/etc/Apache2
$ cd ../cups 
$ cd -
/etc/Apache2
$ (cd ~/..; pwd)
/Users
$ cd -
/etc/cups
0
fakedrake

La meilleure solution, à mon humble avis, est celle publiée ici: https://stackoverflow.com/a/3373298/9724628 .

Python est nécessaire pour fonctionner, mais il semble couvrir la totalité ou la plupart des cas Edge et constituer une solution très portable.

  1. Avec la résolution des liens symboliques:
python -c "import os,sys; print os.path.realpath(sys.argv[1])" path/to/file
  1. ou sans:
python -c "import os,sys; print os.path.abspath(sys.argv[1])" path/to/file
0
MrSegFaulty

Si le chemin relatif est un chemin de répertoire, alors essayez le mien, ça devrait être le meilleur:

absPath=$(pushd ../SOME_RELATIVE_PATH_TO_Directory > /dev/null && pwd && popd > /dev/null)

echo $absPath
0
Shunfang Lan

Si vous voulez transformer une variable contenant un chemin relatif en un chemin absolu, ceci fonctionne:

   dir=`cd "$dir"`

"cd" résonne sans changer le répertoire de travail, car exécuté ici dans un sous-shell.

0
Eric H.

Ce qu’ils ont dit, sauf que find $PWD ou (en bash) find ~+ est un peu plus pratique.

0
Tobu