Obtener el directorio fuente de un script Bash desde dentro


Use dirname "$0" :

echo "The script you are running has basename `basename "$0"`, dirname `dirname "$0"`"
echo "The present working directory is `pwd`"

El uso de pwd sí solo no funcionará si no está ejecutando el script desde el directorio en el que está contenido.

[matt@server1 ~]$ pwd
[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

¿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 iniciador para otra aplicación. Quiero cambiar el directorio de trabajo al que está ubicado el script Bash, así puedo operar los archivos en ese directorio, así:

$ ./application

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


    # otherwise just get the directory name of the script path
    sourceDir=$(dirname $scriptPath)


Entonces ... creo que tengo este. Tarde en la fiesta, pero creo que algunos apreciarán que esté aquí si encuentran este hilo. Los comentarios deberían 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

## 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

## 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"                           ;;
 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 "$@"


# need this for relative symlinks
while [ -h "$PRG" ] ; do
   PRG=`readlink "$PRG"`

scriptdir=`dirname "$PRG"`

Try the following cross-compatible solution:

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

as realpath or readlink commands are not always available (depending on the operating system) and ${BASH_SOURCE[0]} is available only in bash shell.

Alternatively you can try the following function in bash:

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

This function takes 1 argument. If argument has already absolute path, print it as it is, otherwise print $PWD variable + filename argument (without ./ prefix).


pwd se puede usar 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 darle el directorio de la secuencia de comandos actual).

Sin embargo, dirname proporciona precisamente la parte de directorio del nombre de archivo, que probablemente sea relativa al directorio de trabajo actual. Si su script necesita cambiar el directorio por alguna razón, entonces el resultado de dirname sentido.

Sugiero lo siguiente:


reldir=`dirname $0`
cd $reldir

echo "Directory is $directory"

De esta manera, obtienes un directorio absoluto, en lugar de relativo.

Como la secuencia de comandos se ejecutará en una instancia de bash separada, no es necesario restaurar el directorio de trabajo posteriormente, pero si desea volver a cambiar en la secuencia de comandos 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 es más útil en general.

The best compact solution in my view would be:

"$( cd "$( echo "${BASH_SOURCE[0]%/*}" )"; pwd )"

There is no reliance on anything other than Bash. The use of dirname , readlink and basename will eventually lead to compatibility issues, so they are best avoided if at all possible.

Para sistemas que tienen GNU coreutils readlink (por ejemplo, linux):

$(readlink -f $(dirname "$0"))

No es necesario usar BASH_SOURCE cuando $0 contiene el nombre de archivo del script.

Aquí está la manera simple y correcta:

actual_path=$(readlink -f "${BASH_SOURCE[0]}")
script_dir=$(dirname "$actual_path")


  • ${BASH_SOURCE[0]} - la ruta completa al script. El valor de esto será correcto incluso cuando el script se esté obteniendo, p. Ej. source <(echo 'echo $0') imprime bash , mientras que si lo reemplaza con ${BASH_SOURCE[0]} imprimirá la ruta completa del script. (Por supuesto, esto supone que estás bien tomando una dependencia de Bash).

  • readlink -f - Recursivamente resuelve 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 GNU coreutils y suplantar esto con greadlink -f .

  • Y, por supuesto, dirname obtiene el directorio padre de la ruta.

Puede usar $ BASH_SOURCE


scriptdir=`dirname "$BASH_SOURCE"`

Tenga en cuenta que necesita usar #! / Bin / bash y no #! / Bin / sh ya que es una extensión de bash

SCRIPT_DIR=$( cd ${0%/*} && pwd -P )

pushd . > /dev/null
if ([ -h "${SCRIPT_PATH}" ]); then
  while([ -h "${SCRIPT_PATH}" ]); do cd `dirname "$SCRIPT_PATH"`; 
  SCRIPT_PATH=`readlink "${SCRIPT_PATH}"`; done
cd `dirname ${SCRIPT_PATH}` > /dev/null
popd  > /dev/null

Funciona para todas las versiones, incluyendo

  • cuando se llama a través del enlace suave de profundidad multple,
  • cuando el archivo
  • cuando script llamado por comando " source " aka . (punto) operador.
  • cuando arg $ 0 se modifica de la persona que llama.
  • "./guión"
  • "/ full / path / to / script"
  • "/some/path/../../another/path/script"
  • "./some/folder/script"

Alternativamente, si el script bash en sí es un enlace simbólico relativo , desea seguirlo y devolver la ruta completa del script vinculado al:

pushd . > /dev/null
if ([ -h "${SCRIPT_PATH}" ]) then
  while([ -h "${SCRIPT_PATH}" ]) do cd `dirname "$SCRIPT_PATH"`; SCRIPT_PATH=`readlink "${SCRIPT_PATH}"`; done
cd `dirname ${SCRIPT_PATH}` > /dev/null
popd  > /dev/null

SCRIPT_PATH se da en la ruta completa, sin importar cómo se llame.
Solo asegúrate de ubicar esto al inicio del script.

Este comentario y código Copyleft, licencia seleccionable bajo GPL2.0 o posterior o CC-SA 3.0 (CreativeCommons Share Alike) o posterior. (c) 2008. Todos los derechos reservados. Sin garantía de ningún tipo. Usted ha sido advertido.

Esto es específico de Linux, pero podrías usar:

SELF=$(readlink /proc/$$/fd/255)

Intenta usar:

real=$(realpath $(dirname $0))

$(dirname $(readlink -f $BASH_SOURCE))