Diversi modi per eseguire uno script di shell


44

Esistono diversi modi per eseguire uno script, quelli che conosco sono:

/path/to/script # using the path (absolute or relative)
. script        # using the . (dot)
source script   # using the `source` command

Più di questo? Quali sono le differenze tra loro? Ci sono situazioni che devo usare una e non un'altra?


Ottimo da sapere, grazie alla tua domanda e risposte di seguito, in particolare di Shawn. Vorrei aggiungere qualcosa che non mi era molto evidente fino a quando non ho eseguito alcuni test. L'inclusione di un "/" nel secondo modo sopra porterà il comando alla modalità 1 sopra. Cioè, mentre "./myscript.sh" segue la modalità 1, ". Myscript.sh" si attacca alla modalità 2. Hai menzionato "usando il percorso (assoluto o relativo)", ma volevi solo renderlo evidente.
Arun,

Risposte:


32

Un altro modo è quello di chiamare l'interprete e passandogli il percorso dello script:

/bin/sh /path/to/script

Il punto e la sorgente sono equivalenti. (EDIT: no, non lo sono: come sottolinea KeithB in un commento su un'altra risposta, "." Funziona solo con shell correlate a bash, dove "source" funziona con shell sia bash che csh.) Esegue lo script in -place (come se avessi copiato e incollato lo script proprio lì). Ciò significa che restano tutte le funzioni e le variabili non locali nello script. Significa anche se lo script fa un cd in una directory, sarai ancora lì quando avrà finito.

Gli altri modi di eseguire uno script lo eseguiranno nella sua stessa shell secondaria. Le variabili nello script non sono ancora vive al termine. Se lo script ha cambiato directory, non influisce sull'ambiente chiamante.

/ path / to / script e / bin / sh script sono leggermente diversi. In genere, una sceneggiatura all'inizio ha uno "shebang" che assomiglia a questo:

#! /bin/bash

Questo è il percorso dell'interprete di script. Se specifica un interprete diverso da quello che fai quando lo esegui, allora potrebbe comportarsi diversamente (o potrebbe non funzionare affatto).

Ad esempio, gli script Perl e gli script Ruby iniziano con (rispettivamente):

#! /bin/perl

e

#! /bin/ruby

Se esegui uno di questi script eseguendolo /bin/sh script, non funzioneranno affatto.

Ubuntu in realtà non usa la shell bash, ma molto simile chiamata dash. Gli script che richiedono bash possono funzionare in modo leggermente errato quando vengono chiamati facendo /bin/sh scriptperché hai appena chiamato uno script bash utilizzando l'interprete trattino.

Un'altra piccola differenza tra la chiamata diretta dello script e il passaggio del percorso dello script all'interprete è che lo script deve essere contrassegnato come eseguibile per eseguirlo direttamente, ma non per eseguirlo passando il percorso all'interprete.

Un'altra variante minore: puoi aggiungere un prefisso a uno di questi modi per eseguire uno script con eval, quindi puoi avere

eval sh script
eval script
eval . script

e così via. In realtà non cambia nulla, ma ho pensato di includerlo per completezza.


6
Dire "Ubuntu in realtà non usa la shell bash" è impreciso e tecnicamente errato. Ubuntu fa usare la shell bash, il punto è che shcorrisponde dash, ma non bash.
Faheem Mitha,

@Shawn Nel primo paragrafo che hai scritto "Esegue lo script sul posto (come se avessi copiato e incollato lo script proprio lì). Ciò significa che restano tutte le funzioni e le variabili non locali nello script." cosa intendi con la seconda riga qui? Puoi per favore spiegare.
Geek,

@Geek quando esegui uno script come processo figlio (il modo normale), tutte le variabili e le funzioni che definisce (è l'ambiente) scompaiono quando termina il processo. Se si genera lo script, tali variabili e funzioni vengono create nell'ambiente corrente; al termine dello script, rimangono le modifiche all'ambiente.
Shawn J. Goff,

@ ShawnJ.Goff grazie per il chiarimento. +1.
Geek,

Trama: Bourne Shell (sh) accetta solo punti - non sorgente in quanto è un bash incorporato. pubs.opengroup.org/onlinepubs/9699919799/utilities/… Quindi direi che il modo più portatile è un punto.
dezza,

9

Molte persone eseguono il debug degli script della shell aggiungendo i seguenti flag di debug allo script:

set -x     # Print command traces before executing command.
set -v     # Prints shell input lines as they are read.
set -xv    # Or do both

Ciò significa che è necessario aprire il file con un editor (supponendo che si disponga delle autorizzazioni per modificare il file), aggiungere una riga come set -x, salvare il file, quindi eseguire il file. Quindi quando hai finito devi seguire gli stessi passaggi e rimuovere il set -x, ecc. Ecc. Questo può essere noioso.

Invece di fare tutto ciò, è possibile impostare i flag di debug sulla riga di comando:

$ bash -x ~/bin/ducks
+ du -cks -x dir1 dir2 dir3 file1 file2 file3
+ sort -n
+ tail .ducks
123 etc
424 bin
796 total



$ sh -xv ~/bin/ducks  
#!/usr/bin/env bash

# Find the disk hog
# Borrowed from http://oreilly.com/pub/h/15
...
...

2
Un suggerimento correlato: ho preso l'abitudine di mettere emulate sh 2>/dev/nullin cima ai miei script di shell. Quando eseguito con zsh, questo lo mette in modalità compatibile POSIX. Quando eseguito con altre shell, la linea non ha alcun effetto. Quindi posso eseguire lo script con zsh -x /path/to/script. Mi piace zsh qui perché fornisce tracce migliori di bash o ksh.
Gilles 'SO- smetti di essere malvagio' il

7

Shawn J. Goff ha fatto molti buoni punti, ma non ha incluso l'intera storia:

Ubuntu in realtà non usa la shell bash, ma molto simile chiamata dash. Gli script che richiedono bash possono funzionare in modo leggermente errato quando vengono chiamati facendo /bin/shscript perché hai appena chiamato uno script bash utilizzando l'interprete trattino.

Molti script di sistema (come in init.d, in / etc e così via) hanno un shebang #!/bin/sh, ma /bin/shin realtà è un collegamento simbolico a un'altra shell - in passato /bin/bash, al giorno d'oggi /bin/dash. Ma quando uno di essi viene invocato come /bin/sh, si comportano in modo diverso, cioè si attaccano alla modalità di compatibilità POSIX.

Come lo fanno? Bene, controllano come sono stati invocati.

Uno stesso shellscript può testare come è stato invocato e fare cose diverse, a seconda di ciò? Sì, può. Quindi il modo in cui lo invochi può sempre portare a risultati diversi, ma ovviamente è raro che ti infastidisca. :)

Come regola empirica: se stai imparando una shell specifica come bash e scrivi comandi da un tutorial di bash, inserisci #!/bin/bashil titolo, non #!/bin/sh, se non diversamente indicato. Altrimenti i tuoi comandi potrebbero non riuscire. E se non hai scritto tu stesso uno script, invocalo direttamente ( ./foo.sh, bar/foo.sh) invece di indovinare una shell ( sh foo.sh, sh bar/foo.sh). Lo shebang dovrebbe invocare il guscio giusto.

E qui ci sono altri due tipi di invocazione:

cat foo.sh | dash
dash < foo.sh

5

.e sourcesono equivalenti in quanto non generano un sottoprocesso ma eseguono comandi nella shell corrente. Questo è importante quando lo script imposta le variabili di ambiente o cambia la directory di lavoro corrente.

Usando il percorso o dandolo a /bin/shcrea un nuovo processo in cui vengono eseguiti i comandi.


2
sh script
bash script

Sto riflettendo se ci sono più ...

.e sourcesono uguali. Dopo l'esecuzione, qualsiasi modifica dell'ambiente scriptverrà mantenuta. Di solito, sarebbe usato per creare una libreria Bash, quindi la libreria può essere riutilizzata in molti script diversi.

Inoltre è un buon modo per mantenere la directory corrente. Se cambi directory nello script, non verrà applicato nella shell su cui esegui quello script. Ma se lo si esegue per eseguirlo, dopo la chiusura dello script, la directory corrente verrà mantenuta.


2
.funziona solo in sh / bash e shell correlate. sourcefunziona anche in csh e shell correlate.
KeithB

1

" Userland exec " conta come un modo diverso? Userland exec carica il codice e lo fa eseguire senza l'uso di una chiamata di sistema execve ().


1
. ./filename
# ( dot space dot slash filename )

Esegue lo script nella shell corrente quando la directory non si trova nel percorso.


1

. e source sono almeno un po 'diversi in zsh (è quello che uso) perché

source file

Works, mentre

. file

non ha bisogno

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