2008-10-10 11 views
410

¿Cómo puedo determinar el nombre del archivo de script Bash dentro del script?¿Cómo sé el nombre del archivo de script en un script Bash?

Al igual que si mi secuencia de comandos está en el archivo runme.sh, ¿cómo lo haría para que aparezca el mensaje "Está ejecutando runme.sh" sin hardcoding?

+3

Similar [¿Puede un script bash decir en qué directorio está almacenado?] (A http://stackoverflow.com/questions/59895/can-a-bash-script-tell-what-directory-its-stored-in) – Rodrigue

+0

Para el directorio, ver: [Obtener el directorio de origen de un script Bash desde dentro] (https://stackoverflow.com/q/59895/55075). – kenorb

Respuesta

427
me=`basename "$0"` 

Para la lectura a través de un enlace simbólico, que por lo general no es lo que quiere (por lo general, no quiere confundir al usuario de esta manera), intenta:

me="$(basename "$(test -L "$0" && readlink "$0" || echo "$0")")" 

OMI, que se va a producir salida confusa. "Corrí foo.sh, pero está diciendo que estoy ejecutando bar.sh !? Debe ser un error!" Además, uno de los propósitos de tener enlaces simbólicos con nombres diferentes es proporcionar diferentes funcionalidades basadas en el nombre al que se llama (piense gzip y gunzip en algunas plataformas).

+2

Bonito broche de oro con el basename allí. – dmckee

+17

$ 0 le da el nombre a través del cual se invocó el script, no la ruta real del archivo de script real. –

+3

Funciona a menos que lo llamen mediante un enlace simbólico. Pero, incluso entonces, generalmente es lo que quieres de todos modos, IME. – Tanktalus

0

echo "Se está ejecutando $ 0"

+0

No es script bash, es ruta completa. –

5

Puede usar $ 0 a determinar el nombre de secuencia de comandos (con ruta completa) - para obtener el nombre de secuencia de comandos sólo se puede recortar esa variable con

basename $0 
56

Si el nombre del script tiene espacios, una forma más sólida es usar "$0" o "$(basename "$0")" - o en MacOS: "$(basename \"$0\")". Esto evita que el nombre sea mutilado o interpretado de ninguna manera. En general, es una buena práctica citar siempre nombres variables en el intérprete de comandos.

+2

(¡Mejor respuesta que la que obtiene todos los votos!) – slashmais

+9

(De acuerdo, pero ¿por qué estamos hablando entre paréntesis?) – ephemient

+2

(IME, es raro que sea importante, y por eso mi respuesta no se molestó con las comillas). – Tanktalus

0

$0 no contesta la pregunta (como yo lo entiendo). Una demostración:

 
$ cat script.sh 
#! /bin/sh 
echo `basename $0` 
$ ./script.sh 
script.sh 
$ ln script.sh linktoscript 
$ ./linktoscript 
linktoscript 

¿Cómo se llega ./linktoscript para imprimir script.sh?

[EDITAR] Por @ephemient en los comentarios anteriores, aunque el vínculo simbólico puede parecer artificial, es posible jugar con $0 de modo que no represente un recurso del sistema de archivos. El OP es un poco ambiguo sobre lo que quería.

+0

agregó una respuesta a esto a mi respuesta anterior, pero no estoy de acuerdo con que esto sea realmente lo que quiero (o que lo desee, salvo algunas circunstancias atenuantes que actualmente no puedo comprender) – Tanktalus

+2

Creo que la impresión de linktoscript en lugar de script.sh es una característica, no un error. Varios comandos Unix usan su nombre para alterar su comportamiento. Un ejemplo es vi/ex/view. – mouviciel

+0

@Tanktalus: hay casos en los que desea que cambie el comportamiento en función de la ubicación real del archivo; en un proyecto anterior tenía una secuencia de comandos de "rama activa", que enlazaba simbólicamente varias herramientas de la rama I estaba trabajando en; Esas herramientas podrían entonces descubrir en qué directorio se habían verificado para ejecutar contra los archivos correctos. –

19

Para responder Chris Conway, en Linux (al menos) que podría hacer esto:

echo $(basename $(readlink -nf $0)) 

impresiones readlink el valor de un enlace simbólico. Si no es un enlace simbólico, imprime el nombre del archivo. -n le dice que no imprima una nueva línea. -f le dice que siga el enlace por completo (si un enlace simbólico fuera un enlace a otro, también lo resolvería).

+1

-n es inofensivo pero no necesario porque la estructura $ (...) lo recortará. – zhaorufei

+1

+1 para agregar el bit de readlink y ser muy conciso (Y para explicar lo que 'readlink' hace y las opciones' -nf'. Es un gran problema mío cuando las personas no explican esas dos cosas ... porque entonces tengo que buscar esas cosas desperdiciando minutos previos míos.) –

+0

Solo responde trabajar en FreeBSD (v10.2). –

19

Si lo desea sin la ruta, entonces sería utilizar ${0##*/}

+3

+1 Así que también leíste la página del hombre bash, ¿eh? – guns

+1

¡Mejor que lo creas! Hago este tipo de cosas todo el tiempo. –

+0

¿Y qué pasa si lo quiero sin una extensión de archivo opcional? –

11

Estas respuestas son correctas para los casos Afirman pero hay todavía un problema si se ejecuta el script desde otro script utilizando la 'fuente' (para que se ejecute en el mismo shell). En este caso, obtienes los $ 0 del script de llamada. Y en este caso, no creo que sea posible obtener el nombre del script en sí.

Este es un caso de borde y no debe tomarse TAMBIÉN en serio. Si ejecuta el script desde otro script directamente (sin 'fuente'), usar $ 0 funcionará.

+2

Tiene un muy buen punto. No es un caso extremo IMO. Sin embargo, hay una solución: ver [la respuesta de Dimitre Radoulov arriba] (http://stackoverflow.com/questions/192319/in-the-bash-script-how-do-i-know-the-script-file-name/ 639500 # 639500) –

148

Con fiesta> = 3 las siguientes obras:

$ ./s 
0 is: ./s 
BASH_SOURCE is: ./s 
$ . ./s 
0 is: bash 
BASH_SOURCE is: ./s 

$ cat s 
#!/bin/bash 

printf '$0 is: %s\n$BASH_SOURCE is: %s\n' "$0" "$BASH_SOURCE" 
+15

¡Genial! Esa es la respuesta que funciona para ./scrip.sh y source ./script.sh – zhaorufei

+3

Esto es lo que quiero, y es fácil usar "dirname $ BASE_SOURCE'" para obtener el directorio que los scripts localizan. –

+1

Casi aprendí esa diferencia de la peor manera cuando escribo un script de eliminación automática. Afortunadamente, 'rm' recibió el alias de 'rm -i' :) – kervin

194
 
# ------------- SCRIPT ------------- # 

#!/bin/bash 

echo 
echo "# arguments called with ----> ${@}  " 
echo "# \$1 ----------------------> $1  " 
echo "# \$2 ----------------------> $2  " 
echo "# path to me ---------------> ${0}  " 
echo "# parent path --------------> ${0%/*} " 
echo "# my name ------------------> ${0##*/} " 
echo 
exit 
 
# ------------- CALLED ------------- # 

# Notice on the next line, the first argument is called within double, 
# and single quotes, since it contains two words 

$ /misc/shell_scripts/check_root/show_parms.sh "'hello there'" "'william'" 

# ------------- RESULTS ------------- # 

# arguments called with ---> 'hello there' 'william' 
# $1 ----------------------> 'hello there' 
# $2 ----------------------> 'william' 
# path to me --------------> /misc/shell_scripts/check_root/show_parms.sh 
# parent path -------------> /misc/shell_scripts/check_root 
# my name -----------------> show_parms.sh 

# ------------- END ------------- # 
+6

¿Cómo funcionan las sintaxis '$ {0%/*}' y '$ {0 ## * /}'? – Nicole

+8

@NickC see [** Substring Removal **] (http://tldp.org/LDP/abs/html/string-manipulation.html) – cychoi

+0

¿Cómo obtengo '' show_params', es decir, el nombre sin ninguna extensión opcional ? –

8

Re: de Tanktalus (aceptado) para que responda más arriba, una forma un poco más limpia es utilizar:

me=$(readlink --canonicalize --no-newline $0) 

Si su script ha sido obtenido de otra secuencia de comandos bash, puede usar:

me=$(readlink --canonicalize --no-newline $BASH_SOURCE) 

Acepto que sería confuso desreferenciar enlaces simbólicos si su objetivo es proporcionar comentarios al usuario, pero hay ocasiones en las que necesita obtener el nombre canónico para un script u otro archivo, y esta es la mejor manera , imo.

+1

Buenos detalles, gracias. – Ma99uS

+1

Gracias por los nombres de script 'fuente'd. –

54

$BASH_SOURCE da la respuesta correcta cuando se obtiene el script.

Sin embargo, esto incluye la ruta de lo que para obtener las secuencias de comandos de nombres de archivos Solo, utilice:

$(basename $BASH_SOURCE) 
5
this="$(dirname "$(realpath "$BASH_SOURCE")")" 

Esto resuelve los enlaces simbólicos (realpath hace eso), se ocupa de los espacios (comillas dobles hacen esto), y encuentre el nombre del guión actual, incluso cuando haya recibido el origen (./mitblock) o haya sido llamado por otros guiones ($ BASH_SOURCE lo maneje). Después de todo eso, es bueno guardar esto en una variable de entorno para volver a usar o para copiar fácilmente en otro lugar (this =) ...

+3

FYI 'realpath' no es un comando BASH incorporado. Es un ejecutable independiente que está disponible solo en ciertas distribuciones – StvnW

10

He encontrado que esta línea funciona siempre, independientemente de si el archivo está siendo originado o ejecutado como un script.

echo "${BASH_SOURCE[${#BASH_SOURCE[@]} - 1]}" 

Si quieres seguir enlaces simbólicos usar readlink en el camino que llega más arriba, de forma recursiva o no de forma recursiva.

La razón por la que funciona el one-liner se explica por el uso de la variable de entorno BASH_SOURCE y su asociado FUNCNAME.

BASH_SOURCE

una variable de matriz cuyos miembros son los nombres de archivo de fuente donde se definen los correspondientes nombres de función shell en la variable de matriz FUNCNAME. La función de shell $ {FUNCNAME [$ i]} se define en el archivo $ {BASH_SOURCE [$ i]} y se llama desde $ {BASH_SOURCE [$ i + 1]}.

FUNCNAME

una variable de matriz que contiene los nombres de todas las funciones de concha actualmente en la pila de llamadas ejecución. El elemento con índice 0 es el nombre de cualquier función de shell actualmente en ejecución. El elemento inferior (el que tiene el índice más alto) es "principal". Esta variable existe solo cuando se está ejecutando una función de shell. Las asignaciones a FUNCNAME no tienen ningún efecto y devuelven un estado de error. Si FUNCNAME no está configurado, pierde sus propiedades especiales, incluso si se restablece posteriormente.

Esta variable se puede usar con BASH_LINENO y BASH_SOURCE. Cada elemento de FUNCNAME tiene elementos correspondientes en BASH_LINENO y BASH_SOURCE para describir la pila de llamadas. Por ejemplo, se llamó a $ {FUNCNAME [$ i]} desde el archivo $ {BASH_SOURCE [$ i + 1]} en el número de línea $ {BASH_LINENO [$ i]}. El builtin de quien llama muestra la pila de llamadas actual utilizando esta información.

[Fuente: manual de Bash]

+1

Funciona si el archivo en el archivo 'a' (suponiendo que el contenido de' a' es 'echo" $ {BASH_SOURCE [$ {# BASH_SOURCE [@]} - 1] } "') de una sesión interactiva, entonces le dará la ruta 'a'. Pero si escribes un script 'b' con' source a' y ejecutas './B', devolverá la ruta de' b'. – PSkocik

2

Info gracias a Bill Hernández. Agregué algunas preferencias que estoy adoptando.

#!/bin/bash 
function Usage(){ 
    echo " Usage: show_parameters [ arg1 ][ arg2 ]" 
} 
[[ ${#2} -eq 0 ]] && Usage || { 
    echo 
    echo "# arguments called with ----> ${@}  " 
    echo "# \$1 -----------------------> $1  " 
    echo "# \$2 -----------------------> $2  " 
    echo "# path to me ---------------> ${0}  " | sed "s/$USER/\$USER/g" 
    echo "# parent path --------------> ${0%/*} " | sed "s/$USER/\$USER/g" 
    echo "# my name ------------------> ${0##*/} " 
    echo 
} 

Saludos

-5

algo como esto?

export LC_ALL=en_US.UTF-8 
#!/bin/bash 
#!/bin/sh 

#---------------------------------------------------------------------- 
start_trash(){ 
ver="htrash.sh v0.0.4" 
$TRASH_DIR # url to trash $MY_USER 
$TRASH_SIZE # Show Trash Folder Size 

echo "Would you like to empty Trash [y/n]?" 
read ans 
if [ $ans = y -o $ans = Y -o $ans = yes -o $ans = Yes -o $ans = YES ] 
then 
echo "'yes'" 
cd $TRASH_DIR && $EMPTY_TRASH 
fi 
if [ $ans = n -o $ans = N -o $ans = no -o $ans = No -o $ans = NO ] 
then 
echo "'no'" 
fi 
return $TRUE 
} 
#----------------------------------------------------------------------- 

start_help(){ 
echo "HELP COMMANDS-----------------------------" 
echo "htest www     open a homepage " 
echo "htest trash    empty trash  " 
return $TRUE 
} #end Help 
#-----------------------------------------------# 

homepage="" 

return $TRUE 
} #end cpdebtemp 

# -Case start 
# if no command line arg given 
# set val to Unknown 
if [ -z $1 ] 
then 
    val="*** Unknown ***" 
elif [ -n $1 ] 
then 
# otherwise make first arg as val 
    val=$1 
fi 
# use case statement to make decision for rental 
case $val in 
    "trash") start_trash ;; 
    "help") start_help ;; 
    "www") firefox $homepage ;; 
    *) echo "Sorry, I can not get a $val for you!";; 
esac 
# Case stop 
+4

-1, no responde la pregunta (no muestra cómo encontrar el nombre del guión), y es un ejemplo confuso en un guión con muchos errores. –

6

si su secuencia de comandos shell de invocación como

/home/mike/runme.sh 

$ 0 es el nombre completo

/home/mike/runme.sh 

nombre base $ 0, se obtenga el nombre de archivo base

runme.sh 

y necesita poner este nombre básico en una variable lik e

filename=$(basename $0) 

y añadir su texto adicional

echo "You are running $filename" 

por lo que sus guiones como

/home/mike/runme.sh 
#!/bin/bash 
filename=$(basename $0) 
echo "You are running $filename" 
1
echo "$(basename "`test -L ${BASH_SOURCE[0]} \ 
        && readlink ${BASH_SOURCE[0]} \ 
        || echo ${BASH_SOURCE[0]}`")" 
1

En bash se puede obtener el nombre del archivo de script utilizando $0. Generalmente $1, $2 etc. tienen acceso a los argumentos CLI. De manera similar, $0 es para acceder al nombre que desencadena el scrip (nombre de archivo de script).

#!/bin/bash 
echo "You are running $0" 
... 
... 
1

Desde algunos comentarios preguntado por el nombre del archivo sin extensión, aquí está un ejemplo cómo lograr eso:

FileName=${0##*/} 
FileNameWithoutExtension=${FileName%.*} 

Enjoy!

Cuestiones relacionadas