Comment récupérer le chemin absolu d'un fichier arbitraire depuis OS X

18

Je recherche une commande simple qui peut être utilisée dans Bash pour trouver le chemin absolu et canonisé vers un fichier sur un OS X (similaire à `` readlink -f '' sous Linux).

L'exemple de session bash suivant décrit un utilitaire [fictif] appelé `` abspath '' qui présente le comportement souhaité:

$ pwd
/Users/guyfleegman

$ ls -lR
drwxr-xr-x  4 guyfleegman  crew  136 Oct 30 02:09 foo

./foo:
-rw-r--r--  1 guyfleegman  crew  0 Oct 30 02:07 bar.txt
lrwxr-xr-x  1 guyfleegman  crew  7 Oct 30 02:09 baz.txt -> bar.txt


$ abspath .
/Users/guyfleegman

$ abspath foo
/Users/guyfleegman/foo

$ abspath ./foo/bar.txt
/Users/guyfleegman/foo/bar.txt

$ abspath foo/baz.txt
/Users/guyfleegman/foo/baz.txt

Comme avec la dernière invocation de `` abspath '' dans l'exemple ci-dessus, je préférerais qu'il ne résout pas automatiquement les liens symboliques, mais je ne vais pas être trop pointilleux ici.

Michael Wehner
la source

Réponses:

16
function abspath() { pushd . > /dev/null; if [ -d "$1" ]; then cd "$1"; dirs -l +0; else cd "`dirname \"$1\"`"; cur_dir=`dirs -l +0`; if [ "$cur_dir" == "/" ]; then echo "$cur_dir`basename \"$1\"`"; else echo "$cur_dir/`basename \"$1\"`"; fi; fi; popd > /dev/null; }

Exemples:

abspath / => /

abspath /.DS_Store => /.DS_Store

abspath ~ => /Users/mschrag

cd /tmp; abspath . => /tmp

cd /; abspath .DS_Store => /.DS_Store
mschrag
la source
11

Je ne pense pas qu'il y ait une commande buildin qui fasse cela. Jesse Wilson a écrit un script bash pour cela:

#!/bin/bash
cd -P -- "$(dirname -- "$1")" &&
printf '%s\n' "$(pwd -P)/$(basename -- "$1")"

Cependant, cela ne fonctionne pas bien pour les chemins directement en dessous /, tels que /etc(impression //etc), ainsi que .et ..(impression /cwd/.dans les deux cas). J'ai essayé de le modifier, mais mon bash-fu insuffisant m'a échoué.

Voici ma suggestion:

#!/usr/bin/env python
import os.path
import sys

for arg in sys.argv[1:]:
    print os.path.abspath(arg)

Enregistrez sous /usr/bin/abspathou quelque chose comme ça et rendez-le exécutable. Exemple de sortie:

Servus08:~ danielbeck$ abspath .
/Users/danielbeck
Servus08:~ danielbeck$ abspath /tmp
/tmp
Servus08:~ danielbeck$ abspath Documents
/Users/danielbeck/Documents
Servus08:~ danielbeck$ abspath . /tmp Documents
/Users/danielbeck
/tmp
/Users/danielbeck/Documents

Si vous ne voulez résolution symlink, changer la printligne comme ceci:

    print os.path.realpath(os.path.abspath(arg))

pour obtenir ceci:

Servus08:~ danielbeck$ abspath . /tmp Documents
/Users/danielbeck
/private/tmp
/Users/danielbeck/Documents
Daniel Beck
la source
1
Je pense que vous êtes sur la bonne voie avec la première approche basée sur le shell, mais je crois que l'utilisation d'un autre langage pour faire cela va quelque peu à l'encontre du but, car il introduit des dépendances supplémentaires et est à peu près équivalent à simplement compiler la version GNU de `readlink (1) 'sous OS X (en supposant que cela puisse être fait; je ne l'ai pas encore vérifié).
Michael Wehner,
2
@Michael Vous êtes soit sur un système avec GNU readlink, soit sur OS X - non? OS X a Python prêt à l'emploi. En théorie, c'est une nouvelle dépendance, en pratique non. Quoi qu'il en soit, c'est tout ce que je peux vous offrir.
Daniel Beck
L'approche pragmatique que vous impliquez est louable, si elle est trop simpliste. Dans tous les cas, si nous adoptions cette approche au lieu de tout ce qui est écrit uniquement en bash (ce qui est absolument faisable et ne dépend de rien d'autre - cela ne peut tout simplement pas être fait en une seule ligne), Python n'est probablement pas 't le meilleur choix. Perl et Ruby (et probablement PHP) peuvent le faire succinctement sur la ligne de commande sans avoir besoin de créer un fichier de script réel.
Michael Wehner
1
@ Michael: C'est vrai, mais ce n'est pas ce que je commentais. Je vous propose une solution à 90% écrite en pure bash par Jesse Wilson + l'analyse pourquoi c'est seulement 90%. Si cela ne vous pose aucun problème, c'est bien. Je vous ai également donné une solution 100% légèrement plus compliquée en Python. Alors que d'autres langages de script peuvent être plus brefs (Perl tristement célèbre :-)), tous nécessitent un fichier supplémentaire pour stocker la commande. Ou voulez-vous l'écrire chaque fois que vous voulez l'utiliser? C'est aussi pourquoi j'ai ajouté la capacité multi-lignes pour gérer plusieurs paramètres, 1 ou 2 lignes, puis ne faites pas de différence.
Daniel Beck
2
@Michael, pourquoi Python ne serait-il pas un bon choix sur OS X? Il est même livré avec des commandes qui sont en fait des scripts Python, commefile /usr/bin/xattr
Arjan
8

Une option serait d'installer simplement coreutils et d'utiliser greadlink -f. Il résout les liens symboliques et fonctionne avec /Foo/ou ~/foo.txts'ils n'existent pas, mais pas avec /Foo/foo.txtsi /Foo/n'existe pas.

$ brew install coreutils
$ greadlink -f /etc
/private/etc
$ greadlink -f ~/Documents/
/Users/lauri/Documents
$ greadlink -f ..
/Users
$ greadlink -f //etc/..////
/private
$ greadlink -f /Foo
/Foo
$ greadlink -f /Foo/foo.txt
$ 

Cela ne résout pas les liens symboliques et ne fonctionne pas non /Foo/foo.txtplus.

abspath() {
  if [ -d "$1" ]; then
    ( cd "$1"; dirs -l +0 )
  else
    ( cd "$(dirname "$1")"; d=$(dirs -l +0); echo "${d%/}/${1##*/}" )
  fi
}

abspath /etc # /etc
abspath ~/Foo/foo.txt # doesn't work
abspath ~/Foo # works
abspath .
abspath ./
abspath ../
abspath ..
abspath /
abspath ~
abspath ~/
abspath ~/Documents
abspath /\"\ \'
abspath /etc/../etc/
abspath /private//etc/
abspath /private//
abspath //private # //private
abspath ./aa.txt
abspath aa.tar.gz
abspath .aa.txt
abspath /.DS_Store
abspath ~/Documents/Books/

dirs -leffectue l'expansion du tilde. dirs +0imprime uniquement le répertoire le plus haut s'il y a d'autres répertoires dans la pile.

Lri
la source
2

Je suppose que vous pouvez le faire avec du python ou du rubis.

$ ruby -e 'puts File.expand_path("~/somepath")'

ou en faire une commande avec

#!/usr/bin/env ruby
puts File.expand_path(ARGV[0])
Geai
la source
Mon commentaire précédent sur la réponse de Daniel Beck s'applique également ici (je préférerais une solution purement Bash-y), bien que si je devais utiliser une autre langue pour y parvenir, j'aime votre solution jusqu'à présent pour sa brièveté. :) J'envelopperais probablement aussi l'appel à ruby ​​(par exemple, la fonction abspath () {ruby -e "met File.expand_path ('$ 1')";}) et le mettrais dans mon `.profile '.
Michael Wehner
1

Si le module File :: Spec est installé pour perl, vous pouvez simplement faire ceci:

perl -MFile::Spec -e 'print File::Spec->rel2abs("../however/complex/../you/want/to.get"), "\n"'
Dodger
la source
0

Pour les scripts bash / sh, vous pouvez utiliser cette fonction récursive:

canonical_readlink ()
{
    cd `dirname $1`;
    __filename=`basename $1`;
    if [ -h "$__filename" ]; then
        canonical_readlink `readlink $__filename`;
    else
        echo "`pwd -P`";
    fi
}

answer=$(canonical_readlink $0)
Gregory Burd
la source
Certaines variables et substitutions de commandes ne sont pas citées correctement. Vous pouvez utiliser des variables locales au lieu de noms de variables comme __filename. Le script se comporte actuellement plus comme de dirnametoute façon.
Lri
0

Installez la bibliothèque suivante pour OSX:

brew install coreutils

greadlink -f file.txt
anh_ng8
la source
0

Si l'installation de coreutils n'est pas une option, ce qui suit gère les combinaisons de liens symboliques,. et .. et fonctionne sur des fichiers et des dossiers comme GNU realpath:

#!/usr/bin/env bash
realpath()
{
    if ! pushd $1 &> /dev/null; then 
        pushd ${1##*/} &> /dev/null
        echo $( pwd -P )/${1%/*}
    else
        pwd -P
    fi
    popd > /dev/null
}

Mais il ne prend pas en charge --relative-to de realpath. Cela nécessiterait /programming//a/12498485/869951 .

Oliver
la source
0

Étant donné que la contrainte est MacOS (OS X à l'époque), PHP est disponible par défaut. Cela renverra le répertoire racine du fichier. Supprimez également dirnamele fichier.

export SOURCE_DIRECTORY="$(php -r 'echo dirname(realpath($argv[1]));' -- "${BASH_SOURCE[0]}")"
danemacmillan
la source