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.

Né -efné BASH_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.