Básicamente, necesito ejecutar el script con rutas relacionadas con la ubicación del archivo de script de shell, ¿cómo puedo cambiar el directorio actual al mismo directorio donde reside el archivo de script?
Básicamente, necesito ejecutar el script con rutas relacionadas con la ubicación del archivo de script de shell, ¿cómo puedo cambiar el directorio actual al mismo directorio donde reside el archivo de script?
Respuestas:
En Bash, deberías obtener lo que necesitas así:
#!/usr/bin/env bash
BASEDIR=$(dirname "$0")
echo "$BASEDIR"
readlink
(consulte la respuesta de todos a continuación)
$BASH_SOURCE
en lugar de $0
, porque $0
no siempre contiene la ruta del script que se invoca, como cuando se 'obtiene' un script.
$BASH_SOURCE
es específico de Bash, la pregunta es sobre el script de shell en general.
CUR_PATH=$(pwd)
o pwd
devuelve el directorio actual (que no tiene que ser el directorio principal de los scripts)!
$BASH_SOURCE
, y devuelve lo que necesitaba. Mi secuencia de comandos se llama desde otra secuencia de comandos y $0
regresa .
mientras que $BASH_SOURCE
devuelve el subdirectorio correcto (en mi caso scripts
).
La publicación original contiene la solución (ignore las respuestas, no agregan nada útil). El interesante trabajo lo realiza el mencionado comando unix readlink
con opción -f
. Funciona cuando el guión es invocado por una ruta absoluta o relativa.
Para bash, sh, ksh:
#!/bin/bash
# Absolute path to this script, e.g. /home/user/bin/foo.sh
SCRIPT=$(readlink -f "$0")
# Absolute path this script is in, thus /home/user/bin
SCRIPTPATH=$(dirname "$SCRIPT")
echo $SCRIPTPATH
Para tcsh, csh:
#!/bin/tcsh
# Absolute path to this script, e.g. /home/user/bin/foo.csh
set SCRIPT=`readlink -f "$0"`
# Absolute path this script is in, thus /home/user/bin
set SCRIPTPATH=`dirname "$SCRIPT"`
echo $SCRIPTPATH
Ver también: https://stackoverflow.com/a/246128/59087
readlink
. Es por eso que recomendé usar pushd / popd (incorporados para bash).
-f
opción de readlink
hacer algo diferente en OS X (Lion) y posiblemente BSD. stackoverflow.com/questions/1055671/…
-f
no es compatible con OS X (a partir de Lion); allí puede soltar la -f
tarea de resolver como máximo un nivel de indirección, por ejemplo pushd "$(dirname "$(readlink "$BASH_SOURCE" || echo "$BASH_SOURCE")")"
, o puede rodar su propio script recursivo de seguimiento de enlaces simbólicos como se demuestra en la publicación vinculada.
sh /some/other/directory/script.sh)
, en este caso .
sería tu pwd, no/some/other/directory
Un comentario anterior sobre una respuesta lo dijo, pero es fácil pasarlo por alto entre todas las otras respuestas.
Al usar bash:
echo this file: "$BASH_SOURCE"
echo this dir: "$(dirname "$BASH_SOURCE")"
dirname "$BASH_SOURCE"
lugar, debe usar para manejar espacios en $ BASH_SOURCE.
"$(dirname "${BASH_SOURCE[0]}")"
Asumiendo que estás usando bash
#!/bin/bash
current_dir=$(pwd)
script_dir=$(dirname $0)
echo $current_dir
echo $script_dir
Este script debe imprimir el directorio en el que se encuentra, y luego el directorio en el que se encuentra el script. Por ejemplo, al llamarlo /
con el script en /home/mez/
, se genera
/
/home/mez
Recuerde, cuando asigne variables desde la salida de un comando, envuelva el comando $(
y )
- o no obtendrá la salida deseada.
Si estás usando bash ...
#!/bin/bash
pushd $(dirname "${0}") > /dev/null
basedir=$(pwd -L)
# Use "pwd -P" for the path without links. man bash for more info.
popd > /dev/null
echo "${basedir}"
pushd
/ popd
con cd $(dirname "${0}")
y cd -
hacer que funcione en otros shells, si tienen un pwd -L
.
Como sugiere el Marko:
BASEDIR=$(dirname $0)
echo $BASEDIR
Esto funciona a menos que ejecute el script desde el mismo directorio donde reside el script, en cuyo caso obtendrá un valor de '.'
Para solucionar ese problema, use:
current_dir=$(pwd)
script_dir=$(dirname $0)
if [ $script_dir = '.' ]
then
script_dir="$current_dir"
fi
Ahora puede usar la variable current_dir a lo largo de su script para referirse al directorio del script. Sin embargo, esto aún puede tener el problema del enlace simbólico.
La mejor respuesta para esta pregunta se respondió aquí:
Obtener el directorio fuente de un script Bash desde dentro
Y es:
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
One-liner que le dará el nombre completo del directorio de la secuencia de comandos sin importar de dónde se llame.
Para entender cómo funciona, puede ejecutar el siguiente script:
#!/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'"
Hagámoslo un POSIX oneliner:
a="/$0"; a=${a%/*}; a=${a#/}; a=${a:-.}; BASEDIR=$(cd "$a"; pwd)
Probado en muchos shells compatibles con Bourne, incluidos los BSD.
Hasta donde sé, soy el autor y lo puse en dominio público. Para obtener más información, consulte: https://www.jasan.tk/posts/2017-05-11-posix_shell_dirname_replacement/
cd: too many arguments
si espacios en la ruta, y vuelve $PWD
. (una solución obvia, pero solo muestra cuántos casos extremos realmente hay)
BASE_DIR="$(cd "$(dirname "$0")"; pwd)";
echo "BASE_DIR => $BASE_DIR"
Si desea obtener el directorio de script real (independientemente de si está invocando el script usando un enlace simbólico o directamente), intente:
BASEDIR=$(dirname $(realpath "$0"))
echo "$BASEDIR"
Esto funciona tanto en Linux como en macOS. No pude ver a nadie aquí mencionar realpath
. No estoy seguro de si hay algún inconveniente en este enfoque.
en macOS, necesita instalar coreutils
para usar realpath
. Por ejemplo: brew install coreutils
.
INTRODUCCIÓN
Esta respuesta corrige la respuesta muy rota pero sorprendentemente mejor votada de este hilo (escrita por TheMarko):
#!/usr/bin/env bash
BASEDIR=$(dirname "$0")
echo "$BASEDIR"
¿POR QUÉ EL USO DE dirname "$ 0" EN SU PROPIO NO FUNCIONA?
dirname $ 0 solo funcionará si el usuario inicia el script de una manera muy específica. Pude encontrar varias situaciones en las que esta respuesta falla y bloquea el script.
En primer lugar, comprendamos cómo funciona esta respuesta. Está obteniendo el directorio de script haciendo
dirname "$0"
$ 0 representa la primera parte del comando que llama al script (es básicamente el comando ingresado sin los argumentos:
/some/path/./script argument1 argument2
$ 0 = "/ some / path /./ script"
dirname básicamente encuentra el último / en una cadena y lo trunca allí. Entonces si lo haces:
dirname /usr/bin/sha256sum
obtendrá: / usr / bin
Este ejemplo funciona bien porque / usr / bin / sha256sum es una ruta formateada correctamente pero
dirname "/some/path/./script"
no funcionaría bien y te daría:
BASENAME="/some/path/." #which would crash your script if you try to use it as a path
Digamos que está en el mismo directorio que su script y lo inicia con este comando
./script
$ 0 en esta situación será ./script y dirname $ 0 dará:
. #or BASEDIR=".", again this will crash your script
Utilizando:
sh script
Sin ingresar la ruta completa también dará un BASEDIR = "."
Usando directorios relativos:
../some/path/./script
Da un nombre de directorio $ 0 de:
../some/path/.
Si está en el directorio / some y llama al script de esta manera (observe la ausencia de / al principio, nuevamente una ruta relativa):
path/./script.sh
Obtendrá este valor para dirname $ 0:
path/.
y ./path/./script (otra forma de la ruta relativa) da:
./path/.
Las únicas dos situaciones en las que trabajará con base $ 0 es si el usuario usa sh o touch para iniciar un script porque ambas generarán $ 0:
$0=/some/path/script
que le dará una ruta que puede usar con dirname.
LA SOLUCIÓN
Debería tener en cuenta y detectar cada una de las situaciones mencionadas anteriormente y aplicar una solución si surge:
#!/bin/bash
#this script will only work in bash, make sure it's installed on your system.
#set to false to not see all the echos
debug=true
if [ "$debug" = true ]; then echo "\$0=$0";fi
#The line below detect script's parent directory. $0 is the part of the launch command that doesn't contain the arguments
BASEDIR=$(dirname "$0") #3 situations will cause dirname $0 to fail: #situation1: user launches script while in script dir ( $0=./script)
#situation2: different dir but ./ is used to launch script (ex. $0=/path_to/./script)
#situation3: different dir but relative path used to launch script
if [ "$debug" = true ]; then echo 'BASEDIR=$(dirname "$0") gives: '"$BASEDIR";fi
if [ "$BASEDIR" = "." ]; then BASEDIR="$(pwd)";fi # fix for situation1
_B2=${BASEDIR:$((${#BASEDIR}-2))}; B_=${BASEDIR::1}; B_2=${BASEDIR::2}; B_3=${BASEDIR::3} # <- bash only
if [ "$_B2" = "/." ]; then BASEDIR=${BASEDIR::$((${#BASEDIR}-1))};fi #fix for situation2 # <- bash only
if [ "$B_" != "/" ]; then #fix for situation3 #<- bash only
if [ "$B_2" = "./" ]; then
#covers ./relative_path/(./)script
if [ "$(pwd)" != "/" ]; then BASEDIR="$(pwd)/${BASEDIR:2}"; else BASEDIR="/${BASEDIR:2}";fi
else
#covers relative_path/(./)script and ../relative_path/(./)script, using ../relative_path fails if current path is a symbolic link
if [ "$(pwd)" != "/" ]; then BASEDIR="$(pwd)/$BASEDIR"; else BASEDIR="/$BASEDIR";fi
fi
fi
if [ "$debug" = true ]; then echo "fixed BASEDIR=$BASEDIR";fi
Esta línea le dice dónde está el script de shell, no importa si lo ejecutó o si lo obtuvo . Además, resuelve los enlaces simbólicos involucrados, si ese es el caso:
dir=$(dirname $(test -L "$BASH_SOURCE" && readlink -f "$BASH_SOURCE" || echo "$BASH_SOURCE"))
Por cierto, supongo que estás usando / bin / bash .
Tantas respuestas, todas plausibles, cada una con ventajas y desventajas y objetivos ligeramente diferentes (que probablemente deberían establecerse para cada una). Aquí hay otra solución que cumple con el objetivo principal de ser claro y trabajar en todos los sistemas, en todos los bash (sin suposiciones sobre las versiones readlink
u pwd
opciones de bash ), y hace razonablemente lo que esperaría que suceda (por ejemplo, resolver enlaces simbólicos es un problema interesante, pero generalmente no es lo que realmente quieres), maneja casos extremos como espacios en las rutas, etc., ignora cualquier error y usa un valor predeterminado sensato si hay algún problema.
Cada componente se almacena en una variable separada que puede usar individualmente:
# script path, filename, directory
PROG_PATH=${BASH_SOURCE[0]} # this script's name
PROG_NAME=${PROG_PATH##*/} # basename of script (strip path)
PROG_DIR="$(cd "$(dirname "${PROG_PATH:-$PWD}")" 2>/dev/null 1>&2 && pwd)"
Inspirado por la respuesta de blueyed
read < <(readlink -f $0 | xargs dirname)
cd $REPLY
Eso debería hacer el truco:
echo `pwd`/`dirname $0`
Puede parecer feo dependiendo de cómo se invocó y el cwd, pero debería llevarlo a donde necesita ir (o puede ajustar la cadena si le importa cómo se ve).
`pwd`/`dirname $0`
pero aún puede fallar en enlaces simbólicos