Der einzige wesentliche Unterschied besteht in der Beschaffung und Ausführung eines Skripts. source foo.sh
Es wird als Quelle verwendet, und alle anderen von Ihnen gezeigten Beispiele werden ausgeführt. Ausführlicher:
./file.sh
Dadurch wird ein Skript ausgeführt, file.sh
das im aktuellen Verzeichnis ( ./
) gespeichert ist . Normalerweise command
sucht die Shell beim Ausführen in den Verzeichnissen $PATH
nach einer ausführbaren Datei mit dem Namen command
. Wenn Sie einen vollständigen Pfad geben, wie /usr/bin/command
oder ./command
, dann die $PATH
ignoriert wird und dass bestimmte Datei ausgeführt wird .
../file.sh
Dies ist im Grunde das Gleiche wie, mit der ./file.sh
Ausnahme, dass statt im aktuellen Verzeichnis nach file.sh
gesucht wird, im übergeordneten Verzeichnis gesucht wird ( ../
).
sh file.sh
Dies entspricht sh ./file.sh
, wie oben, der Ausführung des file.sh
im aktuellen Verzeichnis aufgerufenen Skripts . Der Unterschied besteht darin, dass Sie es explizit mit der sh
Shell ausführen. Auf Ubuntu-Systemen ist das dash
und nicht bash
. Normalerweise haben Skripte eine Shebang-Zeile , die das Programm angibt , unter dem sie ausgeführt werden sollen. Wenn Sie sie mit einem anderen anrufen, wird dies überschrieben. Beispielsweise:
$ 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
Dieses Skript gibt einfach den Namen der Shell aus, mit der es ausgeführt wurde. Mal sehen, was es zurückgibt, wenn es auf verschiedene Arten aufgerufen wird:
$ bash foo.sh
bash
$ sh foo.sh
sh
$ zsh foo.sh
zsh
shell script
Wenn Sie also ein Skript mit aufrufen, wird die shebang-Zeile (falls vorhanden) überschrieben und das Skript mit der von Ihnen angegebenen Shell ausgeführt.
source file.sh
oder . file.sh
Dies wird überraschenderweise als Beschaffung des Skripts bezeichnet. Das Schlüsselwort source
ist ein Alias für den Shell- .
Befehl builtin . Auf diese Weise können Sie das Skript in der aktuellen Shell ausführen. Wenn ein Skript ausgeführt wird, wird es normalerweise in einer eigenen Shell ausgeführt, die sich von der aktuellen unterscheidet. Um zu veranschaulichen:
$ cat foo.sh
#!/bin/bash
foo="Script"
echo "Foo (script) is $foo"
Wenn ich nun die Variable foo
auf einen anderen Wert in der übergeordneten Shell setze und dann das Skript ausführe, gibt das Skript einen anderen Wert aus foo
(da er auch im Skript festgelegt ist), aber der Wert foo
in der übergeordneten Shell bleibt unverändert:
$ 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
Wenn ich das Skript jedoch als Quelle verwende, anstatt es auszuführen, wird es in derselben Shell ausgeführt, sodass der Wert foo
im übergeordneten Element geändert wird:
$ 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
Daher wird Sourcing in den wenigen Fällen verwendet, in denen ein Skript die Shell beeinflussen soll, von der aus Sie es ausführen. Es wird normalerweise verwendet, um Shell-Variablen zu definieren und nach Abschluss des Skripts verfügbar zu machen.
Der Grund, warum Sie unterschiedliche Antworten erhalten, ist zuallererst, dass Ihr Skript nicht das tut, was Sie denken, dass es es tut. Es zählt, wie oft bash
in der Ausgabe von angezeigt wird ps
. Dies ist nicht die Anzahl der offenen Terminals , sondern die Anzahl der laufenden Shells (tatsächlich ist es nicht einmal das, aber das ist eine andere Diskussion). Zur Verdeutlichung habe ich Ihr Skript ein wenig vereinfacht:
#!/bin/bash
logname=terdon
not=`ps -au$logname | grep -c bash`
echo "The number of shells opened by $logname is $not"
Führen Sie es auf verschiedene Arten aus, wobei nur ein einziges Terminal geöffnet ist:
Direkter Start, ./foo.sh
.
$ ./foo.sh
The number of shells opened by terdon is 1
Hier verwenden Sie die Shebang-Linie. Dies bedeutet, dass das Skript direkt von dem ausgeführt wird, was dort eingestellt ist. Dies wirkt sich darauf aus, wie das Skript in der Ausgabe von angezeigt wird ps
. Anstatt als aufgeführt zu werden bash foo.sh
, wird nur angezeigt, foo.sh
was bedeutet, dass Sie grep
es verpassen werden. Es werden tatsächlich 3 Bash-Instanzen ausgeführt: der übergeordnete Prozess, die Bash, die das Skript ausführt, und eine andere, die den ps
Befehl ausführt . Letzteres ist wichtig, da beim Starten eines Befehls mit Befehlsersetzung ( `command`
oder $(command)
) eine Kopie der übergeordneten Shell gestartet wird, die den Befehl ausführt. Hier wird jedoch aufgrund der Art und Weise, in der ps
die Ausgabe erfolgt , keine davon angezeigt .
Direktes Starten mit expliziter (bash) Shell
$ bash foo.sh
The number of shells opened by terdon is 3
Hier wird bash foo.sh
die Ausgabe von ps
angezeigt bash foo.sh
und gezählt , weil Sie mit ausgeführt werden. Hier haben wir also den übergeordneten Prozess, das bash
Ausführen des Skripts und die geklonte Shell (Ausführen der ps
), die alle angezeigt werden, da jetzt ps
jeder von ihnen angezeigt wird, da Ihr Befehl das Wort enthält bash
.
Direktes Starten mit einer anderen Shell ( sh
)
$ sh foo.sh
The number of shells opened by terdon is 1
Dies ist anders, da Sie das Skript mit sh
und nicht mit ausführen bash
. Daher ist die einzige bash
Instanz die übergeordnete Shell, in der Sie Ihr Skript gestartet haben. Alle anderen oben genannten Shells werden sh
stattdessen von ausgeführt.
Beschaffung (entweder durch .
oder source
dasselbe)
$ . ./foo.sh
The number of shells opened by terdon is 2
Wie ich oben erklärt habe, wird ein Skript bei der Beschaffung in derselben Shell wie der übergeordnete Prozess ausgeführt. Es wird jedoch eine separate Subshell gestartet, um den ps
Befehl zu starten. Dadurch wird die Summe auf zwei erhöht .
Als letzte Anmerkung ist die richtige Art, laufende Prozesse zu zählen, nicht zu analysieren, ps
sondern zu verwenden pgrep
. All diese Probleme wären vermieden worden, wenn Sie nur gelaufen wären
pgrep -cu terdon bash
Eine funktionierende Version Ihres Skripts, die immer die richtige Nummer ausgibt, ist (beachten Sie, dass keine Befehlsersetzung erfolgt):
#!/usr/bin/env bash
user="terdon"
printf "Open shells:"
pgrep -cu "$user" bash
Das gibt 1 zurück, wenn es bezogen wird, und 2 (da eine neue Bash gestartet wird, um das Skript auszuführen) für alle anderen Arten des Startens. Beim Starten mit wird immer noch 1 zurückgegeben, sh
da dies beim untergeordneten Prozess nicht der Fall ist bash
.