Como detectar se um script está sendo originado

Eu tenho um script em que não quero que ele chame exit se estiver sendo originado.

Eu pensei em verificar se $0 == bash mas isso tem problemas se o script é originado de outro script, ou se o usuário o origina de um shell diferente como o ksh .

Existe uma maneira confiável de detectar se um script está sendo originado?

    Isto parece ser portátil entre Bash e Korn:

     [[ $_ != $0 ]] && echo "Script is being sourced" || echo "Script is a subshell" 

    Uma linha semelhante a esta ou uma atribuição como `pathname =” $ _ “(com um teste e uma ação posteriores) deve estar na primeira linha do script ou na linha após o shebang (que, se usado, deve ser para ksh para que funcione na maioria das circunstâncias).

    Se sua versão do Bash souber sobre a variável de matriz BASH_SOURCE, tente 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 ..." 

    Depois de ler a resposta do @DennisWilliamson, há alguns problemas, veja abaixo:

    Como esta questão significa ksh e bash , há outra parte nesta resposta sobre ksh … veja abaixo.

    Maneira bash simples

     [ "$0" = "$BASH_SOURCE" ] 

    Vamos tentar (na mosca porque essa festa poderia ;-):

     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) 

    Eu uso a source vez disso . para legibilidade (como . é um alias para 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) 

    Observe que o número do processo não muda enquanto o processo se mantiver :

     echo $$ 29301 

    Por que não usar a comparação $_ == $0

    Para garantir muitos casos, começo a escrever um script verdadeiro :

     #!/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 isso para um arquivo chamado testscript :

     cat >testscript chmod +x testscript 

    Agora podemos testar:

     ./testscript proc: 25758[ppid:24890] is own (DW purpose: subshell) 

    Isso está ok.

     . ./testscript proc: 24890[ppid:24885] is sourced (DW purpose: sourced) source ./testscript proc: 24890[ppid:24885] is sourced (DW purpose: sourced) 

    Isso está ok.

    Mas, para testar um script antes de adicionar o sinalizador -x :

     bash ./testscript proc: 25776[ppid:24890] is own (DW purpose: sourced) 

    Ou para usar variables ​​pré-definidas:

     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) 

    Isso não funciona mais.

    Mover o comentário da quinta linha para a sexta forneceria uma resposta mais legível:

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

    Mais difícil: ksh agora ...

    Como eu não uso muito o ksh , depois de ler na man page, há as minhas tentativas:

     #!/bin/ksh set >/tmp/ksh-$$.log 

    Copie isso em um testfile.ksh :

     cat >testfile.ksh chmod +x testfile.ksh 

    Do que executar duas vezes:

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

    e veja:

     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 

    Há alguma variável herdada em uma corrida de origem , mas nada realmente relacionado ...

    Você pode até verificar que $SECONDS está perto de 0.000 , mas isso garante apenas casos de origem manual ...

    Você ainda pode tentar verificar o que é pai:

    Coloque isso no seu testfile.ksh :

     ps $PPID 

    Do 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 

    ou ps ho cmd $PPID , mas este trabalho apenas para um nível de subsessões ...

    Desculpe, não consegui encontrar uma maneira confiável de fazer isso, sob o ksh .

    Soluções robustas para bash , ksh , zsh , incluindo uma solução cross-shell , além de uma solução compatível com POSIX razoavelmente robusta :

    • Os números de versão indicados são aqueles nos quais a funcionalidade foi verificada – provavelmente, essas soluções também funcionam em versões anteriores – o feedback é bem-vindo .

    • Usando apenas resources POSIX (como em dash , que atua como /bin/sh no Ubuntu), não existe uma maneira robusta de determinar se um script está sendo gerado – veja a parte inferior desta resposta para obter a melhor aproximação .

    One-liners seguir – explicação abaixo; a versão cross-shell é complexa, mas deve funcionar de forma robusta:

    • bash (verificado no 3.57)

       [[ $0 != "$BASH_SOURCE" ]] && sourced=1 || sourced=0 
    • ksh (verificado em 93u +)

       [[ $(cd "$(dirname -- "$0")" && printf '%s' "${PWD%/}/")$(basename -- "$0") != "${.sh.file}" ]] && sourced=1 || sourced=0 
    • zsh (verificado em 5.0.5) – certifique-se de chamar isto fora de uma function

       [[ $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 
    • shell cruzado, incluindo shells somente POSIX-features ( dash , sh ) ; não um one-liner (single pipeline) por razões técnicas e não totalmente robusto (veja abaixo):

       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 

    Explicação:


    festança

     [[ "$0" != "$BASH_SOURCE" ]] && sourced=1 || sourced=0 
    • $BASH_SOURCE … ALWAYS contém o argumento do arquivo de script , seja originado ou não.
    • $0
      • se NÃO for originado: sempre idêntico a $BASH_SOURCE
      • SE FONTE:
        • se originado de outro script: o script do qual o script em questão está sendo originado.
        • se originado interativamente:
          • Normalmente, bash para um shell de não-login e -bash para um shell de login (como no OSX) ou, se o Bash foi invocado como sh , de forma análoga sh ou -sh .
          • No entanto, se o Bash foi iniciado (diretamente) com um caminho relativo ou absoluto, esse caminho será refletido em $0 .
          • Observe também que é possível lançar o Bash (ou qualquer programa) com um valor arbitrário para $0 , usando o exec builtin com a opção -a .

    ksh

     [[ \ $(cd "$(dirname -- "$0")" && printf '%s' "${PWD%/}/")$(basename -- "$0") != \ "${.sh.file}" \ ]] && sourced=1 || sourced=0 

    A variável especial ${.sh.file} é um pouco análoga a $BASH_SOURCE ; Observe que ${.sh.file} causa um erro de syntax no bash, zsh e dash, portanto, certifique-se de executá-lo condicionalmente em scripts multi-shell.

    Ao contrário do bash, $0 e ${.sh.file} NÃO têm garantia de ser exatamente idênticos no caso não originado, pois $0 pode ser um caminho relativo , enquanto ${.sh.file} é sempre um caminho completo , portanto $0 deve ser resolvido para um caminho completo antes de comparar.


    zsh

     [[ $ZSH_EVAL_CONTEXT =~ :file$ ]] && sourced=1 || sourced=0 

    $ZSH_EVAL_CONTEXT contém informações sobre o contexto de avaliação – chame isso fora de uma function. Dentro do escopo de nível superior de um script originado, $ZSH_EVAL_CONTEXT termina com :file .

    Advertência: Dentro de uma substituição de comando, zsh acrescenta :cmdsubst , então teste $ZSH_EVAL_CONTEXT para :file:cmdsubst$ there.


    Usando apenas resources POSIX

    Se você estiver disposto a fazer certas suposições, você pode fazer uma suposição razoável, mas não à prova de idiotas , sobre se o seu script está sendo originado, baseado em saber os nomes de arquivos dos shells que podem estar executando o seu script .
    Notavelmente, isso significa que essa abordagem falha se seu script estiver sendo originado por outro script .

    A seção “Como lidar com invocações originadas” nesta minha resposta discute os casos de borda que não podem ser manipulados com resources POSIX apenas em detalhes.

    Isso depende do comportamento padrão de $0 , que zsh , por exemplo, não exibe.

    Assim, a abordagem mais segura é combinar os methods robustos específicos de shell acima com uma solução de fallback para todos os shells restantes.

    Dica do chapéu para Stéphane Desneux e sua resposta para a inspiração (transformando minha expressão de instrução cross-shell em uma instrução sh -compatible if e adicionando um manipulador para outras 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 

    A resposta BASH_SOURCE[] (bash-3.0 e posterior) parece mais simples, embora BASH_SOURCE[] não esteja documentado a funcionar fora de um corpo de function (atualmente ele funciona, em desacordo com a página man).

    A maneira mais robusta, como sugerido por Wirawan Purwanto, é verificar FUNCNAME[1] dentro de uma function :

     function mycheck() { declare -p FUNCNAME; } mycheck 

    Então:

     $ bash sourcetest.sh declare -a FUNCNAME='([0]="mycheck" [1]="main")' $ . sourcetest.sh declare -a FUNCNAME='([0]="mycheck" [1]="source")' 

    Isso é o equivalente a verificar a saída do caller , os valores main e source distinguem o contexto do chamador. Usar FUNCNAME[] economiza a captura e a análise da saída do caller . Você precisa saber ou calcular a profundidade da sua chamada local para estar correto. Casos como um script sendo originado de outra function ou script farão com que a matriz (pilha) seja mais profunda. ( FUNCNAME é uma variável especial do array bash, deve ter índices contíguos correspondentes à pilha de chamadas, desde que nunca seja unset .)

     function issourced() { [[ ${FUNCNAME[@]: -1} == "source" ]] } 

    (No bash-4.2 e posterior você pode usar o formulário mais simples ${FUNCNAME[-1]} para o último item da matriz. Melhorado e simplificado graças ao comentário de Dennis Williamson abaixo.)

    No entanto, o problema descrito é ” Eu tenho um script onde não quero que ele chame ‘exit’ se estiver sendo originado “. O idioma comum da bash para essa situação é:

     return 2>/dev/null || exit 

    Se o script estiver sendo originado, o return terminará o script originado e retornará ao chamador.

    Se o script estiver sendo executado, o return retornará um erro (redirecionado) e a exit encerrará o script normalmente. Tanto o return quanto a exit podem ter um código de saída, se necessário.

    Infelizmente, isso não funciona em ksh (pelo menos não na versão derivada da AT & T que tenho aqui), trata-se de return como equivalente a exit se invocado fora de uma function ou script de origem.

    Atualizado : O que você pode fazer nas versões contemporâneas do ksh é verificar a variável especial .sh.level que está definida para a profundidade da chamada da function. Para um script chamado, este será inicialmente indefinido, para um script de origem pontual será definido como 1.

     function issourced { [[ ${.sh.level} -eq 2 ]] } issourced && echo this script is sourced 

    Isso não é tão robusto quanto a versão bash, você deve invocar issourced() no arquivo que está testando no nível superior ou em uma profundidade de function conhecida.

    (Você também pode estar interessado neste código no github que usa uma function de disciplina ksh e algum truque de armadilha de debugging para emular o array bash FUNCNAME .)

    A resposta canônica aqui: http://mywiki.wooledge.org/BashFAQ/109 também oferece $- como outro indicador (embora imperfeito) do estado do shell.


    Notas:

    • é possível criar funções bash chamadas “main” e “source” ( sobrescrevendo o builtin ), esses nomes podem aparecer em FUNCNAME[] mas desde que apenas o último item do array seja testado, não há ambigüidade.
    • Eu não tenho uma boa resposta para pdksh . A coisa mais próxima que posso encontrar aplica-se apenas ao pdksh , onde cada fonte de um script abre um novo descritor de arquivo (começando com 10 para o script original). Quase certamente não é algo que você quer confiar …

    TL; DR

    Tente executar uma declaração de return . Se o script não for originado, isso gerará um erro. Você pode pegar esse erro e prosseguir conforme necessário.

    Coloque isso em um arquivo e chame-o 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 

    Execute diretamente:

     shell-prompt> sh test.sh output: This script is not sourced. 

    Fonte:

     shell-prompt> source test.sh output: This script is sourced. 

    Para mim, isso funciona em zsh e bash.

    Explicação

    A instrução de return gerará um erro se você tentar executá-lo fora de uma function ou se o script não for originado. Tente isso de um prompt de shell:

     shell-prompt> return output: ...can only `return` from a function or sourced script 

    Você não precisa ver essa mensagem de erro, portanto, você pode redirect a saída para dev / null:

     shell-prompt> return >/dev/null 2>&1 

    Agora verifique o código de saída. 0 significa OK (nenhum erro ocorreu), 1 significa que ocorreu um erro:

     shell-prompt> echo $? output: 1 

    Você também quer executar a declaração de return dentro de um sub-shell. Quando a declaração de return é executada. . . bem . . . retorna. Se você executá-lo em um sub-shell, ele retornará desse sub-shell, ao invés de retornar do seu script. Para executar no sub-shell, coloque-o em $(...) :

     shell-prompt> $(return >/dev/null 2>$1) 

    Agora, você pode ver o código de saída do sub-shell, que deve ser 1, porque foi gerado um erro dentro do sub-shell:

     shell-prompt> echo $? output: 1 

    Eu darei uma resposta específica da BASH. Korn shell, desculpe. Suponha que o nome do seu script seja include2.sh ; então faça uma function dentro do include2.sh chamado am_I_sourced . Aqui está minha versão demo do 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 

    Agora tente executá-lo de várias maneiras:

     ~/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 

    Então isso funciona sem exceção, e não está usando o material $_ frágil. Este truque usa o recurso de introspecção do BASH, ou seja, variables FUNCNAME e BASH_SOURCE ; veja a documentação deles na página de manual do bash.

    Apenas duas ressalvas:

    1) a chamada para am_I_called deve ocorrer no script de origem, mas não dentro de qualquer function, para que ${FUNCNAME[1]} retorne outra coisa. Sim … você poderia ter verificado ${FUNCNAME[2]} – mas você só dificulta a sua vida.

    2) A function am_I_called deve residir no script de origem se você quiser descobrir qual o nome do arquivo que está sendo incluído.

    Eu gostaria de sugerir uma pequena correção para a resposta muito útil de Dennis , para torná-la um pouco mais portátil, espero:

     [ "$_" != "$0" ] && echo "Script is being sourced" || echo "Script is a subshell" 

    porque [[ não é reconhecido pelo shell (um pouco retentivo anal da IMHO) compatível com POSIX do Debian, dash . Além disso, pode-se precisar das aspas para proteger contra nomes de arquivos contendo espaços, novamente no dito shell.

    FWIW, depois de ler todas as outras respostas, eu propus a seguinte solução para mim:

    Isso funciona para todos os scripts, que começam com #!/bin/bash mas também podem ser obtidos 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 receita de script tem as seguintes propriedades:

    • Se executado por bash , main é chamado.
    • Se originado por bash , main é chamado apenas, se o script de chamada tiver o mesmo nome. (Por exemplo, se ele se origina.)
    • Se originado por um shell diferente de bash , main não é chamado.
    • Se executado por um shell diferente de bash , main não é chamado.
    • Se avaliado pelo bash com eval ( eval "`cat script`" todas as aspas são importantes! ) Não vindo diretamente da linha de comando, isto chama main . Para todas as outras variantes do eval , main não é chamado.

    • Se main não é chamado, ele retorna true ( $?=0 ).

    • E não depende de comportamento não documentado que possa mudar.

    Assim, exceto em alguns casos improváveis, main é chamado apenas quando o script é executado da maneira usual. Normalmente, isso é o que você quer, especialmente porque falta um código difícil de entender.

    Como BASH_SOURCE não pode ser BASH_SOURCE no bash , mas em todos os outros shells, isso também captura o caso de borda onde BASH_SOURCE é definido como $0 .

    Note que é muito semelhante ao código Python:

     if __name__ == '__main__': main() 

    O que também impede a chamada de main , exceto para alguns casos de canto, já que você pode importar / carregar o script e fazer com que __name__='__main__'

    Por que eu acho que esta é uma boa maneira geral de resolver o desafio

    Se você tem algo, que pode ser originado por múltiplos shells, deve ser compatível. No entanto (leia as outras respostas), como não há maneira portátil de detectar a source , você deve alterar as regras .

    Ao aplicar que o script deve ser executado por /bin/bash , você faz exatamente isso.

    Isso resolve todos os casos, mas , nesse caso, o script não pode ser executado diretamente:

    • /bin/bash não está instalado ou é disfuncional (ou seja, em um ambiente de boot)
    • Se você canalizá-lo para um shell não bash como em curl https://example.com/script | $SHELL curl https://example.com/script | $SHELL onde $SHELL não é bash

    No entanto, não consigo pensar em nenhum motivo real em que você precise disso e também na capacidade de obter exatamente o mesmo script em paralelo! Normalmente você pode envolvê-lo, de modo que o script seja sempre originado. Em seguida, execute o main manualmente. Curtiu isso:

    • sh -c '. script && main'
    • echo 'eval "`curl https://example.com/script`" && main' | $SHELL

    Importante:

    O último executa main duas vezes, se $SHELL executar o bash e a linha não for executada a partir da linha de comando. (Mas eu realmente não consigo pensar em nenhuma razão pela qual você deveria usar isso em um script, exceto por enganar o código de propósito.)

    Nota

    Esta resposta não teria sido possível sem a ajuda de todas as outras respostas! Mesmo os errados – o que me fez postar isso.

    Isso funciona mais tarde no script e não depende da variável _:

     ## Check to make sure it is not sourced: Prog=myscript.sh if [ $(basename $0) = $Prog ]; then exit 1 # not sourced fi 

    ou

     [ $(basename $0) = $Prog ] && exit 

    $_ é bastante frágil. Você tem que verificar isso como a primeira coisa que você faz no script. E mesmo assim, não é garantido que contenha o nome do seu shell (se originado) ou o nome do script (se executado).

    Por exemplo, se o usuário definiu BASH_ENV , então, no topo de um script, $_ contém o nome do último comando executado no script BASH_ENV .

    A melhor maneira que encontrei é usar $0 assim:

     name="myscript.sh" main() { echo "Script was executed, running main..." } case "$0" in *$name) main "$@" ;; esac 

    Infelizmente, desta forma não funciona fora da checkbox em zsh devido à opção functionargzero fazendo mais do que o seu nome sugere, e sendo ativado por padrão.

    Para contornar isso, coloquei unsetopt functionargzero no meu .zshenv .

    Eu segui a expressão compacta do mklement0 .

    Isso é legal, mas notei que pode falhar no caso do ksh quando invocado como isto:

     /bin/ksh -c ./myscript.sh 

    (acha que é originado e não é porque ele executa um subshell) Mas a expressão funcionará para detectar isso:

     /bin/ksh ./myscript.sh 

    Além disso, mesmo se a expressão for compacta, a syntax não é compatível com todos os shells.

    Então terminei com o seguinte código, que funciona para bash, zsh, dash e 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 

    Sinta-se à vontade para adicionar suporte a conchas exóticas 🙂

    Eu não acho que haja alguma maneira portátil de fazer isso tanto no ksh quanto no bash. No bash você poderia detectá-lo usando a saída do caller , mas eu não acho que exista equivalente no ksh.

    Eu precisava de um one-liner que funcionasse no [mac, linux] com o bash.version> = 3 e nenhuma dessas respostas se encheckboxva na conta.

     [[ ${BASH_SOURCE[0]} = $0 ]] && main "$@" 

    Direto ao ponto: você deve avaliar se a variável “$ 0” é igual ao nome do seu Shell.

    Como isso:

     #!/bin/bash echo "First Parameter: $0" echo if [[ "$0" == "bash" ]] ; then echo "The script was sourced." else echo "The script WAS NOT sourced." fi 

    Via SHELL :

     $ bash check_source.sh First Parameter: check_source.sh The script WAS NOT sourced. 

    Via FONTE :

     $ source check_source.sh First Parameter: bash The script was sourced. 


    É muito difícil ter uma maneira 100% portátil de detectar se um script foi originado ou não.

    Quanto à minha experiência (7 anos com Shellscripting) , a única maneira segura (não depender de variables ​​de ambiente com PIDs e assim por diante, o que não é seguro devido ao fato de ser algo VARIÁVEL ), você deve:

    • estender as possibilidades do seu se
    • usando switch / case, se você quiser.

    Ambas as opções não podem ser dimensionadas automaticamente, mas é a maneira mais segura.


    Por exemplo:

    Quando você fonte um script através de uma session SSH, o valor retornado pela variável “$ 0” (quando usando fonte ), é -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 

    OU

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

    Acabei de verificar [[ $_ == "$(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 

    Quando usar curl ... | bash -s -- ARGS curl ... | bash -s -- ARGS para executar script remoto on-the-fly, o $ 0 será apenas bash vez de normal /bin/bash quando executar arquivo de script real, então eu uso o type -p "$0" para mostrar o caminho completo do bash .

    teste:

     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