Ich habe ein Bash-Skript geschrieben und es ausgeführt, ohne es zuerst zu kompilieren. Es hat perfekt funktioniert. Es kann mit oder ohne Berechtigungen funktionieren, aber wenn es um C-Programme geht, müssen wir den Quellcode kompilieren. Warum?
Ich habe ein Bash-Skript geschrieben und es ausgeführt, ohne es zuerst zu kompilieren. Es hat perfekt funktioniert. Es kann mit oder ohne Berechtigungen funktionieren, aber wenn es um C-Programme geht, müssen wir den Quellcode kompilieren. Warum?
Antworten:
Dies bedeutet, dass Shell-Skripte nicht kompiliert, sondern interpretiert werden: Die Shell interpretiert Skripte Befehl für Befehl und findet jedes Mal heraus, wie die einzelnen Befehle ausgeführt werden. Dies ist für Shell-Skripte sinnvoll, da sie ohnehin die meiste Zeit damit verbringen, andere Programme auszuführen.
C-Programme hingegen werden normalerweise kompiliert: Bevor sie ausgeführt werden können, konvertiert ein Compiler sie ein für alle Mal in Maschinencode. In der Vergangenheit gab es C-Dolmetscher (wie den C-Interpreter von HiSoft auf dem Atari ST), aber sie waren sehr ungewöhnlich. Heutzutage sind C-Compiler sehr schnell; TCC ist so schnell, dass Sie damit "C-Skripte" mit einem #!/usr/bin/tcc -run
Shebang erstellen können , sodass Sie C-Programme erstellen können , die auf die gleiche Weise wie Shell-Skripte ausgeführt werden (aus Sicht der Benutzer).
Einige Sprachen haben normalerweise sowohl einen Interpreter als auch einen Compiler: BASIC ist ein Beispiel, das mir in den Sinn kommt.
Sie können auch sogenannte Shell-Skript-Compiler finden, aber die, die ich gesehen habe, sind nur verschleierte Wrapper: Sie verwenden immer noch eine Shell, um das Skript tatsächlich zu interpretieren. Wie mtraceur betont , wäre ein richtiger Shell-Skript-Compiler sicherlich möglich, aber nicht sehr interessant.
Eine andere Möglichkeit, darüber nachzudenken, besteht darin, zu berücksichtigen, dass die Skriptinterpretationsfunktion einer Shell eine Erweiterung ihrer Befehlszeilenverarbeitungsfunktion darstellt, was natürlich zu einem interpretierten Ansatz führt. C hingegen wurde entwickelt, um eigenständige Binärdateien zu erstellen. Dies führt zu einem kompilierten Ansatz. Sprachen, die normalerweise kompiliert werden, neigen dazu, auch Interpreter oder zumindest Befehlszeilen-Parser zu sprießen (bekannt als REPLs, Read-Eval-Print-Schleifen ; eine Shell ist selbst eine REPL).
execve
, open
, close
, read
, write
, und pipe
syscalls, durchsetzt mit einigen getenv
, setenv
und interne hashmap / Array - Operationen (für Nicht-exportierten Variablen ), etc. Bourne Shell und Derivate sind auch keine Programmiersprachen, die so viel von Low-Level-Compiler-Optimierungen wie Code-Neuordnung usw.
Betrachten Sie das folgende Programm:
2 Mars Bars
2 Milks
1 Bread
1 Corn Flakes
Auf dem bash
Weg wandern Sie durch den Laden und suchen nach Marsbars, suchen sie schließlich und suchen dann nach Milch usw. Dies funktioniert, weil Sie ein komplexes Programm namens "Erfahrener Käufer" ausführen, das ein Brot erkennt, wenn Sie eines sehen und all die anderen Komplexitäten des Einkaufens. bash
ist ein ziemlich komplexes Programm.
Alternativ können Sie Ihre Einkaufsliste einem Einkaufs-Compiler übergeben. Der Compiler denkt eine Weile nach und gibt Ihnen eine neue Liste. Diese Liste ist LANG , besteht aber aus viel einfacheren Anweisungen:
... lots of instructions on how to get to the store, get a shopping cart etc.
move west one aisle.
move north two sections.
move hand to shelf three.
grab object.
move hand to shopping cart.
release object.
... and so on and so forth.
Wie Sie sehen können, weiß der Compiler genau, wo sich alles im Shop befindet, sodass die gesamte Phase "Nach Dingen suchen" nicht benötigt wird.
Dies ist ein eigenständiges Programm, für dessen Ausführung kein "erfahrener Käufer" erforderlich ist. Alles was es braucht ist ein Mensch mit "Basic Human Operating System".
Zurück zu Computerprogrammen: bash
ist "Erfahrener Käufer" und kann ein Skript nehmen und es einfach tun, ohne etwas zu kompilieren. Der AC-Compiler erstellt ein eigenständiges Programm, für dessen Ausführung keine Hilfe mehr erforderlich ist.
Sowohl Interpreter als auch Compiler haben ihre Vor- und Nachteile.
Es kommt alles auf den technischen Unterschied an, wie das Programm, das Sie als Mensch lesen / schreiben können, in die Maschinenanweisungen konvertiert wird, die Ihr Computer versteht - und die unterschiedlichen Vor- und Nachteile jeder Methode sind der Grund, warum einige Sprachen so geschrieben werden, dass sie Compiler benötigen und einige sind geschrieben, um interpretiert zu werden.
(Hinweis: Ich vereinfache hier viel, um die Frage zu beantworten. Für ein tieferes Verständnis erläutern / verfeinern die technischen Hinweise am Ende meiner Antwort einige der Vereinfachungen hier und die Kommentare zu dieser Antwort einige nützliche Klarstellungen und Diskussionen auch ..)
Grundsätzlich gibt es zwei allgemeine Kategorien von Programmiersprachen:
C gehört zur ersten Kategorie (der C- Compiler übersetzt die C- Sprache in den Maschinencode Ihres Computers : Der Maschinencode wird in einer Datei gespeichert, und wenn Sie diesen Maschinencode ausführen, macht er, was Sie wollen).
Bash gehört zur zweiten Kategorie (der Bash- Interpreter liest die Bash- Sprache und der Bash- Interpreter macht das, was Sie wollen: Es gibt also kein "Compilermodul" an sich, der Interpreter interpretiert und führt aus, während ein Compiler liest und übersetzt). .
Möglicherweise haben Sie bereits bemerkt, was dies bedeutet:
Mit C führen Sie den Schritt "Interpretieren" einmal aus . Wenn Sie das Programm ausführen müssen, weisen Sie Ihren Computer an, den Maschinencode auszuführen. Ihr Computer kann ihn einfach direkt ausführen, ohne zusätzliche "Überlegungen" anstellen zu müssen.
Mit bash müssen Sie jedes Mal, wenn Sie das Programm ausführen, den Schritt "Interpretieren" ausführen - auf Ihrem Computer wird der Bash-Interpreter ausgeführt, und der Bash-Interpreter führt jedes Mal zusätzliche "Überlegungen" durch, um herauszufinden, was für jeden Befehl zu tun ist .
Daher benötigen C-Programme mehr CPU, Speicher und Zeit für die Vorbereitung (Kompilierungsschritt), aber weniger Zeit und Arbeit für die Ausführung. Bash-Programme benötigen weniger CPU, Speicher und Zeit für die Vorbereitung, aber mehr Zeit und Arbeit für die Ausführung. Sie werden diese Unterschiede wahrscheinlich die meiste Zeit nicht bemerken, weil Computer heutzutage sehr schnell sind, aber es macht einen Unterschied, und dieser Unterschied summiert sich, wenn Sie große oder komplizierte Programme oder viele kleine Programme ausführen müssen.
Da C-Programme in Maschinencode (die "Muttersprache") des Computers konvertiert werden, können Sie ein Programm nicht auf einen anderen Computer mit einem anderen Maschinencode kopieren (z. B. Intel 64-Bit auf Intel 32) -bit oder von Intel zu ARM oder MIPS oder was auch immer). Sie haben die Zeit zu verbringen , es für die andere Maschinensprache zu kompilieren wieder . Ein Bash-Programm kann jedoch einfach auf einen anderen Computer verschoben werden, auf dem der Bash-Interpreter installiert ist, und es läuft einwandfrei.
Die Hersteller von C haben vor einigen Jahrzehnten ein Betriebssystem und andere Programme auf Hardware geschrieben, das durch moderne Standards eher eingeschränkt war. Aus verschiedenen Gründen war die Konvertierung der Programme in den Maschinencode des Computers zu dieser Zeit der beste Weg, um dieses Ziel zu erreichen. Außerdem erledigten sie die Art von Arbeit, bei der es wichtig war, dass der von ihnen geschriebene Code effizient lief .
Und die Macher der Bourne-Shell und -Bash wollten das Gegenteil: Sie wollten Programme / Befehle schreiben, die sofort ausgeführt werden konnten - in der Befehlszeile eines Terminals möchten Sie nur eine Zeile, einen Befehl schreiben und haben ausführen. Und sie wollten, dass Skripte, die Sie geschrieben haben, überall dort funktionieren, wo Sie den Shell-Interpreter / das Shell-Programm installiert haben.
Kurz gesagt, Sie benötigen keinen Compiler für Bash, aber einen für C, da diese Sprachen unterschiedlich in tatsächliche Computeraktionen konvertiert werden und diese unterschiedliche Vorgehensweise ausgewählt wurde, weil die Sprachen unterschiedliche Ziele hatten.
Sie könnten tatsächlich einen C- Interpreter oder einen Bash- Compiler erstellen. Nichts hindert das daran, möglich zu sein: Es sind nur diese Sprachen, die für verschiedene Zwecke entwickelt wurden. Es ist oft einfacher, das Programm einfach in eine andere Sprache umzuschreiben, als einen guten Interpreter oder Compiler für eine komplexe Programmiersprache zu schreiben. Besonders wenn diese Sprachen eine bestimmte Sache haben, in der sie gut waren und in erster Linie mit einer bestimmten Arbeitsweise entworfen wurden. C wurde für die Kompilierung entwickelt, daher fehlt eine Menge praktischer Kurzform, die Sie in einer interaktiven Shell benötigen, aber es ist sehr gut geeignet, um eine sehr spezifische Manipulation von Daten / Speicher auf niedriger Ebene und die Interaktion mit dem Betriebssystem auszudrücken Dies sind Aufgaben, die Sie häufig ausführen, wenn Sie effizient kompilierten Code schreiben möchten. In der Zwischenzeit ist bash sehr gut darin, andere Programme auszuführen.
Weiterführende Details: Es gibt tatsächlich Programmiersprachen, die eine Mischung aus beiden Typen sind (sie übersetzen den Quellcode "meistens", so dass sie den größten Teil der Interpretation / des "Denkens" einmal und nur wenig tun können der Interpretation / "Denken" später). Java, Python und viele andere moderne Sprachen sind tatsächlich solche Mischungen: Sie versuchen, Ihnen einige der Portabilitäts- und / oder Entwicklungsvorteile der interpretierten Sprachen und einige der Geschwindigkeit kompilierter Sprachen zu vermitteln. Es gibt viele Möglichkeiten, solche Ansätze zu kombinieren, und verschiedene Sprachen tun dies unterschiedlich. Wenn Sie sich mit diesem Thema befassen möchten, können Sie sich über Programmiersprachen informieren, die in "Bytecode" kompiliert werden (ähnlich wie das Kompilieren in Ihre eigene erfundene "Maschinensprache").
Sie haben nach dem Ausführungsbit gefragt: Tatsächlich ist das ausführbare Bit nur dazu da, dem Betriebssystem mitzuteilen, dass diese Datei ausgeführt werden darf . Ich vermute, dass der einzige Grund, warum Bash-Skripte ohne die Ausführungsberechtigung für Sie funktionieren , darin besteht, dass Sie sie in einer Bash-Shell ausführen . Normalerweise gibt das Betriebssystem nur einen Fehler zurück, wenn es aufgefordert wird, eine Datei ohne gesetztes Ausführungsbit auszuführen. Einige Shells wie bash werden diesen Fehler jedoch erkennen und es sich zur Aufgabe machen, die Datei trotzdem auszuführen, indem sie im Grunde die Schritte emulieren, die das Betriebssystem normalerweise ausführen würde (lesen Sie die Zeile "#!" Am Anfang der Datei und versuchen Sie es um dieses Programm auszuführen, um die Datei zu interpretieren, mit einer Standardeinstellung von entweder selbst oder /bin/sh
wenn es keine "#!" - Zeile gibt).
Manchmal ist bereits ein Compiler auf Ihrem System installiert, und manchmal werden IDEs mit einem eigenen Compiler geliefert und / oder führen die Kompilierung für Sie aus. Dadurch fühlt sich eine kompilierte Sprache möglicherweise wie eine nicht kompilierte Sprache an, aber der technische Unterschied ist immer noch vorhanden.
Eine "kompilierte" Sprache wird nicht unbedingt in Maschinencode kompiliert, und das gesamte Kompilieren ist ein Thema für sich. Grundsätzlich wird der Begriff weit verbreitet verwendet: Er kann sich tatsächlich auf einige Dinge beziehen. In einem bestimmten Sinne ist ein "Compiler" nur ein Übersetzer von einer Sprache (normalerweise eine "höhere" Sprache, die für Menschen einfacher zu verwenden ist) in eine andere Sprache (normalerweise eine "niedrigere" Sprache, die für Computer einfacher zu verwenden ist - manchmal, aber eigentlich nicht sehr oft, ist dies Maschinencode). Manchmal, wenn Leute "Compiler" sagen, sprechen sie wirklich davon, dass mehrere Programme zusammenarbeiten (für einen typischen C-Compiler sind es tatsächlich vier Programme: der "Pre-Prozessor", der Compiler selbst, der "Assembler" und der " Linker ").
Programmier- / Skriptsprachen können kompiliert oder interpretiert werden.
Kompilierte ausführbare Dateien sind immer schneller und viele Fehler können vor der Ausführung erkannt werden.
Interpretierte Sprachen sind in der Regel einfacher zu schreiben und anzupassen, da sie weniger streng sind als kompilierte Sprachen. Sie müssen nicht kompiliert werden, wodurch sie einfacher zu verteilen sind.
Stellen Sie sich vor, dass Englisch nicht Ihre Muttersprache ist (das könnte für Sie recht einfach sein, wenn Englisch nicht Ihre Muttersprache ist).
Es gibt drei Möglichkeiten, wie Sie dies lesen können:
Computer haben eine Art "Muttersprache" - eine Kombination aus Anweisungen, die der Prozessor versteht, und Anweisungen, die das Betriebssystem (z. B. Windows, Linux, OSX usw.) versteht. Diese Sprache ist für Menschen nicht lesbar.
Skriptsprachen wie Bash fallen normalerweise in die Kategorien 1 und 2. Sie nehmen jeweils eine Zeile, übersetzen diese Zeile und führen sie aus. Fahren Sie dann mit der nächsten Zeile fort. Unter Mac und Linux werden standardmäßig einige verschiedene Interpreter für verschiedene Sprachen wie Bash, Python und Perl installiert. Unter Windows müssen Sie diese selbst installieren.
Viele Skriptsprachen führen eine kleine Vorverarbeitung durch. Versuchen Sie, die Ausführung zu beschleunigen, indem Sie Codestücke kompilieren, die häufig ausgeführt werden oder die Anwendung sonst verlangsamen würden. Einige Begriffe, von denen Sie vielleicht hören, umfassen die AOT-Kompilierung (Ahead-of-Time) oder die JIT-Kompilierung (Just-in-Time).
Schließlich übersetzen kompilierte Sprachen - wie C - das gesamte Programm, bevor Sie sie ausführen können. Dies hat den Vorteil, dass die Übersetzung auf einem anderen Computer als der Ausführung durchgeführt werden kann. Wenn Sie also dem Benutzer das Programm geben, obwohl möglicherweise noch Fehler vorliegen, können bereits verschiedene Arten von Fehlern behoben werden. Genau wie wenn Sie dies Ihrem Übersetzer gegeben haben und ich erwähne, wie das garboola mizene resplunks
für Sie wie gültiges Englisch aussehen könnte, aber der Übersetzer kann Ihnen sagen, dass ich Unsinn spreche. Wenn Sie ein kompiliertes Programm ausführen, benötigt es keinen Interpreter - es ist bereits in der Muttersprache des Computers
Kompilierte Sprachen haben jedoch einen Nachteil: Ich erwähnte, dass Computer eine Muttersprache haben, die sich aus Funktionen der Hardware und des Betriebssystems zusammensetzt. Wenn Sie Ihr Programm unter Windows kompilieren, erwarten Sie nicht, dass das kompilierte Programm ausgeführt wird ein Mac. Einige Sprachen umgehen dies, indem sie zu einer Art Halbsprache kompilieren - ein bisschen wie Pidgin English -. Auf diese Weise erhalten Sie die Vorteile einer kompilierten Sprache sowie eine kleine Geschwindigkeitssteigerung, aber dies bedeutet, dass Sie bündeln müssen einen Interpreter mit Ihrem Code (oder verwenden Sie einen bereits installierten).
Schließlich hat Ihre IDE wahrscheinlich Ihre Dateien für Sie kompiliert und konnte Sie über Fehler informieren, bevor Sie den Code ausgeführt haben. Manchmal kann diese Fehlerprüfung ausführlicher sein als der Compiler. Ein Compiler prüft oft nur so viel, wie er benötigt, um vernünftigen nativen Code zu erzeugen. Eine IDE führt häufig einige zusätzliche Überprüfungen durch und kann Ihnen beispielsweise mitteilen, ob Sie eine Variable zweimal definiert haben oder ob Sie etwas importiert haben, das Sie nicht verwendet haben.
Viele Leute sprechen von Interpretation vs. Kompilierung, aber ich denke, dies kann etwas irreführend sein, wenn man es sich genau ansieht, da einige interpretierte Sprachen vor der Ausführung tatsächlich zu einem Zwischenbytecode kompiliert werden.
Der eigentliche Grund, warum C-Programme in ein ausführbares Format kompiliert werden müssen, besteht darin, dass der Computer viel Arbeit leisten muss, um den Code in einer C-Quelldatei in etwas umzuwandeln, das ausgeführt werden kann, sodass es sinnvoll ist, das Produkt zu speichern von all dem funktioniert in einer ausführbaren Datei, so dass Sie es nicht jedes Mal wiederholen müssen, wenn Sie Ihr Programm ausführen möchten.
Andererseits muss der Shell-Interpreter nur sehr wenig Arbeit leisten, um ein Shell-Skript in "Maschinenoperationen" zu konvertieren. Grundsätzlich muss das Skript nur zeilenweise gelesen, in Leerzeichen aufgeteilt, einige Dateiumleitungen und Pipelines eingerichtet und anschließend ein Fork + Exec ausgeführt werden. Da der Aufwand für das Parsen und Verarbeiten der Texteingabe eines Shell-Skripts im Vergleich zu der Zeit, die zum Starten der Prozesse im Shell-Skript benötigt wird, sehr gering ist, wäre es übertrieben, die Shell-Skripte in ein Zwischen-Maschinenformat zu kompilieren, anstatt nur zu interpretieren den Quellcode direkt.