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

606

¿Cómo puedo determinar el nombre del archivo de script Bash dentro del propio script?

Al igual que si mi script está en el archivo runme.sh, ¿cómo lo haría para mostrar el mensaje "Estás ejecutando runme.sh" sin codificar eso?

Ma99uS
fuente
3
Similar [¿Puede un script bash decir en qué directorio está almacenado?] (A stackoverflow.com/questions/59895/… )
Rodrigue

Respuestas:

631
me=`basename "$0"`

Para leer un enlace simbólico 1 , que generalmente no es lo que desea (por lo general, no desea confundir al usuario de esta manera), intente:

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

OMI, eso producirá resultados confusos. "Ejecuté 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 una funcionalidad diferente basada en el nombre que se le llama (piense en gzip y gunzip en algunas plataformas).


1 Es decir, para resolver enlaces simbólicos de modo que cuando el usuario ejecuta, foo.shque en realidad es un enlace simbólico bar.sh, desea utilizar el nombre resuelto en bar.shlugar de foo.sh.

Tanktalus
fuente
40
$ 0 le da el nombre a través del cual se invocó el script, no la ruta real del archivo de script real.
Chris Conway
44
Funciona a menos que te llamen a través de un enlace simbólico. Pero, incluso entonces, generalmente es lo que quieres de todos modos, IME.
Tanktalus
78
No funciona para scripts que se obtienen en lugar de invocados.
Charles Duffy
1
@Charles Duffy: Vea la respuesta de Dimitre Radoulov a continuación .
Serge Wautier
8
-1, 1. readlink solo viajará un enlace simbólico profundo, 2. $0en el primer ejemplo está sujeto a la división de palabras, 3. $0se pasa a basename, readlink y echo en una posición que le permita ser tratado como un interruptor de línea de comando . Sugiero lugar me=$(basename -- "$0")o mucho más eficiente a expensas de la legibilidad, me=${0##*/}. Para enlaces simbólicos, me=$(basename -- "$(readlink -f -- "$0")")suponiendo utilidades gnu, de lo contrario será un script muy largo que no escribiré aquí.
Score_Under
246
# ------------- 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

# ------------- LLAMADO ------------- ## ------------- LLAMADO ------------- #

# Aviso en la siguiente línea, el primer argumento se llama dentro del doble, # Aviso en la siguiente línea, el primer argumento se llama dentro del doble, 
# y comillas simples, ya que contiene dos palabras# y comillas simples, ya que contiene dos palabras

$ /misc/shell_scripts/check_root/show_parms.sh "'hola allí'" "'william'"/ misc / shell_scripts / check_root / show_parms . sh "'hola' '" "' william '" 

# ------------- RESULTADOS ------------- ## ------------- RESULTADOS ------------- #

# argumentos llamados con ---> 'hola' 'william'# argumentos llamados con ---> 'hola' 'william'
# $ 1 ----------------------> 'hola'# $ 1 ----------------------> 'hola'
# $ 2 ----------------------> 'william'# $ 2 ----------------------> 'william'
# ruta hacia mí --------------> /misc/shell_scripts/check_root/show_parms.sh# ruta hacia mí --------------> /misc/shell_scripts/check_root/show_parms.sh
# ruta principal -------------> / misc / shell_scripts / check_root# ruta principal -------------> / misc / shell_scripts / check_root
# mi nombre -----------------> show_parms.sh# mi nombre -----------------> show_parms.sh

# ------------- FIN ------------- ## ------------- FIN ------------- #
Bill Hernández
fuente
11
@NickC ver eliminación de subcadenas
cychoi
1
¿Cómo obtengo solo show_params, es decir, el nombre sin ninguna extensión opcional?
Acumenus
No funciona en caso de que el script se invoque desde otra carpeta. La ruta está incluida en el ${0##*/}. Probado con GitBash.
AlikElzin-kilaka
1
Lo anterior no funcionó para mí desde un script .bash_login, pero la solución Dimitre Radoulov de $ BASH_SOURCE funciona muy bien.
John
@ John Seguramente te refieres a .bash_profile ? O alternativamente .bashrc ? Sin embargo, debe saber que hay algunas diferencias sutiles en cómo se invocan / obtienen. Estoy muerto de cansancio, pero si lo estoy pensando bien, probablemente sea el problema. Un lugar importante es si está iniciando sesión en un sistema de forma remota a través de, por ejemplo, ssh versus en el sistema local.
Pryftan
193

Con bash> = 3, los siguientes trabajos:

$ ./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"
Dimitre Radoulov
fuente
24
¡Excelente! Esa es la respuesta que funciona tanto para ./scrip.sh como para fuente ./script.sh
zhaorufei
44
Esto es lo que quiero, y es fácil usar " dirname $BASE_SOURCE" para obtener el directorio que ubicaron los scripts.
Larry Cai
2
Casi aprendí esa diferencia de la manera más difícil cuando escribí un script de eliminación automática. Afortunadamente, 'rm' tenía el alias de 'rm -i' :)
kervin
de todos modos a la. ./s para obtener el nombre ./s en lugar de bash? He descubierto que $ 1 no siempre se establece en ./s ...
David Mokon Bond
1
Está en BASH_SOURCE , ¿no?
Dimitre Radoulov
69

$BASH_SOURCE da la respuesta correcta al obtener el script.

Sin embargo, esto incluye la ruta, así que para obtener solo el nombre de archivo de los scripts, use:

$(basename $BASH_SOURCE) 
Zainka
fuente
2
Esta respuesta es, en mi humilde opinión, la mejor porque la solución hace uso del código de autodocumentación. $ BASH_SOURCE es totalmente comprensible sin leer ninguna documentación, mientras que $ {0 ## * /} no lo es
Andreas M. Oberheim el
Esta respuesta es de más valor, creo, porque si corremos como . <filename> [arguments], $0daría el nombre del llamador de shell. Bueno, al menos en OSX seguro.
Mihir
68

Si el nombre del script tiene espacios en él, de una manera más sólida es utilizar "$0"o "$(basename "$0")"- o en MacOS: "$(basename \"$0\")". Esto evita que el nombre sea destrozado o interpretado de ninguna manera. En general, es una buena práctica siempre entre comillas dobles nombres de variables en el shell.

Josh Lee
fuente
2
+1 para respuesta directa + conciso. si necesita funciones de enlace simbólico, sugiero ver: la respuesta de Travis B. Hartwell.
Trevor Boyd Smith
26

Si lo quieres sin la ruta, entonces usarías ${0##*/}

Señor almizclero
fuente
¿Y si lo quiero sin ninguna extensión de archivo opcional?
Acumenus
Para eliminar una extensión, puede probar "$ {VARIABLE% .ext}" donde VARIABLE es el valor que obtuvo de $ {0 ## * /} y ".ext" es la extensión que desea eliminar.
BrianV
19

Para responder a Chris Conway , en Linux (al menos) harías esto:

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

readlink imprime 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 enlace, también resolvería ese enlace).

Travis B. Hartwell
fuente
2
-n es inofensivo pero no necesario porque la estructura $ (...) lo recortará.
zhaorufei
16

He encontrado que esta línea siempre funciona, independientemente de si el archivo se obtiene o se ejecuta como un script.

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

Si desea seguir el uso de enlaces simbólicos readlinken la ruta que obtiene arriba, recursivamente o no recursivamente.

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

BASH_SOURCE

Una variable de matriz cuyos miembros son los nombres de los archivos de origen donde se definen los nombres de funciones de shell correspondientes 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 shell actualmente en la pila de llamadas de ejecución. El elemento con índice 0 es el nombre de cualquier función de shell que se esté ejecutando actualmente. El elemento más inferior (el que tiene el índice más alto) es "principal". Esta variable existe solo cuando se ejecuta una función de shell. Las asignaciones a FUNCNAME no tienen 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, $ {FUNCNAME [$ i]} se llamó desde el archivo $ {BASH_SOURCE [$ i + 1]} en el número de línea $ {BASH_LINENO [$ i]}. La persona que llama muestra la pila de llamadas actual con esta información.

[Fuente: manual de Bash]

gkb0986
fuente
2
Funciona si se origina en el archivo a (se supone que ael contenido es echo "${BASH_SOURCE[${#BASH_SOURCE[@]} - 1]}") de una sesión interactiva, entonces le dará ala ruta. Pero si escribe un script bcon source aél y lo ejecuta ./b, devolverá bla ruta.
PSkocik
12

Estas respuestas son correctas para los casos que indican, pero aún existe un problema si ejecuta el script desde otro script utilizando la palabra clave 'source' (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 marginal y no debe tomarse DEMASIADO en serio. Si ejecuta el script desde otro script directamente (sin 'fuente'), usar $ 0 funcionará.

Jim Dodd
fuente
2
Tienes un muy buen punto. No es un caso límite de la OMI. Sin embargo, hay una solución: ver la respuesta de Dimitre Radoulov arriba
Serge Wautier
10

Dado que algunos comentarios preguntaron sobre el nombre de archivo sin extensión, aquí hay un ejemplo de cómo lograrlo:

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

¡Disfrutar!

Simon Mattes
fuente
9

Re: Respuesta de Tanktalus (aceptada) arriba, una forma un poco más limpia es usar:

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

Si su script proviene de otro script bash, puede usar:

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

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

Simón
fuente
1
Gracias por los sourcenombres de script d.
Fredrick Gauss
8

Puede usar $ 0 para determinar el nombre de su script (con la ruta completa): para obtener el nombre del script solo puede recortar esa variable con

basename $0
VolkA
fuente
8

si su invocar shell script como

/home/mike/runme.sh

$ 0 es el nombre completo

 /home/mike/runme.sh

basename $ 0 obtendrá el nombre del archivo base

 runme.sh

y necesitas poner este nombre básico en una variable como

filename=$(basename $0)

y agrega tu texto adicional

echo "You are running $filename"

así que a tus guiones les gusta

/home/mike/runme.sh
#!/bin/bash 
filename=$(basename $0)
echo "You are running $filename"
LawrenceLi
fuente
7
this="$(dirname "$(realpath "$BASH_SOURCE")")"

Esto resuelve enlaces simbólicos (realpath hace eso), maneja espacios (las comillas dobles hacen esto) y encontrará el nombre del script actual incluso cuando se obtiene (. ./Myscript) o lo llaman otros scripts ($ BASH_SOURCE maneja eso). Después de todo eso, es bueno guardar esto en una variable de entorno para reutilizarlo o copiarlo fácilmente en otro lugar (esto =) ...

jcalfee314
fuente
3
FYI realpathno es un comando BASH incorporado. Es un ejecutable independiente que solo está disponible en ciertas distribuciones
StvnW
4

En bashpuede obtener el nombre del archivo de script utilizando $0. Generalmente $1, $2etc. son para acceder a los argumentos de la CLI. Del mismo modo $0es acceder al nombre que desencadena el script (nombre del archivo de script).

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

Si invoca el script con una ruta como, /path/to/script.shentonces $0también le dará el nombre del archivo con la ruta. En ese caso, debe usar $(basename $0)para obtener solo el nombre del archivo de script.

rashok
fuente
3
echo "$(basename "`test -L ${BASH_SOURCE[0]} \
                   && readlink ${BASH_SOURCE[0]} \
                   || echo ${BASH_SOURCE[0]}`")"
ecwpz91
fuente
2

$0no responde la pregunta (según tengo entendido). Una demostración:

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

¿Cómo se ./linktoscriptimprime script.sh?

[EDITAR] Según @ephemient en los comentarios anteriores, aunque el enlace simbólico puede parecer artificial, es posible jugar con él de $0tal manera que no represente un recurso del sistema de archivos. El OP es un poco ambiguo sobre lo que quería.

Chris Conway
fuente
agregué una respuesta a esto a mi respuesta anterior, pero no estoy de acuerdo con que esto sea lo que realmente se quiere (o que usted debería querer, salvo algunas circunstancias atenuantes que actualmente no puedo entender)
Tanktalus
2
Creo que imprimir linktoscript en lugar de script.sh es una característica, no un error. Varios comandos de Unix usan su nombre para alterar su comportamiento. Un ejemplo es vi / ex / view.
mouviciel
@Tanktalus: Hay casos en los que desea que el comportamiento cambie en función de la ubicación real del archivo: en un proyecto anterior tenía un script de "Rama activa", que enlazaría varias rutas desde la rama en la que estaba. trabajando en; esas herramientas podrían averiguar en qué directorio se habían desprotegido para ejecutar los archivos correctos.
Andrew Aylett
2

Información 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
}

Salud

linxuser
fuente
1

Corto, claro y simple, en my_script.sh

#!/bin/bash

running_file_name=$(basename "$0")

echo "You are running '$running_file_name' file."

Fuera puesto:

./my_script.sh
You are running 'my_script.sh' file.
Bơ Loong A Nhứi
fuente
0

Esto es lo que se me ocurrió, inspirado en la respuesta de Dimitre Radoulov (que, por cierto, he votado) .

script="$BASH_SOURCE"
[ -z "$BASH_SOURCE" ] && script="$0"

echo "Called $script with $# argument(s)"

independientemente de la forma en que llame a su script

. path/to/script.sh

o

./path/to/script.sh
Salathiel Genèse
fuente
-1

echo "Estás ejecutando $ 0"

mmacaulay
fuente
No es un script bash, es la ruta completa.
e-info128
-6

¿Algo así?

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
hynt
fuente
55
-1, no responde la pregunta (no muestra cómo encontrar el nombre del script), y es un ejemplo confuso en un script con errores.
Score_Under