Ruta absoluta del script Bash con OS X

98

Estoy tratando de obtener la ruta absoluta al script que se está ejecutando actualmente en OS X.

Vi muchas respuestas a su favor readlink -f $0. Sin embargo, dado que OS X readlinkes igual que BSD, simplemente no funciona (funciona con la versión de GNU).

¿Existe una solución inmediata para esto?

El poderoso pato de goma
fuente

Respuestas:

88

Hay una realpath()función C que hará el trabajo, pero no veo nada disponible en la línea de comandos. Aquí hay un reemplazo rápido y sucio:

#!/bin/bash

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

realpath "$0"

Esto imprime la ruta textualmente si comienza con a /. De lo contrario, debe ser una ruta relativa, por lo que antepone $PWDal frente. La #./parte se quita ./desde el frente de $1.

John Kugelman
fuente
1
También noté la función C pero no pude encontrar ningún binario correspondiente ni nada. De todos modos, tu función se adapta perfectamente a mis necesidades. ¡Gracias!
The Mighty Rubber Duck
7
Tenga en cuenta que esto no elimina la referencia a los enlaces simbólicos.
Adam Vandenberg
17
realpath ../somethingdevuelve$PWD/../something
Lri
Esto funcionó para mí, pero lo renombré "realpath_osx ()" ya que PUEDE tener que enviar este script bash a un shell de Linux y no quería que colisionara con el realpath "real". (Estoy seguro de que hay una forma más elegante, pero soy un bash n00b.)
Andrew Theken
8
command -v realpath >/dev/null 2>&1 || realpath() { ... }
Kara Brightwell
115

Estos tres sencillos pasos resolverán este y muchos otros problemas de OS X:

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

(3) se puede cambiar a solo realpath, ver (2) salida

Oleg Mikheev
fuente
3
Esto resuelve el problema, pero requiere instalar cosas que quizás no quieras o necesites, o que quizás no estén en el sistema de destino, es decir, OS X
Jason S
6
@JasonS GNU Coreutils es demasiado bueno para no usarlo. Incluye muchas utilidades geniales. De hecho, es tan bueno que el kernel de Linux es inútil sin él y por eso algunas personas lo llaman GNU / Linux. Coreutils es asombroso. excelente respuesta esta debería ser la respuesta seleccionada.
7
@omouse La pregunta menciona específicamente 'una solución lista para usar' (para OS X). Independientemente de lo asombroso que sea coreutils, no está "listo para usar" en OS X, por lo que la respuesta no es buena. No es una cuestión de qué tan bueno es coreutils, o si es mejor que lo que hay en OS X. Hay soluciones " $( cd "$(dirname "$0")" ; pwd -P )listas para usar" que funcionan bien para mí.
Jason S
2
Una de las "características" de OS X es que el directorio y los nombres de archivo no distinguen entre mayúsculas y minúsculas. Como resultado, si usted tiene un directorio llamado XXXy alguien cd xxxentonces pwdvolverá .../xxx. Excepto por esta respuesta, todas las soluciones anteriores regresan xxxcuando lo que realmente desea es XXX. ¡Gracias!
Andrew
1
No sé cómo copiar, pegar y reinventar código sin cesar es mejor que una solución de administrador de paquetes existente. Si lo necesita realpath, ¿qué sucede cuando es casi seguro que necesitará otros elementos coreutils? ¿Reescribir esas funciones en bash también? : P
Ezekiel Victor
28

Ugh. Encontré las respuestas anteriores un poco deficientes por algunas razones: en particular, no resuelven múltiples niveles de enlaces simbólicos, y son extremadamente "Bash-y". Si bien la pregunta original pide explícitamente un "script Bash", también menciona el tipo BSD de Mac OS X, que no es GNU readlink. Así que aquí hay un intento de una portabilidad razonable (lo he comprobado con bash como 'sh' y guión), resolviendo un número arbitrario de enlaces simbólicos; y también debería funcionar con espacios en blanco en la (s) ruta (s), aunque no estoy seguro del comportamiento si hay espacios en blanco en el nombre base de la utilidad en sí, así que tal vez, um, ¿evitarlo?

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

Espero que pueda ser de alguna utilidad para alguien.

Geoff Nixon
fuente
1
Solo recomendaría usar localpara variables definidas dentro de la función para no contaminar el espacio de nombres global. Ej local OURPWD=.... Funciona al menos para bash.
Michael Paesold
Además, el código no debe usar mayúsculas para variables privadas. Las variables en mayúsculas están reservadas para uso del sistema.
tripleee
Gracias por el guion. En caso de que el enlace (s) y el archivo real tengan nombres base diferentes, probablemente sería una buena idea agregar un " BASENAME=$(basename "$LINK") inside the while" y usarlo en el segundo setter LINK y el setter
REALPATH
Esto no maneja los enlaces simbólicos y ..las referencias de los padres como lo realpathharía. Con homebrew coreutilsinstalado, intente ln -s /var/log /tmp/linkexampleentonces realpath /tmp/linkexample/../; esto imprime /private/var. Pero su función produce /tmp/linkexample/..en su lugar, porque ..no es un enlace simbólico.
Martijn Pieters
10

Una variante más amigable con la línea de comandos de la solución Python:

python -c "import os; print(os.path.realpath('$1'))"
nanav yorbiz
fuente
2
En caso de que alguien esté lo suficientemente loco como para iniciar un intérprete de Python para un solo comando ...
Bachsau
Estaba lo suficientemente enojado, pero usadopython -c "import os; import sys; print(os.path.realpath(sys.argv[1]))"
Alex Chamberlain
7

Estaba buscando una solución para usar en un script de provisión del sistema, es decir, ejecutar antes de que Homebrew esté instalado. Al carecer de una solución adecuada, simplemente descargaría la tarea a un lenguaje multiplataforma, por ejemplo, Perl:

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

Más a menudo lo que realmente queremos es el directorio contenedor:

here=$(perl -e 'use File::Basename; use Cwd "abs_path"; print dirname(abs_path(@ARGV[0]));' -- "$0")
4ae1e1
fuente
¡Excelente! ¡Perl es bueno para los pequeños gastos generales! Probablemente podría simplificar la primera versión a FULLPATH=$(perl -e "use Cwd 'abs_path'; print abs_path('$0')"). ¿Alguna razón en contra?
F Pereira
@FPereira Generar código de programa a partir de una cadena proporcionada por el usuario sin escape nunca es una buena idea. ''no es a prueba de balas. Se rompe si $0contiene una sola comilla, por ejemplo. Un ejemplo muy simple: pruebe su versión /tmp/'/test.shy llame /tmp/'/test.shpor su ruta completa.
4ae1e1
O aún más simple, /tmp/'.sh.
4ae1e1
6

Dado que hay un camino real como otros han señalado:

// 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)

Luego compile makey coloque un enlace suave con:
ln -s $(pwd)/realpath /usr/local/bin/realpath

WaffleSouffle
fuente
Podríamos acabamos de hacer gcc realpath.c -o /usr/local/bin/realpath?
Alexander Mills
1
@AlexanderMills No debe ejecutar el compilador como root; y luego, si no lo hace, no tendrá los privilegios para escribir/usr/local/bin
tripleee
6

Usa Python para conseguirlo:

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

print(os.path.realpath(sys.argv[1]))
acrazar
fuente
2

En macOS, la única solución que he encontrado para esto que maneja de manera confiable los enlaces simbólicos es usando realpath. Dado que esto requiere brew install coreutils, simplemente automaticé ese paso. Mi implementación se ve así:

#!/usr/bin/env bash

set -e

if ! which realpath >&/dev/null; then
  if ! which brew >&/dev/null; then
    msg="ERROR: This script requires brew. See https://brew.sh for installation instructions."
    echo "$(tput setaf 1)$msg$(tput sgr0)" >&2
    exit 1
  fi
  echo "Installing coreutils/realpath"
  brew install coreutils >&/dev/null
fi

thisDir=$( dirname "`realpath "$0"`" )
echo "This script is run from \"$thisDir\""


Este error si no se ha brewinstalado, pero también puede instalarlo. Simplemente no me sentía cómodo automatizando algo que cursa código rubí arbitrario de la red.

Tenga en cuenta que esta es una variación automática de la respuesta de Oleg Mikheev .


Una prueba importante

Una buena prueba de cualquiera de estas soluciones es:

  1. poner el código en un archivo de script en algún lugar
  2. en otro directorio, enlace simbólico ( ln -s) a ese archivo
  3. ejecutar el script desde ese enlace simbólico

¿La solución elimina la referencia al enlace simbólico y le proporciona el directorio original? Si es así, funciona.

Puentes de arcilla
fuente
2
abs_path () {    
   echo "$(cd $(dirname "$1");pwd)/$(basename "$1")"
}

dirnamedará el nombre del directorio de /path/to/file, es decir /path/to.

cd /path/to; pwd asegura que la ruta sea absoluta.

basenamele dará solo el nombre del archivo /path/to/file, es decir file.

Logu
fuente
Si bien este código puede responder a la pregunta, proporcionar un contexto adicional sobre por qué y / o cómo este código responde a la pregunta mejora su valor a largo plazo.
Igor F.
1

Entonces, como puede ver arriba, lo intenté hace unos 6 meses. Lo olvidé por completo hasta que volví a necesitar algo similar. Estaba completamente sorprendido de ver cuán rudimentario era; He estado aprendiendo a codificar de forma bastante intensiva durante aproximadamente un año, pero a menudo siento que tal vez no he aprendido nada cuando las cosas están en su peor momento.

Quitaría la 'solución' anterior, pero realmente me gusta que sea un registro de lo mucho que realmente he aprendido en los últimos meses.

Pero yo divago. Me senté y resolví todo anoche. La explicación en los comentarios debería ser suficiente. Si desea realizar un seguimiento de la copia en la que sigo trabajando, puede seguir esta esencia. Probablemente esto haga lo que necesita.

#!/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 "$0"
## specificies that the $0 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 "$0".

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

# Yay ANSI l33t codes! Fancy.
 printf "\n\033[3mfrom/as: \033[4m$0\033[0m\n\n\033[1mUSAGE:\033[0m   "
 printf "\033[4m$scriptname\033[24m [ link | file | dir ]\n\n         "
 printf "Recursive readlink for the authoritative file, symlink after "
 printf "symlink.\n\n\n         \033[4m$scriptname\033[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
Geoff Nixon
fuente
1
El vínculo esencial parece roto.
Alex
esta respuesta funciona: stackoverflow.com/a/46772980/1223975 .... solo debes usar eso.
Alexander Mills
Tenga en cuenta que su implementación no resuelve los enlaces simbólicos antes de ..las referencias principales; por ejemplo, /foo/link_to_other_directory/..se resuelve como /fooel padre de cualquier ruta a la que /foo/link_to_other_directoryapunte el enlace simbólico . readlink -fy realpathresuelva cada componente de la ruta comenzando en la raíz y actualizando los destinos de enlace anteriores al resto que aún se está procesando. Agregué una respuesta a esta pregunta reimplementando esa lógica.
Martijn Pieters
1

realpath para Mac OS X

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

Ejemplo con ruta relacionada:

realpath "../scripts/test.sh"

Ejemplo con carpeta de inicio

realpath "~/Test/../Test/scripts/test.sh"
Evgeny Karpov
fuente
1
buena solución simple, acabo de encontrar una advertencia, cuando invocarla con ..ella no produce la respuesta correcta, así que agregué una verificación si la ruta dada es un directorio: if test -d $path ; then echo $(cd "$path"; pwd) ; else [...]
herbert
No funcionó para mí, mientras "$(dirname $(dirname $(realpath $0)))"que funciona, así que necesito algo más ...
Alexander Mills
El uso inútil deecho no debería también ser perpetrado.
tripleee
En realidad, esto no resuelve los enlaces simbólicos como lo haría realpath. Resuelve ..las referencias a los padres antes de resolver los enlaces simbólicos, no después; intente esto con Homebrew coreutilsinstalado, cree un enlace y ln -s /var/log /tmp/linkexampleluego ejecute realpath /tmp/linkexample/../; esto imprime /private/var. Pero su función produce /tmp/linkexample/..en su lugar, porque pwdtodavía se muestra /tmp/linkexampledespués del cd.
Martijn Pieters
0

Esto parece funcionar para OSX, no requiere ningún binario y se extrajo de aquí

function normpath() {
  # Remove all /./ sequences.
  local path=${1//\/.\//\/}

  # Remove dir/.. sequences.
  while [[ $path =~ ([^/][^/]*/\.\./) ]]; do
    path=${path/${BASH_REMATCH[0]}/}
  done
  echo $path
}
Brad Parks
fuente
0

Me gusta esto:

#!/usr/bin/env bash
function realpath() {
    local _X="$PWD"
    local _LNK=$1
    cd "$(dirname "$_LNK")"
    if [ -h "$_LNK" ]; then
        _LNK="$(readlink "$_LNK")"
        cd "$(dirname "$_LNK")"
    fi
    echo "$PWD/$(basename "$_LNK")"
    cd "$_X"
}
dcmorse
fuente
0

Necesitaba un realpathreemplazo en OS X, uno que funcione correctamente en rutas con enlaces simbólicos y referencias de padres como lo readlink -fharía . Esto incluye resolver enlaces simbólicos en la ruta antes de resolver las referencias principales; por ejemplo, si ha instalado la coreutilsbotella de cerveza casera, ejecute:

$ ln -s /var/log/cups /tmp/linkeddir  # symlink to another directory
$ greadlink -f /tmp/linkeddir/..      # canonical path of the link parent
/private/var/log

Tenga en cuenta que se readlink -fha resuelto /tmp/linkeddir antes de resolver la ..referencia del directorio principal. Por supuesto, no hay es readlink -fen Mac cualquiera .

Entonces, como parte de la implementación de a bash, realpathvolví a implementar lo que hace una llamada de canonicalize_filename_mode(path, CAN_ALL_BUT_LAST)función GNUlib , en Bash 3.2; esta es también la llamada de función que readlink -fhace GNU :

# shellcheck shell=bash
set -euo pipefail

_contains() {
    # return true if first argument is present in the other arguments
    local elem value

    value="$1"
    shift

    for elem in "$@"; do 
        if [[ $elem == "$value" ]]; then
            return 0
        fi
    done
    return 1
}

_canonicalize_filename_mode() {
    # resolve any symlink targets, GNU readlink -f style
    # where every path component except the last should exist and is
    # resolved if it is a symlink. This is essentially a re-implementation
    # of canonicalize_filename_mode(path, CAN_ALL_BUT_LAST).
    # takes the path to canonicalize as first argument

    local path result component seen
    seen=()
    path="$1"
    result="/"
    if [[ $path != /* ]]; then  # add in current working dir if relative
        result="$PWD"
    fi
    while [[ -n $path ]]; do
        component="${path%%/*}"
        case "$component" in
            '') # empty because it started with /
                path="${path:1}" ;;
            .)  # ./ current directory, do nothing
                path="${path:1}" ;;
            ..) # ../ parent directory
                if [[ $result != "/" ]]; then  # not at the root?
                    result="${result%/*}"      # then remove one element from the path
                fi
                path="${path:2}" ;;
            *)
                # add this component to the result, remove from path
                if [[ $result != */ ]]; then
                    result="$result/"
                fi
                result="$result$component"
                path="${path:${#component}}"
                # element must exist, unless this is the final component
                if [[ $path =~ [^/] && ! -e $result ]]; then
                    echo "$1: No such file or directory" >&2
                    return 1
                fi
                # if the result is a link, prefix it to the path, to continue resolving
                if [[ -L $result ]]; then
                    if _contains "$result" "${seen[@]+"${seen[@]}"}"; then
                        # we've seen this link before, abort
                        echo "$1: Too many levels of symbolic links" >&2
                        return 1
                    fi
                    seen+=("$result")
                    path="$(readlink "$result")$path"
                    if [[ $path = /* ]]; then
                        # if the link is absolute, restart the result from /
                        result="/"
                    elif [[ $result != "/" ]]; then
                        # otherwise remove the basename of the link from the result
                        result="${result%/*}"
                    fi
                elif [[ $path =~ [^/] && ! -d $result ]]; then
                    # otherwise all but the last element must be a dir
                    echo "$1: Not a directory" >&2
                    return 1
                fi
                ;;
        esac
    done
    echo "$result"
}

Incluye detección de enlace simbólico circular, saliendo si la misma ruta (intermedia) se ve dos veces.

Si todo lo que necesita es readlink -f, puede utilizar lo anterior como:

readlink() {
    if [[ $1 != -f ]]; then  # poor-man's option parsing
        # delegate to the standard readlink command
        command readlink "$@"
        return
    fi

    local path result seenerr
    shift
    seenerr=
    for path in "$@"; do
        # by default readlink suppresses error messages
        if ! result=$(_canonicalize_filename_mode "$path" 2>/dev/null); then
            seenerr=1
            continue
        fi
        echo "$result"
    done
    if [[ $seenerr ]]; then
        return 1;
    fi
}

Porque realpath, también necesitaba --relative-toy --relative-baseapoyo, lo que le brinda rutas relativas después de canonicalizar:

_realpath() {
    # GNU realpath replacement for bash 3.2 (OS X)
    # accepts --relative-to= and --relative-base options
    # and produces canonical (relative or absolute) paths for each
    # argument on stdout, errors on stderr, and returns 0 on success
    # and 1 if at least 1 path triggered an error.

    local relative_to relative_base seenerr path

    relative_to=
    relative_base=
    seenerr=

    while [[ $# -gt 0 ]]; do
        case $1 in
            "--relative-to="*)
                relative_to=$(_canonicalize_filename_mode "${1#*=}")
                shift 1;;
            "--relative-base="*)
                relative_base=$(_canonicalize_filename_mode "${1#*=}")
                shift 1;;
            *)
                break;;
        esac
    done

    if [[
        -n $relative_to
        && -n $relative_base
        && ${relative_to#${relative_base}/} == "$relative_to"
    ]]; then
        # relative_to is not a subdir of relative_base -> ignore both
        relative_to=
        relative_base=
    elif [[ -z $relative_to && -n $relative_base ]]; then
        # if relative_to has not been set but relative_base has, then
        # set relative_to from relative_base, simplifies logic later on
        relative_to="$relative_base"
    fi

    for path in "$@"; do
        if ! real=$(_canonicalize_filename_mode "$path"); then
            seenerr=1
            continue
        fi

        # make path relative if so required
        if [[
            -n $relative_to
            && ( # path must not be outside relative_base to be made relative
                -z $relative_base || ${real#${relative_base}/} != "$real"
            )
        ]]; then
            local common_part parentrefs

            common_part="$relative_to"
            parentrefs=
            while [[ ${real#${common_part}/} == "$real" ]]; do
                common_part="$(dirname "$common_part")"
                parentrefs="..${parentrefs:+/$parentrefs}"
            done

            if [[ $common_part != "/" ]]; then
                real="${parentrefs:+${parentrefs}/}${real#${common_part}/}"
            fi
        fi

        echo "$real"
    done
    if [[ $seenerr ]]; then
        return 1
    fi
}

if ! command -v realpath > /dev/null 2>&1; then
    # realpath is not available on OSX unless you install the `coreutils` brew
    realpath() { _realpath "$@"; }
fi

Incluí pruebas unitarias en mi solicitud de revisión de código para este código .

Martijn Pieters
fuente
-2

Basado en la comunicación con el comentarista, estuve de acuerdo en que es muy difícil y no tiene una forma trivial de implementar un camino real que se comporta totalmente igual que Ubuntu.

Pero la siguiente versión, puede manejar casos de esquina, la mejor respuesta no puede y satisfacer mis necesidades diarias en macbook. Pon este código en tu ~ / .bashrc y recuerda:

  • arg solo puede ser 1 archivo o dir, sin comodines
  • sin espacios en el directorio o el nombre del archivo
  • al menos el archivo o el directorio principal del directorio existe
  • siéntete libre de usar. .. / cosa, estos son seguros

    # 1. if is a dir, try cd and pwd
    # 2. if is a file, try cd its parent and concat dir+file
    realpath() {
     [ "$1" = "" ] && return 1

     dir=`dirname "$1"`
     file=`basename "$1"`

     last=`pwd`

     [ -d "$dir" ] && cd $dir || return 1
     if [ -d "$file" ];
     then
       # case 1
       cd $file && pwd || return 1
     else
       # case 2
       echo `pwd`/$file | sed 's/\/\//\//g'
     fi

     cd $last
    }
occia
fuente
Quieres evitar el uso inútil deecho . Simplemente pwdhace lo mismo que echo $(pwd)sin tener que generar una segunda copia del shell. Además, no citar el argumento de echoes un error (perderá cualquier espacio en blanco inicial o final, cualquier carácter de espacio en blanco interno adyacente y se expandirán los comodines, etc.). Ver más stackoverflow.com/questions/10067266/…
tripleee
Además, el comportamiento de rutas inexistentes tiene errores; pero supongo que eso es lo que quizás esté tratando de decir la oración "pero recuerda". Aunque el comportamiento en Ubuntu ciertamente no es imprimir el directorio actual cuando solicita el realpathde un directorio inexistente.
tripleee
Por coherencia, probablemente prefiera en dir=$(dirname "$1"); file=$(basename "$1")lugar de la sintaxis de comillas invertidas, obsoleta. También observe la cita correcta de los argumentos, nuevamente.
tripleee
Su respuesta actualizada parece no corregir muchos de los errores y agrega otros nuevos.
tripleee
por favor, denme algunos casos específicos fallidos, porque todas las pruebas que hago en el escritorio ubuntu 18.04 están bien, gracias.
occia