Verschiedene Möglichkeiten, ein Shell-Skript auszuführen

44

Es gibt verschiedene Möglichkeiten, ein Skript auszuführen. Die mir bekannten sind:

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

Sind die mehr davon? Was sind die Unterschiede zwischen ihnen? Gibt es Situationen, in denen ich eine und keine andere verwenden muss?

phunehehe
quelle
Gut zu wissen, dank Ihrer Fragen und Antworten unten, besonders Shawns. Ich möchte etwas hinzufügen, was mir erst bei einigen Tests aufgefallen ist. Das Einfügen eines "/" in die zweite obige Weise bringt den Befehl in den obigen Modus 1. Das heißt, während "./myscript.sh" auf Modus 1 folgt, bleibt ". Myscript.sh" auf Modus 2. Sie haben "Verwenden des Pfads (absolut oder relativ)" erwähnt, wollten dies jedoch nur verdeutlichen.
Arun

Antworten:

32

Eine andere Möglichkeit besteht darin, den Interpreter aufzurufen und den Pfad zum Skript zu übergeben:

/bin/sh /path/to/script

Der Punkt und die Quelle sind gleichwertig. (BEARBEITEN: Nein, sie sind nicht: Wie KeithB in einem Kommentar zu einer anderen Antwort ausführt, funktioniert "." Nur in bash-bezogenen Shells, wobei "source" sowohl in bash- als auch in csh-bezogenen Shells funktioniert.) Das Skript wird in ausgeführt -place (als ob Sie das Skript genau dort kopiert und eingefügt hätten). Dies bedeutet, dass alle Funktionen und nicht lokalen Variablen im Skript verbleiben. Es bedeutet auch, dass Sie immer noch da sind, wenn das Skript eine CD in einem Verzeichnis erstellt.

Die anderen Methoden zum Ausführen eines Skripts führen es in einer eigenen Subshell aus. Variablen im Skript sind nach Abschluss des Vorgangs noch nicht aktiv. Wenn das Skript die Verzeichnisse geändert hat, hat dies keine Auswirkungen auf die aufrufende Umgebung.

/ path / to / script und / bin / sh script unterscheiden sich geringfügig. Typischerweise hat ein Skript am Anfang einen "shebang", der so aussieht:

#! /bin/bash

Dies ist der Pfad zum Skriptinterpreter. Wenn ein anderer Interpreter angegeben wird als bei der Ausführung, verhält er sich möglicherweise anders (oder funktioniert überhaupt nicht).

Beispielsweise beginnen Perl-Skripte und Ruby-Skripte mit (jeweils):

#! /bin/perl

und

#! /bin/ruby

Wenn Sie eines dieser Skripte ausführen /bin/sh script, funktionieren sie überhaupt nicht.

Ubuntu verwendet eigentlich keine Bash-Shell, sondern eine sehr ähnliche namens dash. Skripte, die Bash erfordern, funktionieren möglicherweise etwas falsch, wenn sie mit dem /bin/sh scriptBash-Interpreter aufgerufen werden, da Sie gerade ein Bash-Skript aufgerufen haben.

Ein weiterer kleiner Unterschied zwischen dem direkten Aufrufen des Skripts und der Übergabe des Skriptpfads an den Interpreter besteht darin, dass das Skript als ausführbar markiert sein muss, um es direkt auszuführen, aber nicht, um den Pfad an den Interpreter zu übergeben.

Eine weitere geringfügige Änderung: Sie können eine der folgenden Möglichkeiten zum Ausführen eines Skripts mit dem Präfix eval versehen

eval sh script
eval script
eval . script

und so weiter. Es ändert eigentlich nichts, aber ich dachte, ich würde es aus Gründen der Gründlichkeit einbeziehen.

Shawn J. Goff
quelle
6
Die Aussage "Ubuntu verwendet die Bash-Shell eigentlich nicht" ist ungenau und technisch falsch. Ubuntu macht die bash verwenden, ist der Punkt, shentspricht dash, aber nicht an bash.
Faheem Mitha
@Shawn Im ersten Absatz haben Sie geschrieben: "Das Skript wird direkt ausgeführt (als ob Sie das Skript kopiert und dort eingefügt hätten). Dies bedeutet, dass alle Funktionen und nicht lokalen Variablen im Skript erhalten bleiben." Was meinst du mit der zweiten Zeile hier? Kannst du bitte Erklären.
Geek
@Geek Wenn Sie ein Skript wie gewohnt als untergeordneten Prozess ausführen, verschwinden alle von ihm definierten Variablen und Funktionen (seine Umgebung), wenn der Prozess beendet wird. Wenn Sie das Skript als Quelle angeben, werden diese Variablen und Funktionen in der aktuellen Umgebung erstellt. Nach Abschluss des Skripts bleiben die Änderungen an der Umgebung erhalten.
Shawn J. Goff
@ ShawnJ.Goff danke für die Klarstellung. +1.
Geek
Plot-Twist: Bourne Shell (sh) akzeptiert nur Punkt - nicht Quelle, da es ein Bash-Builtin ist. pubs.opengroup.org/onlinepubs/9699919799/utilities/… Also würde ich sagen, der portabelste Weg ist ein Punkt.
Dezza
9

Die meisten Leute debuggen Shell-Skripte, indem sie dem Skript die folgenden Debug-Flags hinzufügen :

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

Dies bedeutet jedoch, dass Sie die Datei mit einem Editor öffnen müssen (vorausgesetzt, Sie haben die Berechtigung, die Datei zu bearbeiten), indem Sie eine Zeile wie " set -xSpeichern Sie die Datei" hinzufügen und dann die Datei ausführen. Wenn Sie fertig sind, müssen Sie die gleichen Schritte ausführen und entfernen set -x, usw. usw. Dies kann mühsam sein.

Anstatt all das zu tun, können Sie die Debugging-Flags in der Befehlszeile setzen:

$ 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
...
...
Stefan Lasiewski
quelle
2
Ein verwandter Tipp: Ich habe es mir zur Gewohnheit gemacht, emulate sh 2>/dev/nullmeine Shell-Skripte ganz oben zu schreiben. Wenn es mit zsh ausgeführt wird, wird es in den POSIX-kompatiblen Modus versetzt. Wenn die Linie mit anderen Muscheln ausgeführt wird, hat sie keine Auswirkung. Dann kann ich das Skript mit ausführen zsh -x /path/to/script. Ich mag zsh hier, weil es bessere Spuren liefert als bash oder ksh.
Gilles 'SO - hör auf, böse zu sein'
7

Shawn J. Goff machte viele gute Punkte, enthielt aber nicht die ganze Geschichte:

Ubuntu verwendet eigentlich keine Bash-Shell, sondern eine sehr ähnliche namens dash. Skripte, die Bash erfordern, funktionieren möglicherweise etwas falsch, wenn sie mit /bin/shScript aufgerufen werden, da Sie gerade ein Bash-Skript mit dem Dash-Interpreter aufgerufen haben.

Viele System-Skripte (wie zB in init.d, in / etc usw.) haben einen Shebang #!/bin/sh, sind aber /bin/shtatsächlich eine symbolische Verknüpfung zu einer anderen Shell - früher /bin/bash, heute /bin/dash. Aber wenn einer von ihnen als aufgerufen wird /bin/sh, verhalten sie sich anders, dh sie bleiben im POSIX-Kompatibilitätsmodus.

Wie machen sie das? Nun, sie untersuchen, wie sie aufgerufen wurden.

Kann ein Shellscript selbst testen, wie es aufgerufen wurde, und abhängig davon verschiedene Dinge tun? Ja, kann es. Die Art und Weise, wie Sie es aufrufen, kann immer zu unterschiedlichen Ergebnissen führen, aber es wird natürlich selten getan, um Sie zu ärgern. :)

Als Faustregel gilt: Wenn Sie eine bestimmte Shell wie Bash lernen und Befehle aus einem Bash-Tutorial schreiben, geben Sie #!/bin/bashdie Überschrift ein #!/bin/sh, sofern nicht anders angegeben. Anderenfalls könnten Ihre Befehle fehlschlagen. Und wenn Sie selbst kein Skript geschrieben haben, rufen Sie es direkt auf ( ./foo.sh, bar/foo.sh), anstatt eine Shell zu erraten ( sh foo.sh, sh bar/foo.sh). Der Shebang sollte die richtige Shell aufrufen.

Und hier sind zwei andere Arten der Anrufung:

cat foo.sh | dash
dash < foo.sh
Benutzer unbekannt
quelle
5

.und sourcesind insofern äquivalent, als sie keinen Unterprozess erzeugen, sondern Befehle in der aktuellen Shell ausführen. Dies ist wichtig, wenn das Skript Umgebungsvariablen festlegt oder das aktuelle Arbeitsverzeichnis ändert.

Wenn Sie den Pfad verwenden oder angeben, /bin/shwird ein neuer Prozess erstellt, in dem Befehle ausgeführt werden.

mouviciel
quelle
2
sh script
bash script

Ich überlege, ob es noch mehr gibt ...

.und sourcesind gleich. Nach der Ausführung werden alle Änderungen der Umgebung in scriptbeibehalten. Normalerweise wird es verwendet, um eine Bash-Bibliothek zu erstellen, sodass die Bibliothek in vielen verschiedenen Skripten wiederverwendet werden kann.

Es ist auch eine gute Möglichkeit, das aktuelle Verzeichnis zu behalten. Wenn Sie das Verzeichnis im Skript ändern, wird es nicht in der Shell angewendet, in der Sie das Skript ausführen. Wenn Sie es jedoch zum Ausführen verwenden, wird nach dem Beenden des Skripts das aktuelle Verzeichnis beibehalten.

Lebensunterhalt
quelle
2
.Funktioniert nur in sh / bash und verwandten Shells. sourcefunktioniert auch in csh und verwandten shells.
KeithB
1

Zählt " userland exec " anders? Userland exec lädt Code und lässt ihn ohne Verwendung eines Systemaufrufs execve () ausführen.

Bruce Ediger
quelle
1
. ./filename
# ( dot space dot slash filename )

Führt das Skript in der aktuellen Shell aus, wenn sich das Verzeichnis nicht im Pfad befindet.

Stefan
quelle
1

. und source sind zumindest in zsh ein bisschen anders (das ist es, was ich benutze), weil

source file

Funktioniert, während

. file

nicht, es braucht

. ./file
Bollovan
quelle