Cómo detectar si un script está siendo originado

Tengo un script en el que no quiero que llame a exit si se está obteniendo.

Pensé en comprobar si $0 == bash pero esto tiene problemas si el script proviene de otro script, o si el usuario lo obtiene desde un shell diferente como ksh .

¿Hay alguna forma confiable de detectar si un script se está obteniendo?

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

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 ..." 

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 bashs:

 #!/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 .

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 
  • cross-shell, incluidas las shells POSIX-features-only ( dash , sh ) ; no es un trazador de líneas único (tubería individual) por razones técnicas y no es totalmente robusto (ver abajo):

     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 

Explicación:


bash

 [[ "$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ó como sh , análogamente sh 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 progtwig) con un valor arbitrario de $0 , como por ejemplo, utilizando el comando incorporado del exec con la opción -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 syntax 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 scope 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 fonts 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 fonts” 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 statement 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 

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 (stack) sea más profunda. ( FUNCNAME es una variable de matriz de bash especial, debe tener índices contiguos correspondientes a la stack 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 en FUNCNAME[] 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 a pdksh , 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 …

TL; DR

Intenta ejecutar una statement 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 statement 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 statement de return dentro de un subconjunto. Cuando la statement 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 

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.

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 llama main .
  • Si proviene de bash , main solo se llama, si el script de llamada tiene el mismo nombre. (Por ejemplo, si se fonts 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 a main .
  • Si se evalúa por bash con eval ( eval "`cat script`" todas las comillas son importantes! ) Que no provienen directamente de la línea de comandos, esto se llama main . Para todas las demás variantes de eval , main no se llama.

  • Si no se llama main , devuelve true ( $?=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 es bash

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 ejecuta bash 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.

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.

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 

$_ 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 .

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 syntax 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 🙂

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.

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

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 

Terminé con la comprobación de [[ $_ == "$(type -p "$0")" ]]

 if [[ $_ == "$(type -p "$0")" ]]; then echo I am invoked from a sub shell else echo I am invoked from a source command fi 

Cuando use curl ... | bash -s -- ARGS curl ... | bash -s -- ARGS para ejecutar un script remoto sobre la marcha, el $ 0 será simplemente bash lugar de normal /bin/bash cuando ejecute el archivo de script real, entonces uso el type -p "$0" para mostrar la ruta completa de bash .

prueba:

 curl -sSL https://github.com/jjqq2013/bash-scripts/raw/master/common/relpath | bash -s -- /a/b/c/d/e /a/b/CC/DD/EE source <(curl -sSL https://github.com/jjqq2013/bash-scripts/raw/master/common/relpath) relpath /a/b/c/d/e /a/b/CC/DD/EE wget https://github.com/jjqq2013/bash-scripts/raw/master/common/relpath chmod +x relpath ./relpath /a/b/c/d/e /a/b/CC/DD/EE