Ho scritto uno script bash e l'ho eseguito senza prima compilarlo. Ha funzionato perfettamente. Può funzionare con o senza autorizzazioni, ma quando si tratta di programmi C, è necessario compilare il codice sorgente. Perché?
Ho scritto uno script bash e l'ho eseguito senza prima compilarlo. Ha funzionato perfettamente. Può funzionare con o senza autorizzazioni, ma quando si tratta di programmi C, è necessario compilare il codice sorgente. Perché?
Risposte:
Significa che gli script della shell non vengono compilati, vengono interpretati: la shell interpreta gli script un comando alla volta e capisce ogni volta come eseguire ciascun comando. Ciò ha senso per gli script di shell poiché trascorrono comunque la maggior parte del loro tempo a eseguire altri programmi.
D'altra parte, i programmi C vengono generalmente compilati: prima di poter essere eseguiti, un compilatore li converte in codice macchina nella loro interezza, una volta per tutte. Ci sono stati interpreti C in passato (come l' interprete C di HiSoft su Atari ST) ma erano molto insoliti. Oggi i compilatori C sono molto veloci; TCC è così veloce che puoi usarlo per creare "script C", con un #!/usr/bin/tcc -run
shebang, quindi puoi creare programmi C che funzionano allo stesso modo degli script shell (dal punto di vista degli utenti).
Alcune lingue hanno comunemente sia un interprete che un compilatore: BASIC è un esempio che mi viene in mente.
Puoi anche trovare i cosiddetti compilatori di script di shell, ma quelli che ho visto sono solo involucri offuscanti: usano ancora una shell per interpretare effettivamente lo script. Come sottolinea mtraceur, sarebbe sicuramente possibile un vero compilatore di script di shell, ma non molto interessante.
Un altro modo di pensare a questo è considerare che la capacità di interpretazione degli script di una shell è un'estensione della sua capacità di gestione della riga di comando, che porta naturalmente ad un approccio interpretato. C d'altra parte è stato progettato per produrre binari autonomi; questo porta ad un approccio compilato. Le lingue che di solito vengono compilate tendono a far germogliare anche interpreti, o almeno parser da riga di comando (noti come REPL, loop read-eval-print ; una shell è essa stessa una REPL).
execve
, open
, close
, read
, write
, e pipe
syscalls, intervallati da alcuni getenv
, setenv
e le operazioni interne hashmap / matrice (per variabili non esportate ), ecc. La shell Bourne e i derivati non sono anche linguaggi di programmazione che beneficiano tanto delle modifiche di basso livello del compilatore come il riordino del codice, ecc.
Considera il seguente programma:
2 Mars Bars
2 Milks
1 Bread
1 Corn Flakes
A bash
proposito, ti aggiri per il negozio alla ricerca di mars bar, poi li localizzi, poi vai in giro alla ricerca di latte ecc. Funziona perché stai eseguendo un programma complesso chiamato "Shopper con esperienza" in grado di riconoscere un pane quando ne vedi uno e tutte le altre complessità dello shopping. bash
è un programma abbastanza complesso.
In alternativa, puoi consegnare la tua lista della spesa a un compilatore di acquisti. Il compilatore ci pensa per un po 'e ti dà un nuovo elenco. Questo elenco è LUNGO , ma contiene istruzioni molto più semplici:
... 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.
Come puoi vedere, il compilatore sa esattamente dove si trova tutto nel negozio, quindi l'intera fase di "ricerca di cose" non è necessaria.
Questo è un programma a sé stante e non ha bisogno di "Shopper esperto" per eseguire. Tutto ciò di cui ha bisogno è un essere umano con "Sistema operativo umano di base".
Tornando ai programmi per computer: bash
è "shopper esperto" e può prendere uno script e farlo senza compilare nulla. Il compilatore CA produce un programma autonomo che non necessita più di alcun aiuto per l'esecuzione.
Sia gli interpreti che i compilatori hanno i loro vantaggi e svantaggi.
Tutto dipende dalla differenza tecnica tra il modo in cui il programma che puoi leggere / scrivere come un essere umano viene convertito nelle istruzioni della macchina che il tuo computer capisce - e i diversi vantaggi e svantaggi di ciascun metodo è il motivo per cui alcune lingue sono scritte per avere bisogno di compilatori e alcuni sono scritti per essere interpretati.
(Nota: sto semplificando molto qui al fine di affrontare la domanda. Per una comprensione più approfondita, le note tecniche alla base della mia risposta elaborano / perfezionano alcune delle semplificazioni qui, e i commenti su questa risposta hanno alcuni chiarimenti e discussioni utili ..)
Esistono sostanzialmente due categorie generali di linguaggi di programmazione:
C è nella prima categoria (il compilatore C traduce il linguaggio C nel codice macchina del tuo computer : il codice macchina viene salvato in un file e quindi quando esegui quel codice macchina, fa quello che vuoi).
bash è nella seconda categoria (l' interprete bash legge il linguaggio bash e l' interprete bash fa quello che vuoi: quindi non c'è un "modulo compilatore" di per sé, l'interprete esegue l'interpretazione e l'esecuzione, mentre un compilatore fa lettura e traduzione) .
Potresti aver già notato cosa significa:
Con C, esegui il passaggio "interpreta" una volta , quindi ogni volta che devi eseguire il programma, devi semplicemente dire al tuo computer di eseguire il codice macchina - il tuo computer può semplicemente eseguirlo direttamente senza dover fare ulteriori "riflessioni".
Con bash, devi fare il passo "interpretare" ogni volta che esegui il programma: il tuo computer esegue l'interprete bash e l'interprete bash fa un "pensiero" in più per capire cosa deve fare per ogni comando, ogni volta .
Quindi i programmi C richiedono più CPU, memoria e tempo per essere preparati (fase di compilazione) ma meno tempo e lavoro per l'esecuzione. i programmi bash richiedono meno CPU, memoria e tempo per essere preparati, ma più tempo e lavoro per l'esecuzione. Probabilmente non noti queste differenze la maggior parte delle volte perché i computer sono molto veloci al giorno d'oggi, ma fa la differenza e quella differenza si somma quando devi eseguire programmi grandi o complicati o molti piccoli programmi.
Inoltre, poiché i programmi C vengono convertiti in codice macchina (la "lingua madre") del computer, non è possibile prendere un programma e copiarlo su un altro computer con un codice macchina diverso (ad esempio, Intel 64 bit su Intel 32 -bit, o da Intel a ARM o MIPS o altro). Dovete spendere il tempo per compilarlo per quell'altro linguaggio macchina di nuovo . Ma un programma bash può essere spostato su un altro computer su cui è installato l'interprete bash e funzionerà perfettamente.
I produttori di C stavano scrivendo un sistema operativo e altri programmi sull'hardware di diversi decenni fa, che era piuttosto limitato dagli standard moderni. Per vari motivi, convertire i programmi nel codice macchina del computer era il modo migliore per raggiungere quell'obiettivo per loro in quel momento. Inoltre, stavano facendo il tipo di lavoro in cui era importante che il codice che scrivevano funzionasse in modo efficiente .
E i creatori della shell e bash di Bourne volevano il contrario: volevano scrivere programmi / comandi che potevano essere eseguiti immediatamente - sulla riga di comando, in un terminale, si desidera scrivere solo una riga, un comando e averlo eseguire. E volevano che gli script che scrivevi funzionassero ovunque fosse installato l'interprete / programma della shell.
Quindi in breve, non hai bisogno di un compilatore per bash ma ne hai bisogno per C perché quelle lingue sono convertite in azioni informatiche effettive in modo diverso e quei diversi modi di fare sono stati scelti perché le lingue avevano obiettivi diversi.
In realtà potresti creare un interprete C o un compilatore bash. Non c'è nulla che impedisca che ciò sia possibile: sono solo quelle lingue create per scopi diversi. Spesso è più semplice riscrivere il programma in un'altra lingua, piuttosto che scrivere un buon interprete o compilatore per un linguaggio di programmazione complesso. Soprattutto quando quelle lingue hanno una cosa specifica in cui erano bravi e sono state progettate con un certo modo di lavorare, in primo luogo. C è stato progettato per essere compilato, quindi manca molta comoda scorciatoia che vorresti in una shell interattiva, ma è molto buona per esprimere una manipolazione molto specifica a basso livello di dati / memoria e interagire con il sistema operativo , che sono compiti che ti ritrovi spesso a fare quando vuoi scrivere codice compilato in modo efficiente. Nel frattempo, bash è molto bravo a eseguire altri programmi,
Dettagli più avanzati: in realtà ci sono linguaggi di programmazione che sono un mix di entrambi i tipi (traducono il codice sorgente "la maggior parte del modo", in modo che possano fare la maggior parte dell'interpretazione / "pensiero" una volta e fare solo un po ' dell'interpretazione / "pensiero" più avanti). Java, Python e molti altri linguaggi moderni sono in realtà tali miscele: cercano di ottenere alcuni dei vantaggi della portabilità e / o dello sviluppo rapido dei linguaggi interpretati e della velocità dei linguaggi compilati. Esistono molti modi possibili per combinare tali approcci e lingue diverse lo fanno diversamente. Se vuoi approfondire questo argomento, puoi leggere i linguaggi di programmazione compilando "bytecode" (che è un po 'come compilare il tuo "linguaggio macchina" inventato
Hai chiesto informazioni sul bit di esecuzione: in realtà, il bit eseguibile è lì solo per dire al sistema operativo che quel file può essere eseguito. Sospetto che l'unico motivo per cui gli script bash funzionano per te senza l' autorizzazione di esecuzione sia in realtà perché li stai eseguendo da una shell bash. Normalmente, il sistema operativo, quando viene richiesto di eseguire un file senza il set di bit di esecuzione, restituirà solo un errore. Ma alcune shell come bash vedranno quell'errore e si prenderanno comunque il compito di eseguire il file, fondamentalmente emulando i passi che il sistema operativo normalmente prenderebbe (cercare la riga "#!" All'inizio del file e provare eseguire quel programma per interpretare il file, con un valore predefinito di se stesso o /bin/sh
se non c'è una linea "#!").
A volte un compilatore è già installato sul tuo sistema, a volte gli IDE vengono con il proprio compilatore e / o eseguono la compilazione per te. Ciò potrebbe far sentire un linguaggio compilato come un linguaggio non compilato da usare, ma la differenza tecnica è ancora presente.
Un linguaggio "compilato" non viene necessariamente compilato nel codice macchina e l'intera compilazione è un argomento in sé. Fondamentalmente, il termine è usato in senso lato: può effettivamente riferirsi ad alcune cose. In un senso specifico, un "compilatore" è solo un traduttore da una lingua (in genere una lingua di "livello superiore" più facile da usare per gli umani) in un'altra lingua (in genere una lingua di "livello inferiore" che è più facile da usare per i computer - a volte, ma in realtà non molto spesso, questo è il codice macchina). Inoltre, a volte quando le persone dicono "compilatore", in realtà parlano di più programmi che lavorano insieme (per un tipico compilatore C, in realtà sono quattro programmi: il "pre-processore", il compilatore stesso, l '"assemblatore" e il " linker ").
I linguaggi di programmazione / scripting possono essere compilati o interpretati.
Gli eseguibili compilati sono sempre più veloci e molti errori possono essere rilevati prima dell'esecuzione.
Le lingue interpretate sono in genere più semplici da scrivere e da adattare essendo meno rigide rispetto alle lingue compilate e non richiedono la compilazione che li rende più facili da distribuire.
Immagina che l'inglese non sia la tua lingua madre (potrebbe essere abbastanza facile per te se l'inglese non è la tua lingua madre).
Ci sono 3 modi in cui potresti leggere questo:
I computer hanno una sorta di "lingua madre": una combinazione di istruzioni che il processore comprende e istruzioni che il sistema operativo (ad esempio Windows, Linux, OSX ecc.) Comprende. Questa lingua non è leggibile dagli umani.
I linguaggi di scripting, come Bash, di solito rientrano nelle categorie 1 e 2. Prendono una riga alla volta, traducono quella riga ed eseguono, quindi passano alla riga successiva. Su Mac e Linux, alcuni interpreti diversi sono installati per impostazione predefinita per lingue diverse, come Bash, Python e Perl. Su Windows, devi installarli tu stesso.
Molti linguaggi di scripting eseguono un po 'di pre-elaborazione: prova ad accelerare l'esecuzione compilando blocchi di codice che verranno eseguiti spesso o che altrimenti rallenterebbero l'applicazione. Alcuni termini di cui potresti aver sentito parlare includono la compilazione Ahead-of-time (AOT) o Just-in-time (JIT).
Infine, i linguaggi compilati - come C - traducono l'intero programma prima di poterli eseguire. Ciò ha il vantaggio che la traduzione può essere eseguita su una macchina diversa rispetto all'esecuzione, quindi quando si dà il programma all'utente, mentre potrebbero esserci ancora dei bug, diversi tipi di errori possono già essere ripuliti. Proprio come se lo dessi al tuo traduttore, e menziono come garboola mizene resplunks
, potrebbe sembrare un inglese valido per te, ma il traduttore può dirti che sto parlando senza senso. Quando esegui un programma compilato, non ha bisogno di un interprete: è già nella lingua madre del computer
Tuttavia, c'è un aspetto negativo delle lingue compilate: ho detto che i computer hanno una lingua nativa, composta da funzionalità dall'hardware e dal sistema operativo - beh, se compili il tuo programma su Windows, non ti aspetti che il programma compilato venga eseguito un Mac. Alcune lingue aggirano questo problema compilando una sorta di linguaggio a metà strada - un po 'come l'inglese Pidgin - in questo modo, ottieni i vantaggi di una lingua compilata, nonché un piccolo aumento della velocità, ma ciò significa che devi raggruppare un interprete con il tuo codice (o utilizzane uno già installato).
Infine, il tuo IDE stava probabilmente compilando i tuoi file per te e potrebbe dirti degli errori prima di eseguire il codice. A volte, questo controllo degli errori può essere più approfondito di quanto farà il compilatore. Un compilatore controllerà spesso solo quanto è necessario per poter produrre un codice nativo ragionevole. Un IDE eseguirà spesso alcuni controlli extra e può dirti, ad esempio, se hai definito una variabile due volte o se hai importato qualcosa che non hai usato.
Molte persone parlano di interpretazione e compilazione, ma penso che questo possa essere un po 'fuorviante se lo guardi da vicino poiché alcuni linguaggi interpretati sono effettivamente compilati in un codice intermedio prima dell'esecuzione.
Alla fine, il vero motivo per cui i programmi C devono essere compilati in formato eseguibile è che il computer deve fare molto lavoro per trasformare il codice in un file sorgente C in qualcosa che può essere eseguito, quindi ha senso salvare il prodotto di tutto ciò che funziona in un file eseguibile, quindi non è necessario farlo di nuovo ogni volta che si desidera eseguire il programma.
D'altra parte, l'interprete Shell deve fare pochissimo lavoro per convertire uno script di shell in "operazioni di macchina". Fondamentalmente deve solo leggere lo script riga per riga, dividerlo su spazi bianchi, impostare alcuni reindirizzamenti di file e pipeline e quindi fare un fork + exec. Poiché il sovraccarico dell'analisi e dell'elaborazione dell'input testuale di uno script di shell è molto ridotto rispetto al tempo necessario per avviare i processi nello script di shell, sarebbe eccessivo compilare gli script di shell in un formato macchina intermedio anziché semplicemente interpretare il codice sorgente direttamente.