L'unica grande differenza è tra l'approvvigionamento e l'esecuzione di uno script. source foo.sh
lo procurerà e tutti gli altri esempi che mostri sono in esecuzione. Più in dettaglio:
./file.sh
Questo eseguirà uno script chiamato file.sh
che si trova nella directory corrente ( ./
). Normalmente, quando si esegue command
, la shell cercherà tra le directory in $PATH
un file eseguibile chiamato command
. Se si fornisce un percorso completo, come /usr/bin/command
o ./command
, quindi $PATH
viene ignorato e viene eseguito quel file specifico.
../file.sh
Questo è fondamentalmente lo stesso di ./file.sh
tranne che invece di cercare nella directory corrente file.sh
, cerca nella directory parent ( ../
).
sh file.sh
Questo equivalente a sh ./file.sh
, come sopra, eseguirà lo script chiamato file.sh
nella directory corrente. La differenza è che lo stai eseguendo esplicitamente con la sh
shell. Sui sistemi Ubuntu, questo è dash
e non bash
. Di solito, gli script hanno una riga shebang che fornisce al programma in cui devono essere eseguiti. Chiamandoli con uno diverso si annulla quello. Per esempio:
$ cat foo.sh
#!/bin/bash
## The above is the shebang line, it points to bash
ps h -p $$ -o args='' | cut -f1 -d' ' ## This will print the name of the shell
Lo script stamperà semplicemente il nome della shell utilizzata per eseguirlo. Vediamo cosa restituisce quando viene chiamato in diversi modi:
$ bash foo.sh
bash
$ sh foo.sh
sh
$ zsh foo.sh
zsh
Quindi, chiamare chiamando uno script con shell script
sovrascriverà la linea shebang (se presente) ed eseguirà lo script con qualunque shell tu gli dica.
source file.sh
o . file.sh
Questo si chiama, abbastanza sorprendentemente, approvvigionamento della sceneggiatura. La parola chiave source
è un alias del .
comando incorporato shell . Questo è un modo per eseguire lo script all'interno della shell corrente. Normalmente, quando uno script viene eseguito, viene eseguito nella sua shell che è diversa da quella corrente. Illustrare:
$ cat foo.sh
#!/bin/bash
foo="Script"
echo "Foo (script) is $foo"
Ora, se imposto la variabile foo
su qualcos'altro nella shell genitore e quindi eseguo lo script, lo script stamperà un valore diverso di foo
(perché è anche impostato all'interno dello script) ma il valore di foo
nella shell genitore rimarrà invariato:
$ foo="Parent"
$ bash foo.sh
Foo (script) is Script ## This is the value from the script's shell
$ echo "$foo"
Parent ## The value in the parent shell is unchanged
Tuttavia, se provo lo script invece di eseguirlo, verrà eseguito nella stessa shell, quindi il valore di foo
nel genitore verrà modificato:
$ source ./foo.sh
Foo (script) is Script ## The script's foo
$ echo "$foo"
Script ## Because the script was sourced,
## the value in the parent shell has changed
Pertanto, il sourcing viene utilizzato nei pochi casi in cui si desidera che uno script influisca sulla shell da cui lo si esegue. In genere viene utilizzato per definire le variabili della shell e renderle disponibili al termine dello script.
Tenendo presente tutto ciò, il motivo per cui ottieni risposte diverse è, innanzitutto, che la tua sceneggiatura non fa ciò che pensi che faccia. Conta il numero di volte che bash
appare nell'output di ps
. Questo non è il numero di terminali aperti , è il numero di shell in esecuzione (in realtà, non è nemmeno quello, ma questa è un'altra discussione). Per chiarire, ho semplificato un po 'la tua sceneggiatura a questo:
#!/bin/bash
logname=terdon
not=`ps -au$logname | grep -c bash`
echo "The number of shells opened by $logname is $not"
Ed eseguilo in vari modi con un solo terminale aperto:
Lancio diretto, ./foo.sh
.
$ ./foo.sh
The number of shells opened by terdon is 1
Qui, stai usando la linea shebang. Ciò significa che lo script viene eseguito direttamente da qualsiasi cosa sia impostata lì. Ciò influisce sul modo in cui lo script viene mostrato nell'output di ps
. Invece di essere elencato come bash foo.sh
, verrà mostrato solo come foo.sh
ciò significa che grep
ti mancherà. In realtà ci sono 3 istanze di bash in esecuzione: il processo parent, il bash che esegue lo script e un altro che esegue il ps
comando . Quest'ultimo è importante, l'avvio di un comando con sostituzione del comando ( `command`
o $(command)
) comporta l'avvio di una copia della shell padre e l'esecuzione del comando. Qui, tuttavia, nessuno di questi è mostrato a causa del modo in cui ps
mostra il suo output.
Avvio diretto con shell esplicita (bash)
$ bash foo.sh
The number of shells opened by terdon is 3
Qui, poiché stai funzionando con bash foo.sh
, l'output di ps
verrà mostrato bash foo.sh
e conteggiato. Quindi, qui abbiamo il processo genitore, l' bash
esecuzione dello script e la shell clonata (in esecuzione ps
) tutti mostrati perché ora ps
li mostrerà ciascuno perché il tuo comando includerà la parola bash
.
Avvio diretto con una shell diversa ( sh
)
$ sh foo.sh
The number of shells opened by terdon is 1
Questo è diverso perché stai eseguendo lo script con sh
e non bash
. Pertanto, l'unica bash
istanza è la shell padre in cui è stato avviato lo script. sh
Invece, vengono eseguite tutte le altre shell menzionate sopra .
Sourcing (sia .
o source
, stessa cosa)
$ . ./foo.sh
The number of shells opened by terdon is 2
Come ho spiegato sopra, l'approvvigionamento di uno script provoca l'esecuzione nella stessa shell del processo padre. Tuttavia, viene avviata una subshell separata per avviare il ps
comando e questo porta il totale a due.
Come nota finale, il modo corretto di contare i processi in esecuzione non è analizzare ps
ma utilizzare pgrep
. Tutti questi problemi sarebbero stati evitati se avessi appena corso
pgrep -cu terdon bash
Quindi, una versione funzionante del tuo script che stampa sempre il numero giusto è (nota l'assenza di sostituzione del comando):
#!/usr/bin/env bash
user="terdon"
printf "Open shells:"
pgrep -cu "$user" bash
Ciò restituirà 1 quando viene fornito e 2 (perché verrà lanciato un nuovo bash per eseguire lo script) per tutti gli altri modi di avvio. Restituirà comunque 1 all'avvio sh
poiché il processo figlio non lo è bash
.