Bash script absolute path avec OSX

j'essaie d'obtenir le chemin absolu vers le script en cours d'exécution sur OS X.

j'ai vu beaucoup de réponses allant pour readlink -f " 151900920" . Cependant, comme le readlink D'OS X est le même que celui de BSD, il ne fonctionne tout simplement pas (il fonctionne avec la version de GNU).

y a-t-il des suggestions pour une solution à ce problème?

56
demandé sur Charles 2010-08-26 08:24:02

10 réponses

il y a une fonction realpath() C qui fera l'affaire, mais je ne vois rien de disponible sur la ligne de commande. Voici un remplacement rapide et sale:

#!/bin/bash

realpath() {
    [[  = /* ]] && echo "" || echo "$PWD/${1#./}"
}

realpath ""151900920""

cela imprime le chemin mot à mot s'il commence par un / . Si non, il doit être un chemin relatif, il ajoute $PWD à l'avant. La partie #./ se détache ./ de l'avant de .

61
répondu John Kugelman 2010-08-26 04:48:06

ces trois étapes simples vont résoudre ceci et beaucoup d'autres problèmes OSX:

  1. Installer Homebrew
  2. brew install coreutils
  3. grealpath .

(3) peut être modifiée à realpath , voir (2) de sortie

67
répondu Oleg Mikheev 2015-05-15 19:37:15

Ugh. J'ai trouvé les réponses préalables un peu insuffisantes pour quelques raisons: en particulier, elles ne résolvent pas plusieurs niveaux de liens symboliques, et elles sont extrêmement "Bash-y". Alors que la question originale demande explicitement un "script Bash", elle fait aussi mention du "BSD-like" de Mac OS X, non-GNU readlink . Donc voici une tentative de portabilité raisonnable (je l'ai vérifié avec bash comme 'sh' et dash), la résolution d'un nombre arbitraire de liens symboliques; et il devrait également fonctionner avec les espaces dans le(S) chemin (s), bien que je ne suis pas sûr du comportement s'il y a espace blanc le nom de base de l'utilitaire lui-même, donc peut-être, euh, éviter cela?

#!/bin/sh
realpath() {
  OURPWD=$PWD
  cd "$(dirname "")"
  LINK=$(readlink "$(basename "")")
  while [ "$LINK" ]; do
    cd "$(dirname "$LINK")"
    LINK=$(readlink "$(basename "")")
  done
  REALPATH="$PWD/$(basename "")"
  cd "$OURPWD"
  echo "$REALPATH"
}
realpath "$@"

l'Espérance qui peut être utile à quelqu'un.

18
répondu Geoff Nixon 2013-12-21 02:34:31

je cherchais une solution à utiliser dans un script de fourniture de système, c'est-à-dire avant même que Homebrew ne soit installé. En l'absence d'une solution appropriée, Je déchargerais simplement la tâche dans un langage multiplateforme, par exemple Perl:

script_abspath=$(perl -e 'use Cwd "abs_path"; print abs_path(@ARGV[0])' -- ""151900920"")

plus souvent, ce que nous voulons réellement est le répertoire contenant:

here=$(perl -e 'use File::Basename; use Cwd "abs_path"; print dirname(abs_path(@ARGV[0]));' -- ""151910920"")
6
répondu 4ae1e1 2015-09-18 02:21:30

utilisez python pour l'obtenir

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


print(os.path.realpath(sys.argv[1]))
6
répondu kcats 2017-05-28 10:01:52

Puisqu'il y a un realpath comme d'autres l'ont fait remarquer:

// realpath.c
#include <stdio.h>
#include <stdlib.h>

int main (int argc, char* argv[])
{
  if (argc > 1) {
    for (int argIter = 1; argIter < argc; ++argIter) {
      char *resolved_path_buffer = NULL;
      char *result = realpath(argv[argIter], resolved_path_buffer);

      puts(result);

      if (result != NULL) {
        free(result);
      }
    }
  }

  return 0;
}

Makefile:

#Makefile
OBJ = realpath.o

%.o: %.c
      $(CC) -c -o $@ $< $(CFLAGS)

realpath: $(OBJ)
      gcc -o $@ $^ $(CFLAGS)

puis compiler avec make et mettre en lien avec:

ln -s $(pwd)/realpath /usr/local/bin/realpath

6
répondu WaffleSouffle 2017-10-16 14:41:47

une variante plus conviviale de la solution Python en ligne de commande:

python -c "import os; print(os.path.realpath(''))"
6
répondu nanav yorbiz 2017-10-27 21:29:58

comme vous pouvez le voir ci-dessus, j'ai pris une photo il y a environ 6 mois. Je suis totalement d' Je l'ai oublié jusqu'à ce que je me retrouve à avoir besoin d'une chose similaire. J'ai été complètement choqué pour voir à quel point c'était rudimentaire; j'ai enseigné moi-même à coder assez intensivement depuis environ un an maintenant, mais je me sens souvent comme peut-être que je n'ai rien appris du tout quand les choses sont au pire.

je voudrais enlever la "solution" ci-dessus, mais je vraiment comme il a en quelque sorte d'être un record de de tout ce que j'ai vraiment appris au cours des derniers mois.

mais je m'écarte. Je me suis assis et a travaillé toute la nuit dernière. L'explication en les commentaires devraient être suffisantes. Si vous souhaitez suivre la copie je continue pour travailler sur, vous pouvez suivre cet essentiel. Cela fait probablement ce dont vous avez besoin.

#!/bin/sh # dash bash ksh # !zsh (issues). G. Nixon, 12/2013. Public domain.

## 'linkread' or 'fullpath' or (you choose) is a little tool to recursively
## dereference symbolic links (ala 'readlink') until the originating file
## is found. This is effectively the same function provided in stdlib.h as
## 'realpath' and on the command line in GNU 'readlink -f'.

## Neither of these tools, however, are particularly accessible on the many
## systems that do not have the GNU implementation of readlink, nor ship
## with a system compiler (not to mention the requisite knowledge of C).

## This script is written with portability and (to the extent possible, speed)
## in mind, hence the use of printf for echo and case statements where they
## can be substituded for test, though I've had to scale back a bit on that.

## It is (to the best of my knowledge) written in standard POSIX shell, and
## has been tested with bash-as-bin-sh, dash, and ksh93. zsh seems to have
## issues with it, though I'm not sure why; so probably best to avoid for now.

## Particularly useful (in fact, the reason I wrote this) is the fact that
## it can be used within a shell script to find the path of the script itself.
## (I am sure the shell knows this already; but most likely for the sake of
## security it is not made readily available. The implementation of ""151900920""
## specificies that the "151900920" must be the location of **last** symbolic link in
## a chain, or wherever it resides in the path.) This can be used for some
## ...interesting things, like self-duplicating and self-modifiying scripts.

## Currently supported are three errors: whether the file specified exists
## (ala ENOENT), whether its target exists/is accessible; and the special
## case of when a sybolic link references itself "foo -> foo": a common error
## for beginners, since 'ln' does not produce an error if the order of link
## and target are reversed on the command line. (See POSIX signal ELOOP.)

## It would probably be rather simple to write to use this as a basis for
## a pure shell implementation of the 'symlinks' util included with Linux.

## As an aside, the amount of code below **completely** belies the amount
## effort it took to get this right -- but I guess that's coding for you.

##===-------------------------------------------------------------------===##

for argv; do :; done # Last parameter on command line, for options parsing.

## Error messages. Use functions so that we can sub in when the error occurs.

recurses(){ printf "Self-referential:\n\t$argv ->\n\t$argv\n" ;}
dangling(){ printf "Broken symlink:\n\t$argv ->\n\t"$(readlink "$argv")"\n" ;}
errnoent(){ printf "No such file: "$@"\n" ;} # Borrow a horrible signal name.

# Probably best not to install as 'pathfull', if you can avoid it.

pathfull(){ cd "$(dirname "$@")"; link="$(readlink "$(basename "$@")")"

## 'test and 'ls' report different status for bad symlinks, so we use this.

 if [ ! -e "$@" ]; then if $(ls -d "$@" 2>/dev/null) 2>/dev/null;  then
    errnoent 1>&2; exit 1; elif [ ! -e "$@" -a "$link" = "$@" ];   then
    recurses 1>&2; exit 1; elif [ ! -e "$@" ] && [ ! -z "$link" ]; then
    dangling 1>&2; exit 1; fi
 fi

## Not a link, but there might be one in the path, so 'cd' and 'pwd'.

 if [ -z "$link" ]; then if [ "$(dirname "$@" | cut -c1)" = '/' ]; then
   printf "$@\n"; exit 0; else printf "$(pwd)/$(basename "$@")\n"; fi; exit 0
 fi

## Walk the symlinks back to the origin. Calls itself recursivly as needed.

 while [ "$link" ]; do
   cd "$(dirname "$link")"; newlink="$(readlink "$(basename "$link")")"
   case "$newlink" in
    "$link") dangling 1>&2 && exit 1                                       ;;
         '') printf "$(pwd)/$(basename "$link")\n"; exit 0                 ;;
          *) link="$newlink" && pathfull "$link"                           ;;
   esac
 done
 printf "$(pwd)/$(basename "$newlink")\n"
}

## Demo. Install somewhere deep in the filesystem, then symlink somewhere 
## else, symlink again (maybe with a different name) elsewhere, and link
## back into the directory you started in (or something.) The absolute path
## of the script will always be reported in the usage, along with ""151900920"".

if [ -z "$argv" ]; then scriptname="$(pathfull ""151900920"")"

# Yay ANSI l33t codes! Fancy.
 printf "\n3[3mfrom/as: 3[4m"151900920"3[0m\n\n3[1mUSAGE:3[0m   "
 printf "3[4m$scriptname3[24m [ link | file | dir ]\n\n         "
 printf "Recursive readlink for the authoritative file, symlink after "
 printf "symlink.\n\n\n         3[4m$scriptname3[24m\n\n        "
 printf " From within an invocation of a script, locate the script's "
 printf "own file\n         (no matter where it has been linked or "
 printf "from where it is being called).\n\n"

else pathfull "$@"
fi
2
répondu Geoff Nixon 2013-12-21 18:10:49

realpath for Mac OS X

realpath() {
    path=`eval echo ""`
    folder=$(dirname "$path")
    echo $(cd "$folder"; pwd)/$(basename "$path"); 
}

exemple avec le chemin correspondant:

realpath "../scripts/test.sh"

exemple avec dossier personnel

realpath "~/Test/../Test/scripts/test.sh"
2
répondu Evgeny Karpov 2018-01-21 14:22:56

jetez un oeil à cette question. J'ai trouvé la réponse plus concise.

obtenir le chemin où un script shell osx est situé dans le script...quand le chemin a un espace blanc

0
répondu Nate 2017-05-23 12:18:15