Pregunta ¿Manera confiable para que un script bash obtenga el camino completo a sí mismo? [duplicar]


Esta pregunta ya tiene una respuesta aquí:

Tengo un script bash que necesita saber su camino completo. Estoy tratando de encontrar una manera ampliamente compatible de hacerlo sin terminar con caminos relativos o de aspecto funky. Solo necesito apoyar bash, no sh, csh, etc.

Lo que he encontrado hasta ahora:

  1. La respuesta aceptada para "Obtener el directorio fuente de un script Bash desde dentro"direcciones para obtener la ruta del script a través de dirname $0, lo cual está bien, pero eso puede devolver un relativo camino (como .), que es un problema si desea cambiar directorios en el script y hacer que la ruta siga apuntando al directorio del script. Todavía, dirname será parte del rompecabezas.

  2. La respuesta aceptada para "Ruta absoluta del script Bash con OSX" (OS X específico, pero la respuesta funciona independientemente) da una función que probará para ver si $0 parece relativo y si es así pre-pend $PWD lo. Pero el resultado todavía puede tener bits relativos (aunque en general es absoluto); por ejemplo, si el script es t en el directorio /usr/bin y estás en /usr y tu escribes bin/../bin/t para ejecutarlo (sí, eso es complicado), terminas con /usr/bin/../bin como la ruta del directorio del script. Cual trabajos, pero...

  3. los readlink solución en esta página, que se ve así:

    # Absolute path to this script. /home/user/bin/foo.sh
    SCRIPT=$(readlink -f $0)
    # Absolute path this script is in. /home/user/bin
    SCRIPTPATH=`dirname $SCRIPT`
    

    Pero readlink no es POSIX y aparentemente la solución depende de GNU readlink donde BSD no funcionará por alguna razón (no tengo acceso a un sistema similar a BSD para verificar).

Entonces, varias formas de hacerlo, pero todas tienen sus advertencias.

¿Cuál sería una mejor manera? Donde "mejor" significa:

  • Me da el camino absoluto
  • Saca funky bits incluso cuando se invoca de una manera complicada (ver el comentario en el n. ° 2 arriba). (Por ejemplo, al menos canonicaliza moderadamente la ruta).
  • Se basa solo en bash-ismos o cosas que casi con toda seguridad se encontrarán en los sabores más populares de los sistemas * nix (sistemas GNU / Linux, BSD y similares a BSD como OS X, etc.).
  • Evita llamar a programas externos si es posible (por ejemplo, prefiere bash built-ins).
  • (Actualizado, Gracias por el aviso, cuales) No tiene que resolver los enlaces simbólicos (de hecho, preferiría que los dejara en paz, pero eso no es un requisito).

505
2018-01-23 13:24


origen


Respuestas:


Esto es lo que se me ocurrió (editar: más algunos ajustes proporcionados por sfstewman, Levigroker, Kyle Strandy Rob Kennedy), que parece ajustarse a mis "mejores" criterios:

SCRIPTPATH="$( cd "$(dirname "$0")" ; pwd -P )"

Ese SCRIPTPATH línea parece particularmente indirecta, pero la necesitamos en lugar de SCRIPTPATH=`pwd` para manejar adecuadamente espacios y enlaces simbólicos.

Tenga en cuenta también que las situaciones esotéricas, como ejecutar un script que no provenga de un archivo en un sistema de archivos accesible (lo cual es perfectamente posible), no se atiende allí (o en cualquiera de las otras respuestas que he visto )


393
2018-06-20 07:11



Estoy sorprendido de que realpath comando no ha sido mencionado aquí. Tengo entendido que es ampliamente portable / portado.

Su solución inicial se convierte en:

SCRIPT=`realpath $0`
SCRIPTPATH=`dirname $SCRIPT`

Y para dejar enlaces simbólicos sin resolver según su preferencia:

SCRIPT=`realpath -s $0`
SCRIPTPATH=`dirname $SCRIPT`

153
2018-02-02 04:03



La forma más simple que he encontrado para obtener una ruta canónica completa en bash es usar cd y pwd:

ABSOLUTE_PATH="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/$(basename "${BASH_SOURCE[0]}")"

Utilizando ${BASH_SOURCE[0]} en lugar de $0 produce el mismo comportamiento independientemente de si el script se invoca como <name> o source <name>


135
2018-06-15 12:21



Solo tuve que volver a visitar este tema hoy y encontré https://stackoverflow.com/a/246128/1034080. Desarrolla una solución que Lo he usado en el pasado también.

DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"

Hay más variantes en la respuesta vinculada, p. para el caso donde el script en sí es un enlace simbólico.


37
2017-08-30 13:04



Obtener la ruta absoluta del script de shell

No se usa -f opción en readlink, por lo tanto, debería funcionar en bsd / mac-osx

Apoyos

  • source ./script (Cuando lo llama el . operador punto)
  • Ruta / ruta / a / script absolutas
  • Ruta relativa como ./script
  • /path/dir1/../dir2/dir3/../script
  • Cuando se llama desde symlink
  • Cuando el enlace simbólico está anidado, por ejemplo) foo->dir1/dir2/bar bar->./../doe doe->script
  • Cuando la persona que llama cambia el nombre de las secuencias de comandos

Estoy buscando casos de esquina donde este código no funciona. Por favor hagamelo saber.

Código

pushd . > /dev/null
SCRIPT_PATH="${BASH_SOURCE[0]}";
while([ -h "${SCRIPT_PATH}" ]); do
    cd "`dirname "${SCRIPT_PATH}"`"
    SCRIPT_PATH="$(readlink "`basename "${SCRIPT_PATH}"`")";
done
cd "`dirname "${SCRIPT_PATH}"`" > /dev/null
SCRIPT_PATH="`pwd`";
popd  > /dev/null
echo "srcipt=[${SCRIPT_PATH}]"
echo "pwd   =[`pwd`]"

Conocido issuse

Guión debe estar en el disco en algún lado, deja que sea a través de una red. Si intenta ejecutar este script desde un PIPE, no funcionará

wget -o /dev/null -O - http://host.domain/dir/script.sh |bash

Técnicamente hablando, no está definido.
En términos prácticos, no hay una manera sensata de detectar esto. (el proceso conjunto no puede acceder a env de los padres)


34
2017-09-09 19:01



¿Qué hay de usar:

SCRIPT_PATH=$(dirname `which $0`)

which imprime para mostrar la ruta completa del ejecutable que se habría ejecutado cuando se ingresó el argumento pasado en el indicador del shell (que es lo que contiene $ 0)

dirname quita el sufijo que no es de directorio del nombre del archivo

De ahí que termines con la ruta completa al script, sin importar si la ruta fue especificada o no.


28
2018-05-09 17:06



Como realpath no está instalado por defecto en mi sistema Linux, lo siguiente funciona para mí:

SCRIPT="$(readlink --canonicalize-existing "$0")"
SCRIPTPATH="$(dirname "$SCRIPT")"

$SCRIPT contendrá la ruta real del archivo al script y $SCRIPTPATH la ruta real del directorio que contiene el script

Antes de usar esto, lee los comentarios de esta respuesta.


22
2018-03-24 16:11



Respondiendo a esta pregunta muy tarde, pero uso:

SCRIPT=$( readlink -m $( type -p $0 ))      # Full path to script
BASE_DIR=`dirname ${SCRIPT}`                # Directory script is run in
NAME=`basename ${SCRIPT}`                   # Actual name of script even if linked

11
2017-10-02 16:28



Hemos colocado nuestro propio producto realpath-lib en GitHub para uso comunitario libre y no comprometido.

Plug desvergonzado, pero con esta biblioteca de Bash puedes:

get_realpath <absolute|relative|symlink|local file>

Esta función es el núcleo de la biblioteca:

function get_realpath() {

if [[ -f "$1" ]]
then 
    # file *must* exist
    if cd "$(echo "${1%/*}")" &>/dev/null
    then 
        # file *may* not be local
        # exception is ./file.ext
        # try 'cd .; cd -;' *works!*
        local tmppwd="$PWD"
        cd - &>/dev/null
    else 
        # file *must* be local
        local tmppwd="$PWD"
    fi
else 
    # file *cannot* exist
    return 1 # failure
fi

# reassemble realpath
echo "$tmppwd"/"${1##*/}"
return 0 # success

}

No requiere dependencias externas, solo Bash 4+. También contiene funciones para get_dirname, get_filename, get_stemname y validate_path  validate_realpath. Es gratis, limpio, simple y está bien documentado, por lo que también se puede utilizar con fines de aprendizaje y, sin duda, se puede mejorar. Pruébalo en todas las plataformas.

Actualización: Después de algunas revisiones y pruebas, hemos reemplazado la función anterior con algo que logra el mismo resultado (sin usar dirname, solo Bash puro) pero con una mejor eficiencia:

function get_realpath() {

    [[ ! -f "$1" ]] && return 1 # failure : file does not exist.
    [[ -n "$no_symlinks" ]] && local pwdp='pwd -P' || local pwdp='pwd' # do symlinks.
    echo "$( cd "$( echo "${1%/*}" )" 2>/dev/null; $pwdp )"/"${1##*/}" # echo result.
    return 0 # success

}

Esto también incluye una configuración de entorno no_symlinks que proporciona la capacidad de resolver enlaces simbólicos al sistema físico. Por defecto, mantiene los enlaces simbólicos intactos.


7
2017-11-28 12:01



Puede intentar definir la siguiente variable:

CWD="$(cd -P -- "$(dirname -- "$0")" && pwd -P)"

o puede probar la siguiente función en bash:

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

Esta función toma 1 argumento. Si el argumento ya tiene una ruta absoluta, imprímalo tal como está, de lo contrario imprima $PWD argumento variable + nombre de archivo (sin ./ prefijo).

Relacionado:


5
2017-11-11 18:48



sh manera compatible:

SCRIPT_HOME=`dirname $0 | while read a; do cd $a && pwd && break; done`

5
2017-12-03 13:59