bash - resueltos - Cómo detectar si un script está siendo originado
scripts linux ejercicios resueltos (15)
Tengo un script en el que no quiero que llame a exit
si se está obteniendo. Inicialmente pensé en comprobar si $0 == bash
pero esto tiene problemas si el script proviene de otro script, o si el usuario lo obtiene de ksh
. ¿Hay alguna forma confiable de detectar si un script se está obteniendo?
Daré una respuesta específica de BASH. Concha Korn, lo siento. Supongamos que el nombre de su script es include2.sh
; luego haga una función dentro de include2.sh
llamada am_I_sourced
. Aquí está mi versión demo de include2.sh
:
am_I_sourced()
{
if [ "${FUNCNAME[1]}" = source ]; then
if [ "$1" = -v ]; then
echo "I am being sourced, this filename is ${BASH_SOURCE[0]} and my caller script/shell name was $0"
fi
return 0
else
if [ "$1" = -v ]; then
echo "I am not being sourced, my script/shell name was $0"
fi
return 1
fi
}
if am_I_sourced -v; then
echo "Do something with sourced script"
else
echo "Do something with executed script"
fi
Ahora intenta ejecutarlo de muchas maneras:
~/toys/bash $ chmod a+x include2.sh
~/toys/bash $ ./include2.sh
I am not being sourced, my script/shell name was ./include2.sh
Do something with executed script
~/toys/bash $ bash ./include2.sh
I am not being sourced, my script/shell name was ./include2.sh
Do something with executed script
~/toys/bash $ . include2.sh
I am being sourced, this filename is include2.sh and my caller script/shell name was bash
Do something with sourced script
Entonces esto funciona sin excepción, y no está usando las cosas frágiles $_
. Este truco utiliza la facilidad de introspección de BASH, es decir, las variables FUNCNAME
y BASH_SOURCE
; ver su documentación en la página de manual de bash.
Solo dos advertencias:
1) la llamada a am_I_called
debe tener lugar en el script de origen, pero no dentro de ninguna función, no sea que ${FUNCNAME[1]}
devuelva algo más. Sí ... podrías haber marcado ${FUNCNAME[2]}
- pero solo haces tu vida más difícil.
2) la función am_I_called
debe residir en el script de origen si desea averiguar cuál es el nombre del archivo que se está incluyendo.
Después de leer la respuesta de @ DennisWilliamson, hay algunos problemas, ver a continuación:
Como esta pregunta representa ksh y bash , hay otra parte en esta respuesta referente a ksh ... ver abajo.
Manera simple de bash
[ "$0" = "$BASH_SOURCE" ]
Probemos (sobre la marcha porque ese bash podría ;-):
source <(echo $''#!/bin/bash
[ "$0" = "$BASH_SOURCE" ] && v=own || v=sourced;
echo "process $$ is $v ($0, $BASH_SOURCE)" '')
process 29301 is sourced (bash, /dev/fd/63)
bash <(echo $''#!/bin/bash
[ "$0" = "$BASH_SOURCE" ] && v=own || v=sourced;
echo "process $$ is $v ($0, $BASH_SOURCE)" '')
process 16229 is own (/dev/fd/63, /dev/fd/63)
Uso source
lugar de off .
para legibilidad (como .
es un alias para la source
):
. <(echo $''#!/bin/bash
[ "$0" = "$BASH_SOURCE" ] && v=own || v=sourced;
echo "process $$ is $v ($0, $BASH_SOURCE)" '')
process 29301 is sourced (bash, /dev/fd/63)
Tenga en cuenta que el número de proceso no cambia mientras el proceso permanece en origen :
echo $$
29301
Por qué no usar $_ == $0
comparación
Para garantizar muchos casos, comienzo a escribir un verdadero guión:
#!/bin/bash
# As $_ could be used only once, uncomment one of two following lines
#printf ''_="%s", 0="%s" and BASH_SOURCE="%s"/n'' "$_" "$0" "$BASH_SOURCE"
[[ "$_" != "$0" ]] && DW_PURPOSE=sourced || DW_PURPOSE=subshell
[ "$0" = "$BASH_SOURCE" ] && BASH_KIND_ENV=own || BASH_KIND_ENV=sourced;
echo "proc: $$[ppid:$PPID] is $BASH_KIND_ENV (DW purpose: $DW_PURPOSE)"
Copie esto en un archivo llamado testscript
:
cat >testscript
chmod +x testscript
Ahora podríamos probar:
./testscript
proc: 25758[ppid:24890] is own (DW purpose: subshell)
Está bien.
. ./testscript
proc: 24890[ppid:24885] is sourced (DW purpose: sourced)
source ./testscript
proc: 24890[ppid:24885] is sourced (DW purpose: sourced)
Está bien.
Pero, para probar un script antes de agregar el indicador -x
:
bash ./testscript
proc: 25776[ppid:24890] is own (DW purpose: sourced)
O para usar variables predefinidas:
env PATH=/tmp/bintemp:$PATH ./testscript
proc: 25948[ppid:24890] is own (DW purpose: sourced)
env SOMETHING=PREDEFINED ./testscript
proc: 25972[ppid:24890] is own (DW purpose: sourced)
Esto no funcionará más.
Mover el comentario de la 5ª línea a la 6ª daría una respuesta más legible:
./testscript
_="./testscript", 0="./testscript" and BASH_SOURCE="./testscript"
proc: 26256[ppid:24890] is own
. testscript
_="_filedir", 0="bash" and BASH_SOURCE="testscript"
proc: 24890[ppid:24885] is sourced
source testscript
_="_filedir", 0="bash" and BASH_SOURCE="testscript"
proc: 24890[ppid:24885] is sourced
bash testscript
_="/bin/bash", 0="testscript" and BASH_SOURCE="testscript"
proc: 26317[ppid:24890] is own
env FILE=/dev/null ./testscript
_="/usr/bin/env", 0="./testscript" and BASH_SOURCE="./testscript"
proc: 26336[ppid:24890] is own
Harder: ksh ahora ...
Como no uso mucho ksh , después de leer en la página man, hay mis intentos:
#!/bin/ksh
set >/tmp/ksh-$$.log
Copie esto en un testfile.ksh
:
cat >testfile.ksh
chmod +x testfile.ksh
Que ejecutarlo dos veces:
./testfile.ksh
. ./testfile.ksh
ls -l /tmp/ksh-*.log
-rw-r--r-- 1 user user 2183 avr 11 13:48 /tmp/ksh-9725.log
-rw-r--r-- 1 user user 2140 avr 11 13:48 /tmp/ksh-9781.log
echo $$
9725
y ver:
diff /tmp/ksh-{9725,9781}.log | grep ^/> # OWN SUBSHELL:
> HISTCMD=0
> PPID=9725
> RANDOM=1626
> SECONDS=0.001
> lineno=0
> SHLVL=3
diff /tmp/ksh-{9725,9781}.log | grep ^/< # SOURCED:
< COLUMNS=152
< HISTCMD=117
< LINES=47
< PPID=9163
< PS1=''$ ''
< RANDOM=29667
< SECONDS=23.652
< level=1
< lineno=1
< SHLVL=2
Hay alguna variable heredada en una ejecución de origen , pero nada realmente relacionado ...
Incluso podría verificar que $SECONDS
sea cercano a 0.000
, pero eso solo asegura que haya casos de origen manual ...
Incluso podría intentar verificar qué es el padre:
Coloque esto en su testfile.ksh
:
ps $PPID
Que:
./testfile.ksh
PID TTY STAT TIME COMMAND
32320 pts/4 Ss 0:00 -ksh
. ./testfile.ksh
PID TTY STAT TIME COMMAND
32319 ? S 0:00 sshd: user@pts/4
o ps ho cmd $PPID
, pero esto funciona solo para un nivel de subserie ...
Lo siento, no pude encontrar una manera confiable de hacerlo, en ksh .
Esto funciona más adelante en el script y no depende de la variable _:
## Check to make sure it is not sourced:
Prog=myscript.sh
if [ $(basename $0) = $Prog ]; then
exit 1 # not sourced
fi
o
[ $(basename $0) = $Prog ] && exit
Esto parece ser portátil entre Bash y Korn:
[[ $_ != $0 ]] && echo "Script is being sourced" || echo "Script is a subshell"
Una línea similar a esta o una asignación como `pathname =" $ _ "(con una prueba y acción posterior) debe estar en la primera línea del script o en la línea después del shebang (que, si se usa, debe ser para ksh para que funcione en la mayoría de las circunstancias).
FWIW, después de leer todas las otras respuestas, se me ocurrió la siguiente solución:
Esto funciona para todos los scripts, que comienzan con #!/bin/bash
pero también pueden ser originados por diferentes shells.
#!/bin/bash
# Function definitions (API) and shell variables (constants) go here
main()
{
# The script''s execution part goes here
}
unset BASH_SOURCE 2>/dev/null
test ".$0" != ".$BASH_SOURCE" || main "$@"
Esta receta de script tiene las siguientes propiedades:
- Si se ejecuta por
bash
, se llamamain
. - Si proviene de
bash
,main
solo se llama, si el script de llamada tiene el mismo nombre. (Por ejemplo, si se fuentes a sí mismo) - Si proviene de un shell que no sea
bash
,main
no se llama. - Si se ejecuta mediante un shell que no sea
bash
, no se llama amain
. Si se evalúa por
bash
coneval
(eval "`cat script`"
todas las comillas son importantes! ) Que no provienen directamente de la línea de comandos, esto se llamamain
. Para todas las demás variantes deeval
,main
no se llama.Si no se llama
main
, devuelvetrue
($?=0
).- Y no depende del comportamiento no documentado que podría cambiar.
Por lo tanto, a excepción de algunos casos de esquina improbables, solo se llama a main
, cuando el script se ejecuta de la manera habitual. Normalmente esto es lo que quiere, especialmente porque carece de un código complejo y difícil de entender.
Como BASH_SOURCE
no se puede BASH_SOURCE
en bash
, pero en todos los otros shells, esto también capta el caso de borde donde BASH_SOURCE
se establece en $0
.
Tenga en cuenta que es muy similar al código de Python:
if __name__ == ''__main__'': main()
Lo cual también evita la invocación de
main
, excepto en algunos casos de esquina, ya que puedes importar / cargar el script e imponer ese__name__=''__main__''
Por qué creo que esta es una buena forma general de resolver el desafío
Si tiene algo, que puede ser obtenido por múltiples caparazones, debe ser compatible. Sin embargo (lea las otras respuestas), ya que no hay una forma portátil de detectar el source
, debe cambiar las reglas .
Al hacer cumplir que el script debe ser ejecutado por /bin/bash
, usted exactamente hace esto.
Esto resuelve todos los casos, pero luego, en cuyo caso, el script no se puede ejecutar directamente:
-
/bin/bash
no está instalado o no funciona (por ejemplo, en un entorno de arranque) - Si lo canalizas a un shell que no sea bash como en
curl https://example.com/script | $SHELL
curl https://example.com/script | $SHELL
donde$SHELL
no esbash
Sin embargo, no puedo pensar en ninguna razón real donde lo necesites y también en la posibilidad de obtener el mismo script en paralelo. Por lo general, puede envolverlo, de forma que el script siempre se obtenga. Luego ejecuta el main
a mano. Como eso:
-
sh -c ''. script && main''
-
echo ''eval "`curl https://example.com/script`" && main'' | $SHELL
Importante:
Este último ejecuta
main
dos veces, si$SHELL
ejecutabash
y la línea no se ejecuta desde la línea de comandos. (Pero realmente no puedo pensar en ninguna razón por la que deba usar esto en un script, excepto por engañar el código a propósito).
Nota
¡Esta respuesta no hubiera sido posible sin la ayuda de todas las otras respuestas! Incluso los equivocados, lo que me hizo publicar esto.
La respuesta BASH_SOURCE[]
(bash-3.0 y posterior) parece más simple, aunque BASH_SOURCE[]
no está documentado para funcionar fuera de un cuerpo de función (actualmente funciona, en desacuerdo con la página man).
La forma más robusta, como lo sugiere Wirawan Purwanto, es verificar FUNCNAME[1]
dentro de una función :
function mycheck() { declare -p FUNCNAME; }
mycheck
Entonces:
$ bash sourcetest.sh
declare -a FUNCNAME=''([0]="mycheck" [1]="main")''
$ . sourcetest.sh
declare -a FUNCNAME=''([0]="mycheck" [1]="source")''
Esto es equivalente a verificar la salida de la caller
, los valores main
y source
distinguen el contexto de la persona que llama. El uso de FUNCNAME[]
permite capturar y analizar la salida del caller
. Sin embargo, necesita saber o calcular su profundidad de llamada local para ser correcto. Casos como un script que se obtiene desde otra función o script provocarán que la matriz (pila) sea más profunda. ( FUNCNAME
es una variable de matriz de bash especial, debe tener índices contiguos correspondientes a la pila de llamadas, siempre que nunca se unset
).
function issourced() {
[[ ${FUNCNAME[@]: -1} == "source" ]]
}
(En bash-4.2 y posterior puede usar la forma más simple ${FUNCNAME[-1]}
lugar para el último elemento de la matriz. Mejorado y simplificado gracias al comentario de Dennis Williamson a continuación).
Sin embargo, su problema tal como se establece es " Tengo un script en el que no deseo que llame a ''exit'' si se está obteniendo ". La expresión común de bash
para esta situación es:
return 2>/dev/null || exit
Si el script se está obteniendo, la return
dará por terminado el script de origen y regresará a la persona que llama.
Si el script se está ejecutando, el return
devolverá un error (redirigido), y exit
terminará el script de la forma habitual. Tanto el return
como la exit
pueden tomar un código de salida, si es necesario.
Lamentablemente, esto no funciona en ksh
(al menos no en la versión derivada de AT & T que tengo aquí), trata el return
como equivalente para exit
si se invoca fuera de una función o script de fuente de puntos.
Actualizado : Lo que puede hacer en las versiones contemporáneas de ksh
es verificar la variable especial .sh.level
que está configurada para la profundidad de llamada a la función. En el caso de un guión invocado, inicialmente se desactivará, en el caso de un script de fuente de puntos, se establecerá en 1.
function issourced {
[[ ${.sh.level} -eq 2 ]]
}
issourced && echo this script is sourced
Esto no es tan robusto como la versión de bash, debe invocar issourced()
en el archivo que está probando en el nivel superior o en una profundidad de función conocida.
(También puede interesarle este código en github que usa una función de disciplina ksh
y algunos trucos de depuración para emular la matriz FUNCNAME
).
La respuesta canónica aquí: http://mywiki.wooledge.org/BashFAQ/109 también ofrece $-
como otro indicador (aunque imperfecto) del estado del shell.
Notas:
- es posible crear funciones de bash llamadas "main" y "source" ( anulando el
FUNCNAME[]
in), estos nombres pueden aparecer enFUNCNAME[]
pero siempre que solo se pruebe el último elemento de esa matriz, no hay ambigüedad. - No tengo una buena respuesta para
pdksh
. Lo más cercano que puedo encontrar se aplica solo apdksh
, donde cada fuente de un script abre un nuevo descriptor de archivo (comenzando con 10 para el script original). Casi seguro que no es algo en lo que quieras confiar ...
Me gustaría sugerir una pequeña corrección a la respuesta muy útil de Dennis , para hacerlo un poco más portátil, espero:
[ "$_" != "$0" ] && echo "Script is being sourced" || echo "Script is a subshell"
porque [[
no está reconocido por el shell (algo remanente anal en mi humilde opinión) Debian POSIX compatible , dash
. Además, uno puede necesitar las comillas para proteger contra los nombres de archivo que contienen espacios, de nuevo en dicho shell.
Necesitaba un trazador de líneas que funciona en [mac, linux] con bash.version> = 3 y ninguna de estas respuestas se ajusta a la ley.
[[ ${BASH_SOURCE[0]} = $0 ]] && main "$@"
No creo que haya ninguna forma portátil de hacer esto tanto en ksh como en bash. En bash, podrías detectarlo usando la salida del caller
, pero no creo que exista un equivalente en ksh.
Si su versión Bash sabe sobre la variable de matriz BASH_SOURCE, intente algo como:
# man bash | less -p BASH_SOURCE
#[[ ${BASH_VERSINFO[0]} -le 2 ]] && echo ''No BASH_SOURCE array variable'' && exit 1
[[ "${BASH_SOURCE[0]}" != "${0}" ]] && echo "script ${BASH_SOURCE[0]} is being sourced ..."
$_
es bastante frágil. Debes verificarlo como lo primero que haces en el script. Y aun así, no se garantiza que contenga el nombre de su shell (si tiene origen) o el nombre del script (si se ejecuta).
Por ejemplo, si el usuario ha establecido BASH_ENV
, en la parte superior de un script, $_
contiene el nombre del último comando ejecutado en el script BASH_ENV
.
La mejor forma que he encontrado es usar $0
así:
name="myscript.sh"
main()
{
echo "Script was executed, running main..."
}
case "$0" in *$name)
main "$@"
;;
esac
Desafortunadamente, esta manera no funciona de la caja en zsh debido a que la opción functionargzero
hace más de lo que sugiere su nombre y está activada por defecto.
Para unsetopt functionargzero
esto, puse unsetopt functionargzero
en mi .zshenv
.
Directamente al grano: debe evaluar si la variable "$ 0" es igual al nombre de su Shell.
Me gusta esto:
#!/bin/bash
echo "First Parameter: $0"
echo
if [[ "$0" == "bash" ]] ; then
echo "The script was sourced."
else
echo "The script WAS NOT sourced."
fi
A través de SHELL :
$ bash check_source.sh
First Parameter: check_source.sh
The script WAS NOT sourced.
A través de SOURCE :
$ source check_source.sh
First Parameter: bash
The script was sourced.
Es bastante difícil tener una forma 100% portátil de detectar si un script se obtuvo o no.
En cuanto a mi experiencia (7 años con Shellscripting) , la única forma segura (sin depender de variables de entorno con PID, etc., que no es seguro debido a que es algo VARIABLE ), debes:
- ampliar las posibilidades de su si
- usando el interruptor / caja, si quieres.
Ambas opciones no se pueden escalar automáticamente, pero es la manera más segura.
Por ejemplo:
cuando obtiene un script a través de una sesión SSH, el valor devuelto por la variable "$ 0" (cuando se usa source ) es -bash .
#!/bin/bash
echo "First Parameter: $0"
echo
if [[ "$0" == "bash" || "$0" == "-bash" ]] ; then
echo "The script was sourced."
else
echo "The script WAS NOT sourced."
fi
O
#!/bin/bash
echo "First Parameter: $0"
echo
if [[ "$0" == "bash" ]] ; then
echo "The script was sourced."
elif [[ "$0" == "-bash" ]] ; then
echo "The script was sourced via SSH session."
else
echo "The script WAS NOT sourced."
fi
Soluciones robustas para bash
, ksh
, zsh
, incluida una carcasa cross-shell , más una solución POSIX razonablemente robusta :
Los números de versión proporcionados son aquellos en los que se verificó la funcionalidad. Es probable que estas soluciones también funcionen en versiones mucho más antiguas. Bienvenido a los comentarios .
Al usar solo las características de POSIX (como en el
dash
, que actúa como/bin/sh
en Ubuntu), no hay una manera sólida de determinar si un script se está obteniendo: consulte la parte inferior de esta respuesta para obtener la mejor aproximación .
Una línea de seguimiento: explicación a continuación; la versión cross-shell es compleja, pero debería funcionar de manera robusta:
bash (verificado en 3.57)
[[ $0 != "$BASH_SOURCE" ]] && sourced=1 || sourced=0
ksh (verificado en 93u +)
[[ $(cd "$(dirname -- "$0")" && printf ''%s'' "${PWD%/}/")$(basename -- "$0") != "${.sh.file}" ]] && sourced=1 || sourced=0
zsh (verificado en 5.0.5) - asegúrese de llamar esto fuera de una función
[[ $ZSH_EVAL_CONTEXT =~ :file$ ]] && sourced=1 || sourced=0
cross-shell (bash, ksh, zsh)
([[ -n $ZSH_EVAL_CONTEXT && $ZSH_EVAL_CONTEXT =~ :file$ ]] || [[ -n $KSH_VERSION && $(cd "$(dirname -- "$0")" && printf ''%s'' "${PWD%/}/")$(basename -- "$0") != "${.sh.file}" ]] || [[ -n $BASH_VERSION && $0 != "$BASH_SOURCE" ]]) && sourced=1 || sourced=0
[NO ROBUSTE] Usar solo las características POSIX : ver abajo
Explicación:
intento
[[ "$0" != "$BASH_SOURCE" ]] && sourced=1 || sourced=0
-
$BASH_SOURCE
... SIEMPRE contiene el argumento del archivo de script , ya sea de fuente o no. -
$0
...- si NO tiene origen: siempre idéntico a
$BASH_SOURCE
- SI ESTÁ FUENTE:
- si proviene de otra secuencia de comandos: la secuencia de comandos a partir de la cual se origina la secuencia de comandos.
- si proviene de forma interactiva:
- Por lo general,
bash
para un shell que no es de inicio de sesión, y-bash
para un shell de inicio de sesión (como en OSX) o, si Bash se invocó comosh
, análogamentesh
o-sh
. - Sin embargo, si Bash se inició (directamente) con una ruta relativa o absoluta, esa ruta se reflejará en
$0
. - También tenga en cuenta que es posible ejecutar Bash (o cualquier programa) con un valor arbitrario de
$0
, como por ejemplo, utilizando el comando incorporado delexec
con la opción-a
.
- Por lo general,
- si NO tiene origen: siempre idéntico a
ksh
[[ /
$(cd "$(dirname -- "$0")" && printf ''%s'' "${PWD%/}/")$(basename -- "$0") != /
"${.sh.file}" /
]] &&
sourced=1 || sourced=0
La variable especial ${.sh.file}
es algo análoga a $BASH_SOURCE
; tenga en cuenta que ${.sh.file}
causa un error de sintaxis en bash, zsh y dash, por lo tanto, asegúrese de ejecutarlo condicionalmente en scripts de múltiples capas.
A diferencia de bash, NO se garantiza que $0
y ${.sh.file}
sean exactamente idénticos en el caso sin origen, ya que $0
puede ser una ruta relativa , mientras que ${.sh.file}
siempre es una ruta completa , por lo que $0
debe resolverse en una ruta completa antes de comparar.
zsh
[[ $ZSH_EVAL_CONTEXT =~ :file$ ]] && sourced=1 || sourced=0
$ZSH_EVAL_CONTEXT
contiene información sobre el contexto de evaluación: llame esto fuera de una función. Dentro de un alcance de nivel superior del script de origen, $ZSH_EVAL_CONTEXT
termina con :file
.
Advertencia: dentro de una sustitución de comando, zsh agrega :cmdsubst
, así que pruebe $ZSH_EVAL_CONTEXT
para :file:cmdsubst$
there.
Usando solo las características de POSIX
Si está dispuesto a hacer ciertas suposiciones, puede hacer una suposición razonable, pero no a prueba de tontos, acerca de si su script se está obteniendo de fuentes basadas en el conocimiento de los nombres de archivo de las shells que pueden estar ejecutando su script .
Notablemente, esto significa que este enfoque falla si su secuencia de comandos está siendo originada por otra secuencia de comandos .
La sección "Cómo manejar las invocaciones de fuentes" en esta respuesta mía discute los casos extremos que no se pueden manejar con las características de POSIX solo en detalle.
Esto se basa en el comportamiento estándar de $0
, que zsh
, por ejemplo, no muestra.
Por lo tanto, el enfoque más seguro es combinar los métodos robustos, específicos de la capa superior, con una solución alternativa para todas las capas restantes.
Consejo del sombrero para Stéphane Desneux y su respuesta para la inspiración (transformando mi expresión de enunciado cross-shell en una declaración if
compatible con sh
y agregando un manejador para otras shells).
sourced=0
if [ -n "$ZSH_EVAL_CONTEXT" ]; then
case $ZSH_EVAL_CONTEXT in *:file) sourced=1;; esac
elif [ -n "$KSH_VERSION" ]; then
[ "$(cd $(dirname -- $0) && pwd -P)/$(basename -- $0)" != "$(cd $(dirname -- ${.sh.file}) && pwd -P)/$(basename -- ${.sh.file})" ] && sourced=1
elif [ -n "$BASH_VERSION" ]; then
[ "$0" != "$BASH_SOURCE" ] && sourced=1
else # All other shells: examine $0 for known shell binary filenames
# Detects `sh` and `dash`; add additional shell filenames as needed.
case ${0##*/} in sh|dash) sourced=1;; esac
fi
TL; DR
Intenta ejecutar una declaración de return
. Si el script no tiene origen, generará un error. Puede detectar ese error y proceder como lo necesite.
Pon esto en un archivo y llámalo, por ejemplo, test.sh:
#!/usr/bin/env sh
# Try to execute a `return` statement,
# but do it in a sub-shell and catch the results.
# If this script isn''t sourced, that will raise an error.
$(return >/dev/null 2>&1)
# What exit code did that give?
if [ "$?" -eq "0" ]
then
echo "This script is sourced."
else
echo "This script is not sourced."
fi
Ejecútelo directamente:
shell-prompt> sh test.sh
output: This script is not sourced.
Fuente:
shell-prompt> source test.sh
output: This script is sourced.
Para mí, esto funciona en zsh y bash.
Explicación
La declaración de return
generará un error si intenta ejecutarlo fuera de una función o si el script no tiene origen. Pruebe esto desde un intérprete de comandos de shell:
shell-prompt> return
output: ...can only `return` from a function or sourced script
No necesita ver ese mensaje de error, por lo que puede redirigir la salida a dev / null:
shell-prompt> return >/dev/null 2>&1
Ahora mira el código de salida. 0 significa OK (no se produjo ningún error), 1 significa que ocurrió un error:
shell-prompt> echo $?
output: 1
También desea ejecutar la declaración de return
dentro de un subconjunto. Cuando la declaración de return
ejecuta. . . bien . . . devoluciones. Si lo ejecuta en un subconjunto, saldrá de ese subconjunto, en lugar de volver a salir de su secuencia de comandos. Para ejecutar en el subconjunto, envuélvelo en $(...)
:
shell-prompt> $(return >/dev/null 2>$1)
Ahora, puede ver el código de salida del subconjunto, que debería ser 1, porque se generó un error dentro del subconjunto:
shell-prompt> echo $?
output: 1
Seguí la expresión compacta de mklement0 .
Eso está bien, pero noté que puede fallar en el caso de ksh cuando se invoca así:
/bin/ksh -c ./myscript.sh
(cree que es de origen y no es porque ejecuta una subshell) Pero la expresión funcionará para detectar esto:
/bin/ksh ./myscript.sh
Además, incluso si la expresión es compacta, la sintaxis no es compatible con todas las shells.
Así que terminé con el siguiente código, que funciona para bash, zsh, dash y ksh
SOURCED=0
if [ -n "$ZSH_EVAL_CONTEXT" ]; then
[[ $ZSH_EVAL_CONTEXT =~ :file$ ]] && SOURCED=1
elif [ -n "$KSH_VERSION" ]; then
[[ "$(cd $(dirname -- $0) && pwd -P)/$(basename -- $0)" != "$(cd $(dirname -- ${.sh.file}) && pwd -P)/$(basename -- ${.sh.file})" ]] && SOURCED=1
elif [ -n "$BASH_VERSION" ]; then
[[ $0 != "$BASH_SOURCE" ]] && SOURCED=1
elif grep -q dash /proc/$$/cmdline; then
case $0 in *dash*) SOURCED=1 ;; esac
fi
Siéntase libre de agregar soporte de conchas exóticas :)