Come definire uno script di shell da cui provenire non eseguito


40

Sto definendo uno script di shell che un utente dovrebbe sourcepiuttosto che eseguire.

Esiste un modo convenzionale o intelligente per suggerire all'utente che è così, ad esempio tramite un'estensione di file?

Esiste un codice shell che posso scrivere nel file stesso, il che lo farà eco a un messaggio e si chiuderà se viene eseguito invece di origine, in modo da poter aiutare l'utente a evitare questo errore evidente?


1
Quindi, se l'utente sta scrivendo uno script shell di una riga x, che contiene solo il comando . your-script-to-be-sourced, va bene, ma se vuole eseguirlo bash your-script-to-be-sourceddovrebbe essere proibito? Qual è il punto di questa restrizione?
user1934428,

8
@ user1934428 Certo. Questo è normale per uno script che calcola un numero di envvariabili e le lascia come output di fatto dello script. Un novizio rimarrà bloccato per giorni con il puzzle se gli permetti di eseguirlo.
Kubanczyk,

Risposte:


45

Supponendo che tu stia eseguendo bash, metti il ​​seguente codice vicino all'inizio dello script che vuoi ottenere ma non eseguito:

if [ "${BASH_SOURCE[0]}" -ef "$0" ]
then
    echo "Hey, you should source this script, not execute it!"
    exit 1
fi

Sotto bash, ${BASH_SOURCE[0]}conterrà il nome del file corrente che la shell sta leggendo, indipendentemente dal fatto che sia di provenienza o eseguita.

Al contrario, $0è il nome del file corrente in esecuzione.

-efverifica se questi due file sono lo stesso file. In tal caso, avvisiamo l'utente e usciamo.

-efBASH_SOURCEsono POSIX. Mentre -efè supportato da ksh, yash, zsh e Dash, BASH_SOURCErichiede bash. Inzsh , tuttavia, ${BASH_SOURCE[0]}potrebbe essere sostituito da ${(%):-%N}.


2
Semplicemente echo "Usage: source \"$myfile\""
kubanczyk,

6
@kubanczyk sourcenon è portatile. Considerando che questa risposta è specifica per Bash, non è poi così male, ma è una buona abitudine usare il portatile.
gronostaj

33

Un file non eseguibile può essere fornito ma non eseguito, quindi, come prima linea di difesa, non impostare il flag eseguibile dovrebbe essere un buon suggerimento ...

Modifica: trucco che mi sono appena imbattuto: fai in modo che lo shebang sia un eseguibile che non sia un interprete di shell, /bin/falsefa in modo che lo script restituisca un errore (rc! = 0)

#!/bin/false "This script should be sourced in a shell, not executed directly"

7
Tuttavia, un file non eseguibile può ancora essere eseguito tramite ad es. bash somefile.sh...
twalberg,

1
Se sai che puoi eseguirlo con bash(vd perl, python, awk ...), allora hai cercato la fonte e visto il commento che dice di non farlo :)
xenoid,

1
Gli script Perl sono generalmente nominati somefile.ple Python come somefile.py, quindi no, probabilmente non ho letto i commenti (che cosa sono quelli, comunque, comunque?) bash somefile.shchmod +x somefile.sh; ./somefile.sh
Ed

Anche alcune shell tipo Bourne, incluso bash, proveranno prima su execveun file, ma se ciò fallisce, ispezionano il file manualmente e lo interpretano manualmente #!e lo invocano attraverso quell'interprete: questo è un lascito dai giorni in cui #!era solo uno spazio utente convenzione, invece di essere gestito dal kernel stesso. Penso che bash , almeno, non lo farà per i file non eseguibili, ma non so se sia portatile aspettarsi un comportamento così sano da tutte le shell da cui l'utente potrebbe invocare lo script.
mtraceur,

"Se sai che puoi eseguirlo con bash" Uhm, no, a volte l'utente non ha idea che esistano altre shell bash e che bash script.shpossano essere pericolose.
Sergiy Kolodyazhnyy,

10

Ci sono diversi metodi suggeriti in questo post Stack Overflow , di cui mi è piaciuta quella basata sulle funzioni suggerita da Wirawan Purwanto e mr.spuratic best:

Il modo più robusto, come suggerito da Wirawan Purwanto, è controllare FUNCNAME[1] all'interno di una funzione :

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

Poi:

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

Ciò equivale a controllare l'output di caller, i valori maine sourcedistinguere il contesto del chiamante. L'utilizzo FUNCNAME[]consente di salvare l'acquisizione e l'analisi callerdell'output. Tuttavia, è necessario conoscere o calcolare la profondità della chiamata locale per essere corretti. Casi come uno script proveniente da un'altra funzione o script causano una maggiore profondità dell'array (stack). ( FUNCNAMEè una variabile di array bash speciale, dovrebbe avere indici contigui corrispondenti allo stack di chiamate, purché non lo sia mai unset.)

Quindi puoi aggiungere all'inizio dello script:

function check()
{
    if [[ ${FUNCNAME[-1]} != "source" ]]   # bash 4.2+, use ${FUNCNAME[@]: -1} for older
    then
        printf "Usage: source %s\n" "$0"
        exit 1
    fi
}
check

7

Supponendo che sia solo inutile, piuttosto che dannoso, eseguire lo script, è possibile aggiungere

return 0 || printf 'Must be sourced, not executed\n' >&2

fino alla fine della sceneggiatura. returnal di fuori di una funzione ha un codice di uscita diverso da zero a meno che il file non sia di provenienza.


3
Si noti che questo restituisce uno stato di uscita 0. Prova questo idioma simile che uso invece:return 2>/dev/null; echo "$0: This script must be sourced" 1>&2; exit 1
wjandrea,

5

Quando si genera uno script di shell, la riga shebang viene ignorata. Inserendo uno shebang non valido, è possibile avvisare l'utente che lo script è stato eseguito erroneamente:

#!/bin/bash source-this-script
# ...

Il messaggio di errore sarà questo:

/bin/bash: source-this-script: No such file or directory

Il nome dell'argomento (arbitrario) fornisce già un forte suggerimento, ma il messaggio di errore non è ancora chiaro al 100%. Possiamo risolvere questo problema con uno script di utilità source-this-scriptposizionato da qualche parte nel tuo PATH:

#!/bin/sh
echo >&2 "This script must be sourced, not executed${1:+: }${1:-!}"
exit 1

Ora, il messaggio di errore sarà questo:

This script must be sourced, not executed: path/to/script.sh

Confronto con altri approcci

Rispetto alle altre risposte, questo approccio richiede solo modifiche minime a ciascuno script (e avere una riga shebang aiuta con il rilevamento del tipo di file negli editor e specifica il dialetto dello script shell, quindi ci sono anche vantaggi). Il rovescio della medaglia è un messaggio di errore in qualche modo poco chiaro, o l'aggiunta (una tantum) di un altro script di shell.

bash path/to/script.shTuttavia, non impedisce l'invocazione esplicita tramite (grazie @muru!).


1
Un altro aspetto negativo è che questo non proteggerà contro bash some/script.sh, il che ignorerebbe anche lo shebang.
Muru,

4
Puoi rendere più chiaro il messaggio rendendo lo shebang #!/bin/echo 'You must source this script!'o qualcosa del genere.
Chris,

1
@ Chris: Giusto, ma perderei il rilevamento del tipo di file (ad esempio in Vim) e la documentazione di quale dialetto shell sia. Se non ti interessano questi, il tuo suggerimento eliminerebbe davvero la seconda sceneggiatura!
Ingo Karkat,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.