Schauen Sie sich den Code an:
#!/bin/bash
read -p "Eneter 1 for UID and 2 for LOGNAME" choice
if [ $choice -eq 1 ]
then
read -p "Enter UID: " uid
logname=`cat /etc/passwd | grep $uid | cut -f1 -d:`
else
read -p "Enter Logname: " logname
fi
not=`ps -au$logname | grep -c bash`
echo "The number of terminals opened by $logname are $not"
Mit diesem Code wird die Anzahl der von einem Benutzer auf demselben PC geöffneten Terminals ermittelt. Jetzt sind zwei Benutzer angemeldet, nämlich x und y. Ich bin momentan als y angemeldet und in Benutzer x sind 3 Terminals geöffnet. Wenn ich diesen Code auf verschiedene Arten wie oben beschrieben ausführe, sind die Ergebnisse:
$ ./file.sh
The number of terminals opened by x are 3
$ bash file.sh
The number of terminals opened by x are 5
$ sh file.sh
The number of terminals opened by x are 3
$ source file.sh
The number of terminals opened by x are 4
$ . ./file.sh
The number of terminals opened by x are 4
Hinweis: Ich habe 1 und UID 1000 an alle diese ausführbaren Dateien übergeben.
Können Sie nun bitte die Unterschiede zwischen all diesen erklären?
command-line
bash
scripts
Ramana Reddy
quelle
quelle
Antworten:
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 . Normalerweisecommand
sucht die Shell beim Ausführen in den Verzeichnissen$PATH
nach einer ausführbaren Datei mit dem Namencommand
. 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 nachfile.sh
gesucht wird, im übergeordneten Verzeichnis gesucht wird (../
).sh file.sh
Dies entspricht
sh ./file.sh
, wie oben, der Ausführung desfile.sh
im aktuellen Verzeichnis aufgerufenen Skripts . Der Unterschied besteht darin, dass Sie es explizit mit dersh
Shell ausführen. Auf Ubuntu-Systemen ist dasdash
und nichtbash
. 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: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:
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: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 ausfoo
(da er auch im Skript festgelegt ist), aber der Wertfoo
in der übergeordneten Shell bleibt unverändert: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: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 wirdps
. 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:Führen Sie es auf verschiedene Arten aus, wobei nur ein einziges Terminal geöffnet ist:
Direkter Start,
./foo.sh
.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 werdenbash foo.sh
, wird nur angezeigt,foo.sh
was bedeutet, dass Siegrep
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 denps
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 derps
die Ausgabe erfolgt , keine davon angezeigt .Direktes Starten mit expliziter (bash) Shell
Hier wird
bash foo.sh
die Ausgabe vonps
angezeigtbash foo.sh
und gezählt , weil Sie mit ausgeführt werden. Hier haben wir also den übergeordneten Prozess, dasbash
Ausführen des Skripts und die geklonte Shell (Ausführen derps
), die alle angezeigt werden, da jetztps
jeder von ihnen angezeigt wird, da Ihr Befehl das Wort enthältbash
.Direktes Starten mit einer anderen Shell (
sh
)Dies ist anders, da Sie das Skript mit
sh
und nicht mit ausführenbash
. Daher ist die einzigebash
Instanz die übergeordnete Shell, in der Sie Ihr Skript gestartet haben. Alle anderen oben genannten Shells werdensh
stattdessen von ausgeführt.Beschaffung (entweder durch
.
odersource
dasselbe)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 verwendenpgrep
. All diese Probleme wären vermieden worden, wenn Sie nur gelaufen wärenEine funktionierende Version Ihres Skripts, die immer die richtige Nummer ausgibt, ist (beachten Sie, dass keine Befehlsersetzung erfolgt):
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 istbash
.quelle
./foo.sh
Läuft in einer neuen Shell, die keine Kopie des übergeordneten Elements ist. Wenn Sie beispielsweisefoo="bar"
in Ihrem Terminal festlegen, dass ein Skript ausgeführt wirdecho $foo
, wird eine leere Zeile angezeigt , da die Shell des Skripts den Wert der Variablen nicht geerbt hat.pgrep
ist eine separate Binärdatei und wird von dem Skript ausgeführt, das Sie ausführen.