GNU/Linux >> LINUX-Kenntnisse >  >> Linux

Wie geht Linux mit Shell-Skripten um?

Wenn Sie strace verwenden Sie können sehen, wie ein Shell-Skript ausgeführt wird, wenn es ausgeführt wird.

Beispiel

Angenommen, ich habe dieses Shell-Skript.

$ cat hello_ul.bash 
#!/bin/bash

echo "Hello Unix & Linux!"

Ausführen mit strace :

$ strace -s 2000 -o strace.log ./hello_ul.bash
Hello Unix & Linux!
$

Werfen Sie einen Blick in die strace.log Datei zeigt folgendes.

...
open("./hello_ul.bash", O_RDONLY)       = 3
ioctl(3, SNDCTL_TMR_TIMEBASE or SNDRV_TIMER_IOCTL_NEXT_DEVICE or TCGETS, 0x7fff0b6e3330) = -1 ENOTTY (Inappropriate ioctl for device)
lseek(3, 0, SEEK_CUR)                   = 0
read(3, "#!/bin/bash\n\necho \"Hello Unix & Linux!\"\n", 80) = 40
lseek(3, 0, SEEK_SET)                   = 0
getrlimit(RLIMIT_NOFILE, {rlim_cur=1024, rlim_max=4*1024}) = 0
fcntl(255, F_GETFD)                     = -1 EBADF (Bad file descriptor)
dup2(3, 255)                            = 255
close(3)     
...

Nachdem die Datei eingelesen wurde, wird sie ausgeführt:

...
read(255, "#!/bin/bash\n\necho \"Hello Unix & Linux!\"\n", 40) = 40
rt_sigprocmask(SIG_BLOCK, NULL, [], 8)  = 0
rt_sigprocmask(SIG_BLOCK, NULL, [], 8)  = 0
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 3), ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fc0b38ba000
write(1, "Hello Unix & Linux!\n", 20)   = 20
rt_sigprocmask(SIG_BLOCK, NULL, [], 8)  = 0
read(255, "", 40)                       = 0
exit_group(0)                           = ?

Oben können wir deutlich sehen, dass das gesamte Skript als eine einzelne Entität eingelesen und dann dort ausgeführt wird. Es würde also "erscheinen" zumindest im Fall von Bash, dass es die Datei einliest und dann ausführt. Du meinst also, du könntest das Skript bearbeiten, während es läuft?

HINWEIS: Aber nicht! Lesen Sie weiter, um zu verstehen, warum Sie nicht mit einer laufenden Skriptdatei herumspielen sollten.

Was ist mit anderen Dolmetschern?

Aber Ihre Frage ist etwas daneben. Es ist nicht unbedingt Linux, das den Inhalt der Datei lädt, es ist der Interpreter, der den Inhalt lädt, also hängt es wirklich davon ab, wie der Interpreter implementiert ist, ob er die Datei vollständig oder in Blöcken oder Zeilen auf einmal lädt.

Warum können wir die Datei nicht bearbeiten?

Wenn Sie jedoch ein viel größeres Skript verwenden, werden Sie feststellen, dass der obige Test etwas irreführend ist. Tatsächlich laden die meisten Interpreter ihre Dateien in Blöcken. Dies ist ziemlich üblich bei vielen Unix-Tools, bei denen sie Blöcke einer Datei laden, sie verarbeiten und dann einen anderen Block laden. Sie können dieses Verhalten anhand dieser U&L-Fragen und Antworten sehen, die ich vor einiger Zeit zu grep geschrieben habe , mit dem Titel:Wie viel Text verbraucht grep/egrep jedes Mal?.

Beispiel

Angenommen, wir erstellen das folgende Shell-Skript.

$ ( 
    echo '#!/bin/bash'; 
    for i in {1..100000}; do printf "%s\n" "echo \"$i\""; done 
  ) > ascript.bash;
$ chmod +x ascript.bash

Das Ergebnis ist diese Datei:

$ ll ascript.bash 
-rwxrwxr-x. 1 saml saml 1288907 Mar 23 18:59 ascript.bash

Welches die folgende Art von Inhalt enthält:

$ head -3 ascript.bash ; echo "..."; tail -3 ascript.bash 
#!/bin/bash
echo "1"
echo "2"
...
echo "99998"
echo "99999"
echo "100000"

Wenn Sie dies jetzt mit der gleichen Technik wie oben mit strace ausführen :

$ strace -s 2000 -o strace_ascript.log ./ascript.bash
...    
read(255, "#!/bin/bash\necho \"1\"\necho \"2\"\necho \"3\"\necho \"4\"\necho \"5\"\necho \"6\"\necho \"7\"\necho \"8\"\necho \"9\"\necho \"10\"\necho 
...
...
\"181\"\necho \"182\"\necho \"183\"\necho \"184\"\necho \"185\"\necho \"186\"\necho \"187\"\necho \"188\"\necho \"189\"\necho \"190\"\necho \""..., 8192) = 8192

Sie werden feststellen, dass die Datei in 8-KB-Schritten eingelesen wird, sodass Bash und andere Shells eine Datei wahrscheinlich nicht vollständig laden, sondern sie in Blöcken einlesen.

Referenzen

  • Die #! Magie, Details über den Shebang/Hash-Bang-Mechanismus auf verschiedenen Unix-Varianten

Dies ist mehr abhängig von der Shell als vom Betriebssystem.

Je nach Version ksh Lesen Sie das Skript bei Bedarf in 8k- oder 64k-Byte-Blöcken.

bash Lesen Sie das Skript Zeile für Zeile. Angesichts der Tatsache, dass Zeilen beliebig lang sein können, werden jedoch jedes Mal 8176 Bytes vom Anfang der nächsten zu analysierenden Zeile gelesen.

Dies ist für einfache Konstruktionen, d. h. eine Reihe einfacher Befehle.

Wenn Shell-strukturierte Befehle verwendet werden (ein Fall, in dem die akzeptierte Antwort nicht berücksichtigt wird ) wie ein for/do/done Schleife, ein case/esac switch, ein Here-Dokument, eine in Klammern eingeschlossene Subshell, eine Funktionsdefinition usw. und jede Kombination der oben genannten, Shell-Interpreter lesen bis zum Ende der Konstruktion, um zunächst sicherzustellen, dass kein Syntaxfehler vorliegt.

Dies ist etwas ineffizient, da derselbe Code viele Male immer wieder gelesen werden kann, aber durch die Tatsache gemildert wird, dass dieser Inhalt normalerweise zwischengespeichert wird.

Unabhängig vom Shell-Interpreter ist es sehr unklug, ein Shell-Skript zu ändern, während es ausgeführt wird, da die Shell frei ist, jeden Teil des Skripts erneut zu lesen, und dies zu unerwarteten Syntaxfehlern führen kann, wenn es nicht synchron ist.

Beachten Sie auch, dass bash mit einer Segmentierungsverletzung abstürzen kann, wenn es nicht in der Lage ist, eine übermäßig große Skriptkonstruktion zu speichern, die ksh93 fehlerfrei lesen kann.


Das hängt davon ab, wie der Interpreter arbeitet, der das Skript ausführt. Der Kernel bemerkt lediglich, dass die auszuführende Datei mit #! beginnt , führt den Rest der Zeile im Wesentlichen als Programm aus und gibt ihm die ausführbare Datei als Argument. Wenn der dort aufgeführte Interpreter diese Datei Zeile für Zeile liest (wie es interaktive Shells mit Ihrer Eingabe tun), erhalten Sie genau das (aber mehrzeilige Schleifenstrukturen werden gelesen und zur Wiederholung aufbewahrt); Wenn der Interpreter die Datei in den Speicher schlürft, sie verarbeitet (vielleicht zu einer Zwischendarstellung kompiliert, wie es Perl und Pyton tun), wird die Datei vor der Ausführung vollständig gelesen.

Wenn Sie die Datei in der Zwischenzeit löschen, wird die Datei nicht gelöscht, bis der Interpreter sie schließt (wie immer verschwinden Dateien, wenn die letzte Referenz, sei es ein Verzeichniseintrag oder ein Prozess, der sie offen hält), verschwindet.


Linux
  1. Wie ich Vagrant mit libvirt verwende

  2. So verschlüsseln Sie Dateien mit gocryptfs unter Linux

  3. Linux-Grundlagen:So laden Sie Dateien auf der Shell mit Wget herunter

  4. Linux – Wie funktioniert Load Average mit modernen CPUs?

  5. Wie parse ich Json mit Shell-Scripting in Linux?

So vergleichen Sie Verzeichnisse mit Meld unter Linux

So führen Sie einen Shell-Befehl mit Python aus

Shell-Scripting für Anfänger – So schreiben Sie Bash-Scripts unter Linux

So sichern Sie Linux-Server mit SE Linux

So ändern Sie eine Benutzer-Shell in Linux

Wie verwende ich if-else in Shell-Skripten?