paso - Obtener el directorio de origen de un script Bash desde dentro
ejecutar script bash linux (30)
¿Cómo obtengo la ruta del directorio en el que se encuentra un script de Bash , dentro de ese script?
Por ejemplo, digamos que quiero usar un script Bash como un lanzador para otra aplicación. Quiero cambiar el directorio de trabajo al directorio donde se encuentra el script Bash, para poder operar en los archivos de ese directorio, de esta manera
$ ./application
Aquí está la manera simple y correcta:
actual_path=$(readlink -f "${BASH_SOURCE[0]}")
script_dir=$(dirname "$actual_path")
Explicación:
${BASH_SOURCE[0]}
- la ruta completa al script. El valor de esto será correcto incluso cuando se obtenga el script, por ejemplo,source <(echo ''echo $0'')
imprime bash , al reemplazarlo con${BASH_SOURCE[0]}
imprimirá la ruta completa del script. (Por supuesto, esto supone que está bien si depende de Bash).readlink -f
- Resuelve recursivamente cualquier enlace simbólico en la ruta especificada. Esta es una extensión de GNU, y no está disponible en (por ejemplo) sistemas BSD. Si está ejecutando una Mac, puede usar Homebrew para instalar GNUcoreutils
y suplantar esto congreadlink -f
.Y, por supuesto,
dirname
obtiene el directorio padre de la ruta.
Aquí hay una sola línea compatible con POSIX:
SCRIPT_PATH=`dirname "$0"`; SCRIPT_PATH=`eval "cd /"$SCRIPT_PATH/" && pwd"`
# test
echo $SCRIPT_PATH
El comando dirname es el más básico, simplemente analizando la ruta hasta el nombre del archivo fuera de la variable $ 0 (nombre del script):
dirname "$0"
Pero, como señaló Matt B , el camino devuelto es diferente dependiendo de cómo se llame el script. pwd no hace el trabajo porque solo te dice qué es el directorio actual, no qué directorio reside el script. Además, si se ejecuta un enlace simbólico a un script, obtendrás una ruta (probablemente relativa) a donde reside el enlace, no el script real.
Algunos otros han mencionado el comando readlink , pero en su forma más simple, puedes usar:
dirname "$(readlink -f "$0")"
readlink resolverá la ruta del script a una ruta absoluta desde la raíz del sistema de archivos. Por lo tanto, cualquier ruta que contenga puntos simples o dobles, tildes y / o enlaces simbólicos se resolverá en una ruta completa.
Aquí hay un script que demuestra cada uno de estos, whatdir.sh:
#!/bin/bash
echo "pwd: `pwd`"
echo "/$0: $0"
echo "basename: `basename $0`"
echo "dirname: `dirname $0`"
echo "dirname/readlink: $(dirname $(readlink -f $0))"
Ejecutando este script en mi directorio de casa, usando una ruta relativa:
>>>$ ./whatdir.sh
pwd: /Users/phatblat
$0: ./whatdir.sh
basename: whatdir.sh
dirname: .
dirname/readlink: /Users/phatblat
De nuevo, pero usando la ruta completa al script:
>>>$ /Users/phatblat/whatdir.sh
pwd: /Users/phatblat
$0: /Users/phatblat/whatdir.sh
basename: whatdir.sh
dirname: /Users/phatblat
dirname/readlink: /Users/phatblat
Ahora cambiando directorios:
>>>$ cd /tmp
>>>$ ~/whatdir.sh
pwd: /tmp
$0: /Users/phatblat/whatdir.sh
basename: whatdir.sh
dirname: /Users/phatblat
dirname/readlink: /Users/phatblat
Y finalmente usando un enlace simbólico para ejecutar el script:
>>>$ ln -s ~/whatdir.sh whatdirlink.sh
>>>$ ./whatdirlink.sh
pwd: /tmp
$0: ./whatdirlink.sh
basename: whatdirlink.sh
dirname: .
dirname/readlink: /Users/phatblat
Entonces ... creo que tengo este. Tarde en la fiesta, pero creo que algunos apreciarán que estén aquí si se encuentran con este hilo. Los comentarios deben explicar.
#!/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
Esto debería hacerlo:
DIR=$(dirname "$(readlink -f "$0")")
Trabaja con enlaces simbólicos y espacios en ruta. Vea las páginas del manual para dirname y readlink .
Editar:
Desde la pista de comentarios parece no funcionar con Mac OS. No tengo idea de por qué es eso. ¿Alguna sugerencia?
Esto es específico de Linux, pero podrías usar:
SELF=$(readlink /proc/$$/fd/255)
Esto obtiene el directorio de trabajo actual en Mac OS X 10.6.6:
DIR=$(cd "$(dirname "$0")"; pwd)
He comparado muchas de las respuestas dadas y he encontrado algunas soluciones más compactas. Estos parecen manejar todos los casos de borde loco que surgen de su combinación favorita de:
- Rutas absolutas o rutas relativas
- Enlaces suaves de archivos y directorios
- Invocación como
script
,script
bash script
,bash -c script
,source script
o. script
. script
- Espacios, tabulaciones, nuevas líneas, unicode, etc. en directorios y / o nombre de archivo
- Nombres de archivo que comienzan con un guión
Si está ejecutando desde Linux, parece que usar el controlador proc
es la mejor solución para localizar la fuente completamente resuelta del script actualmente en ejecución (en una sesión interactiva, el enlace apunta al respectivo /dev/pts/X
):
resolved="$(readlink /proc/$$/fd/255 && echo X)" && resolved="${resolved%$''/nX''}"
Esto tiene un poco de fealdad, pero la solución es compacta y fácil de entender. No estamos usando primitivas bash solamente, pero estoy de acuerdo con eso porque readlink
simplifica la tarea considerablemente. El echo X
agrega una X
al final de la cadena variable para que no se coman los espacios en blanco al final del nombre de archivo, y la sustitución del parámetro ${VAR%X}
al final de la línea elimina la X
Debido a que readlink
agrega una nueva línea propia (que normalmente se comería en la sustitución de comandos si no fuera por nuestro truco anterior), también tenemos que deshacernos de eso. Esto se logra más fácilmente usando el esquema de cotización $''''
, que nos permite usar secuencias de escape como /n
para representar nuevas líneas (así es como puede hacer directorios y archivos con nombres falsos).
Lo anterior debe cubrir sus necesidades para ubicar el script actualmente en ejecución en Linux, pero si no tiene el sistema de archivos proc
a su disposición, o si está tratando de localizar la ruta completamente resuelta de algún otro archivo, entonces tal vez esté Encontraré el código de abajo útil. Es sólo una ligera modificación de la anterior. Si está jugando con directorios / nombres de archivo extraños, verificar la salida con ls
y readlink
es informativo, ya que ls
generará rutas "simplificadas", sustituyendo ?
Para cosas como nuevas líneas.
absolute_path=$(readlink -e -- "${BASH_SOURCE[0]}" && echo x) && absolute_path=${absolute_path%?x}
dir=$(dirname -- "$absolute_path" && echo x) && dir=${dir%?x}
file=$(basename -- "$absolute_path" && echo x) && file=${file%?x}
ls -l -- "$dir/$file"
printf ''$absolute_path: "%s"/n'' "$absolute_path"
No creo que esto sea tan fácil como otros lo han hecho. pwd no funciona, ya que el directorio actual no es necesariamente el directorio con el script. $ 0 no siempre tiene la información tampoco. Considere las siguientes tres formas de invocar un script.
./script
/usr/bin/script
script
En la primera y tercera forma, $ 0 no tiene la información de ruta completa. En el segundo y tercero, pwd no funcionan. La única forma de obtener el directorio de la tercera forma sería correr a través de la ruta y encontrar el archivo con la coincidencia correcta. Básicamente, el código tendría que rehacer lo que hace el sistema operativo.
Una forma de hacer lo que está pidiendo es simplemente codificar los datos en el directorio / usr / share y hacer referencia a ellos por la ruta completa. Los datos no deberían estar en el directorio / usr / bin de todos modos, así que probablemente sea lo que hay que hacer.
Para sistemas que tienen enlace de lectura coreutils GNU (por ejemplo, linux):
$(readlink -f "$(dirname "$0")")
No es necesario usar BASH_SOURCE
cuando $0
contiene el nombre de archivo del script.
Probé cada uno de estos y ninguno de ellos funcionó. Uno estaba muy cerca pero tenía un pequeño insecto que lo rompió gravemente; se olvidaron de envolver el camino entre comillas.
Además, muchas personas asumen que está ejecutando el script desde un shell, así que olvídese de que cuando abre un nuevo script, se establece de forma predeterminada en su hogar.
Prueba este directorio por tamaño:
/ var / Nadie / Pensamiento / Acerca de los espacios que están / En un directorio / Nombre / Y aquí está su archivo.text
Esto lo hace bien sin importar cómo o dónde lo ejecute.
#!/bin/bash
echo "pwd: `pwd`"
echo "/$0: $0"
echo "basename: `basename "$0"`"
echo "dirname: `dirname "$0"`"
Así que para que sea realmente útil, aquí se explica cómo cambiar al directorio del script en ejecución:
cd "`dirname "$0"`"
Espero que ayude
Puedes usar $ BASH_SOURCE
#!/bin/bash
scriptdir=`dirname "$BASH_SOURCE"`
Tenga en cuenta que necesita usar #! / Bin / bash y no #! / Bin / sh ya que es una extensión de bash
Trate de usar:
real=$(realpath $(dirname $0))
Una ligera revisión de la solución e-satis y 3bcdnlklvc04a señalada en su respuesta.
SCRIPT_DIR=''''
pushd "$(dirname "$(readlink -f "$BASH_SOURCE")")" > /dev/null && {
SCRIPT_DIR="$PWD"
popd > /dev/null
}
Esto debería seguir funcionando en todos los casos enumerados.
EDIT: prevenir popd después de pushd fallido, gracias a konsolebox
Use dirname "$0"
:
#!/bin/bash
echo "The script you are running has basename `basename "$0"`, dirname `dirname "$0"`"
echo "The present working directory is `pwd`"
el uso de pwd
solo no funcionará si no está ejecutando el script desde el directorio en el que está contenido.
[matt@server1 ~]$ pwd
/home/matt
[matt@server1 ~]$ ./test2.sh
The script you are running has basename test2.sh, dirname .
The present working directory is /home/matt
[matt@server1 ~]$ cd /tmp
[matt@server1 tmp]$ ~/test2.sh
The script you are running has basename test2.sh, dirname /home/matt
The present working directory is /tmp
Vale la pena mencionar $_ como alternativa a $ 0. Si está ejecutando un script desde bash, la respuesta aceptada se puede acortar a:
DIR="$( dirname "$_" )"
Tenga en cuenta que esta debe ser la primera declaración en su script.
Yo usaría algo como esto:
# retrieve the full pathname of the called script
scriptPath=$(which $0)
# check whether the path is a link or not
if [ -L $scriptPath ]; then
# it is a link then retrieve the target path and get the directory name
sourceDir=$(dirname $(readlink -f $scriptPath))
else
# otherwise just get the directory name of the script path
sourceDir=$(dirname $scriptPath)
fi
pwd
puede usarse para encontrar el directorio de trabajo actual, y dirname
para encontrar el directorio de un archivo en particular (el comando que se ejecutó es $0
, por lo que dirname $0
debería proporcionarle el directorio del script actual).
Sin embargo, dirname
proporciona precisamente la parte del directorio del nombre de archivo, que es muy probable que sea relativa al directorio de trabajo actual. Si su script necesita cambiar el directorio por alguna razón, entonces la salida de dirname
significado.
Sugiero lo siguiente:
#!/bin/bash
reldir=`dirname $0`
cd $reldir
directory=`pwd`
echo "Directory is $directory"
De esta manera, obtienes un directorio absoluto, en lugar de relativo.
Dado que el script se ejecutará en una instancia de bash independiente, no es necesario restaurar el directorio de trabajo posteriormente, pero si desea volver a cambiarlo en su script por alguna razón, puede asignar fácilmente el valor de pwd
a una variable antes Cambia de directorio, para uso futuro.
Aunque solo
cd `dirname $0`
resuelve el escenario específico en la pregunta, me parece que tener el camino absoluto a más útil en general.
Esta es la única manera que he encontrado para decir de manera confiable:
SCRIPT_DIR=$(dirname $(cd "$(dirname "$BASH_SOURCE")"; pwd))
Esto funciona en bash-3.2:
path="$( dirname "$( which "$0" )" )"
Aquí hay un ejemplo de su uso:
Digamos que tienes un directorio ~ / bin , que está en tu $ PATH . Tienes el script A dentro de este directorio. Es fuente s guión ~ / bin / lib / B . Usted sabe dónde está el script incluido es relativo a la original (el subdirectorio lib ), pero no en los que es relativo al directorio actual del usuario.
Esto se resuelve con lo siguiente (dentro de A ):
source "$( dirname "$( which "$0" )" )/lib/B"
No importa dónde esté el usuario o cómo llame al script, esto siempre funcionará.
La mejor solución compacta en mi opinión sería:
"$( cd "$( echo "${BASH_SOURCE[0]%/*}" )"; pwd )"
No hay confianza en otra cosa que Bash. El uso de dirname
, readlink
y basename
eventualmente conducirá a problemas de compatibilidad, por lo que es mejor evitarlos si es posible.
Ninguno de estos funcionó para un script de bash lanzado por Finder en OS X; terminé usando:
SCRIPT_LOC="`ps -p $$ | sed /PID/d | sed s:.*/Network/:/Network/: |
sed s:.*/Volumes/:/Volumes/:`"
No es bonito, pero hace el trabajo.
Pruebe la siguiente solución compatible con versiones cruzadas:
CWD="$(cd -P -- "$(dirname -- "$0")" && pwd -P)"
as realpath
o los readlink
comandos no siempre están disponibles (dependiendo del sistema operativo) y ${BASH_SOURCE[0]}
está disponible solo en shell bash.
Alternativamente, puedes 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 el $PWD
argumento variable + nombre de archivo (sin ./
prefijo).
Relacionado:
Hmmm, si en la ruta, basename y dirname simplemente no lo vamos a cortar y recorrer la ruta es difícil (¡y si el padre no exportó PATH!) Sin embargo, el shell debe tener un identificador abierto para su script, y en bash el identificador es # 255.
SELF=`readlink /proc/$$/fd/255`
funciona para mi.
#!/bin/sh
PRG="$0"
# need this for relative symlinks
while [ -h "$PRG" ] ; do
PRG=`readlink "$PRG"`
done
scriptdir=`dirname "$PRG"`
$(dirname "$(readlink -f "$BASH_SOURCE")")
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )"
es un útil de una sola línea que le dará el nombre completo del directorio de la secuencia de comandos sin importar desde dónde se llame.
Funcionará siempre que el último componente de la ruta utilizada para encontrar el script no sea un enlace simbólico (los enlaces de directorio están bien). Si también desea resolver cualquier enlace al script en sí, necesita una solución multilínea:
SOURCE="${BASH_SOURCE[0]}"
while [ -h "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink
DIR="$( cd -P "$( dirname "$SOURCE" )" >/dev/null && pwd )"
SOURCE="$(readlink "$SOURCE")"
[[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE" # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located
done
DIR="$( cd -P "$( dirname "$SOURCE" )" >/dev/null && pwd )"
Este último funcionará con cualquier combinación de alias, source
, bash -c
, enlaces simbólicos, etc.
Cuidado: si cd
en un directorio diferente antes de ejecutar este fragmento de código, ¡el resultado puede ser incorrecto! Además, ten cuidado con $CDPATH
errores de $CDPATH
.
Para entender cómo funciona, intente ejecutar este formulario más detallado:
#!/bin/bash
SOURCE="${BASH_SOURCE[0]}"
while [ -h "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink
TARGET="$(readlink "$SOURCE")"
if [[ $TARGET == /* ]]; then
echo "SOURCE ''$SOURCE'' is an absolute symlink to ''$TARGET''"
SOURCE="$TARGET"
else
DIR="$( dirname "$SOURCE" )"
echo "SOURCE ''$SOURCE'' is a relative symlink to ''$TARGET'' (relative to ''$DIR'')"
SOURCE="$DIR/$TARGET" # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located
fi
done
echo "SOURCE is ''$SOURCE''"
RDIR="$( dirname "$SOURCE" )"
DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )"
if [ "$DIR" != "$RDIR" ]; then
echo "DIR ''$RDIR'' resolves to ''$DIR''"
fi
echo "DIR is ''$DIR''"
Y se imprimirá algo como:
SOURCE ''./scriptdir.sh'' is a relative symlink to ''sym2/scriptdir.sh'' (relative to ''.'')
SOURCE is ''./sym2/scriptdir.sh''
DIR ''./sym2'' resolves to ''/home/ubuntu/dotfiles/fo fo/real/real1/real2''
DIR is ''/home/ubuntu/dotfiles/fo fo/real/real1/real2''
SCRIPT_DIR=$( cd ${0%/*} && pwd -P )
pushd . > /dev/null
SCRIPT_PATH="${BASH_SOURCE[0]}"
if ([ -h "${SCRIPT_PATH}" ]); then
while([ -h "${SCRIPT_PATH}" ]); do cd `dirname "$SCRIPT_PATH"`;
SCRIPT_PATH=`readlink "${SCRIPT_PATH}"`; done
fi
cd `dirname ${SCRIPT_PATH}` > /dev/null
SCRIPT_PATH=`pwd`;
popd > /dev/null
Funciona para todas las versiones, incluyendo
- cuando se llama a través de enlace flexible de profundidad múltiple,
- cuando el archivo
- cuando el script llama por el comando "
source
" aka.
operador (punto). - cuando arg
$0
se modifica de la persona que llama. -
"./script"
-
"/full/path/to/script"
-
"/some/path/../../another/path/script"
-
"./some/folder/script"
Alternativamente, si el script bash en sí mismo es un enlace simbólico relativo , desea seguirlo y devolver la ruta completa del script enlazado a:
pushd . > /dev/null
SCRIPT_PATH="${BASH_SOURCE[0]}";
if ([ -h "${SCRIPT_PATH}" ]) then
while([ -h "${SCRIPT_PATH}" ]) do cd `dirname "$SCRIPT_PATH"`; SCRIPT_PATH=`readlink "${SCRIPT_PATH}"`; done
fi
cd `dirname ${SCRIPT_PATH}` > /dev/null
SCRIPT_PATH=`pwd`;
popd > /dev/null
SCRIPT_PATH
se proporciona en la ruta completa, sin importar cómo se llame.
Solo asegúrate de ubicarlo al inicio del script.
Este comentario y código se pueden copiar, licencia seleccionable bajo la GPL2.0 o posterior o CC-SA 3.0 (CreativeCommons Share Alike) o posterior. (c) 2008. Todos los derechos reservados. No hay garantía de ningún tipo. Usted ha sido advertido.
http://www.gnu.org/licenses/gpl-2.0.txt
http://creativecommons.org/licenses/by-sa/3.0/
18eedfe1c99df68dc94d4a94712a71aaa8e1e9e36cacf421b9463dd2bbaa02906d0d6656