Escrevi um script bash e o executei sem compilá-lo primeiro. Funcionou perfeitamente. Pode funcionar com ou sem permissões, mas quando se trata de programas em C, precisamos compilar o código-fonte. Por quê?
Escrevi um script bash e o executei sem compilá-lo primeiro. Funcionou perfeitamente. Pode funcionar com ou sem permissões, mas quando se trata de programas em C, precisamos compilar o código-fonte. Por quê?
Respostas:
Isso significa que os scripts de shell não são compilados, são interpretados: o shell interpreta os scripts um comando de cada vez e descobre sempre como executar cada comando. Isso faz sentido para scripts shell, pois eles passam a maior parte do tempo executando outros programas de qualquer maneira.
Os programas C, por outro lado, geralmente são compilados: antes que possam ser executados, um compilador os converte em código de máquina por inteiro, de uma vez por todas. Havia intérpretes C no passado (como o intérprete C da HiSoft no Atari ST), mas eles eram muito incomuns. Atualmente, os compiladores C são muito rápidos; TCC é tão rápido que você pode usá-lo para criar "scripts C", com um #!/usr/bin/tcc -run
shebang, assim você pode criar programas C que funcionam da mesma forma como scripts shell (na perspectiva dos usuários).
Algumas linguagens geralmente têm intérprete e compilador: BASIC é um exemplo que vem à mente.
Você também pode encontrar os chamados compiladores de scripts de shell, mas os que eu vi são apenas ofuscantes wrappers: eles ainda usam um shell para realmente interpretar o script. Como o mtraceur aponta que um compilador de script de shell adequado certamente seria possível, mas não muito interessante.
Outra maneira de pensar sobre isso é considerar que o recurso de interpretação de script de um shell é uma extensão do seu recurso de manipulação de linha de comando, o que naturalmente leva a uma abordagem interpretada. C, por outro lado, foi projetado para produzir binários independentes; isso leva a uma abordagem compilada. Os idiomas geralmente compilados tendem a gerar intérpretes também, ou pelo menos analisadores de linha de comando (conhecidos como REPLs, loops de leitura e avaliação de impressão ; um shell é um REPL).
execve
, open
, close
, read
, write
, e pipe
syscalls, intercaladas com alguns getenv
, setenv
e operações hashmap / matriz interna (para variáveis não-exportados ), etc. Bourne shell e derivados também são linguagens de programação que não beneficiam tanto de ajustes do compilador de baixo nível como o código re-ordenação, etc.
Considere o seguinte programa:
2 Mars Bars
2 Milks
1 Bread
1 Corn Flakes
No bash
caminho, você passeia pela loja procurando bares de Marte, finalmente os localiza e depois procura leite, etc. Isso funciona porque você está executando um programa complexo chamado "Comprador experiente" que pode reconhecer um pão quando você vê um e todas as outras complexidades das compras. bash
é um programa bastante complexo.
Como alternativa, você pode entregar sua lista de compras a um compilador de compras. O compilador pensa por um tempo e fornece uma nova lista. Esta lista é LONGA , mas consiste em instruções muito mais simples:
... 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.
Como você pode ver, o compilador sabe exatamente onde está tudo na loja, de modo que toda a fase "procurando coisas" não é necessária.
Este é um programa por si só e não precisa de "Comprador experiente" para executar. Tudo o que precisa é de um ser humano com "Sistema operacional humano básico".
Voltando aos programas de computador: bash
é "Comprador experiente" e pode pegar um script e apenas fazê-lo sem compilar nada. O compilador de CA produz um programa autônomo que não precisa mais de ajuda para ser executado.
Tanto intérpretes quanto compiladores têm suas vantagens e desvantagens.
Tudo se resume à diferença técnica entre como o programa que você pode ler / gravar como humano é convertido nas instruções da máquina que seu computador entende - e as diferentes vantagens e desvantagens de cada método é a razão pela qual algumas linguagens são escritas para a necessidade de compiladores , e alguns são escritos para serem interpretados.
(Observação: estou simplificando muito aqui para abordar a questão. Para uma compreensão mais aprofundada, as notas técnicas na parte inferior da minha resposta elaboram / refinam algumas das simplificações aqui, e os comentários sobre essa resposta foram alguns esclarecimentos e discussões úteis também.)
Existem basicamente duas categorias gerais de linguagens de programação:
C está na primeira categoria (o compilador C traduz o idioma C no código de máquina do computador : o código da máquina é salvo em um arquivo e, quando você executa esse código, faz o que deseja).
bash está na segunda categoria (o intérprete do bash lê o idioma do bash e o intérprete do bash faz o que você deseja: portanto, não existe um "módulo do compilador", o intérprete faz a interpretação e a execução, enquanto um compilador lê e traduz) .
Você já deve ter percebido o que isso significa:
Com C, você executa a etapa "interpretar" uma vez e , sempre que precisar executar o programa, basta instruir o computador a executar o código da máquina - o computador pode executá-lo diretamente, sem precisar "pensar".
Com o bash, você precisa executar a etapa "interpretar" toda vez que executa o programa - seu computador está executando o interpretador bash e o interpretador bash faz um "pensamento" extra para descobrir o que é necessário fazer para cada comando, sempre .
Portanto, os programas C levam mais CPU, memória e tempo para se preparar (a etapa de compilação), mas menos tempo e trabalho para executar. programas bash levam menos CPU, memória e tempo para se preparar, mas mais tempo e trabalho para executar. Você provavelmente não percebe essas diferenças na maioria das vezes porque os computadores são muito rápidos hoje em dia, mas isso faz diferença, e essa diferença aumenta quando você precisa executar programas grandes ou complicados ou muitos pequenos programas.
Além disso, como os programas C são convertidos em código de máquina (o "idioma nativo") do computador, você não pode pegar um programa e copiá-lo em outro computador com um código de máquina diferente (por exemplo, Intel de 64 bits no Intel 32 bits, ou da Intel para ARM ou MIPS ou o que for). Você precisa gastar tempo para compilá-lo novamente para essa outra linguagem de máquina . Mas um programa bash pode ser movido para outro computador que tenha o interpretador bash instalado e ele funcionará perfeitamente.
Os fabricantes de C estavam escrevendo um sistema operacional e outros programas em hardware de várias décadas atrás, o que era bastante limitado pelos padrões modernos. Por várias razões, converter os programas no código de máquina do computador era a melhor maneira de atingir esse objetivo na época. Além disso, eles estavam fazendo o tipo de trabalho em que era importante que o código que escrevessem fosse executado com eficiência .
E os criadores do shell e do bash Bourne queriam o oposto: eles queriam escrever programas / comandos que pudessem ser executados imediatamente - na linha de comando, em um terminal, você quer apenas escrever uma linha, um comando e tê-lo executar. E eles queriam que os scripts que você escrevesse funcionassem em qualquer lugar em que o programa / interpretador de shell estivesse instalado.
Portanto, em resumo, você não precisa de um compilador para o bash, mas precisa de um para o C, porque esses idiomas são convertidos em ações reais do computador de maneira diferente e a maneira diferente de fazer isso foi escolhida porque os idiomas tinham objetivos diferentes.
Você realmente pode criar um intérprete C ou um compilador bash. Não há nada que impeça que isso seja possível: são apenas esses idiomas que foram criados para diferentes propósitos. Geralmente, é mais fácil reescrever o programa em outro idioma do que escrever um bom intérprete ou compilador para uma linguagem de programação complexa. Especialmente quando essas línguas têm algo específico em que eram boas e foram projetadas com uma certa maneira de trabalhar em primeiro lugar. C foi projetado para ser compilado; portanto, faltam muitas abreviações convenientes que você deseja em um shell interativo, mas é muito bom para expressar manipulação muito específica e de baixo nível de dados / memória e interagir com o sistema operacional , que são tarefas que você costuma fazer quando deseja escrever um código compilado com eficiência. Enquanto isso, o bash é muito bom na execução de outros programas,
Detalhes mais avançados: na verdade, existem linguagens de programação que são uma mistura de ambos os tipos (elas traduzem o código-fonte "na maior parte do caminho", para que possam interpretar a maior parte da interpretação / "pensamento" uma vez e fazer apenas um pouco da interpretação / "pensamento"). Java, Python e muitas outras linguagens modernas são realmente essas misturas: eles tentam obter alguns dos benefícios de portabilidade e / ou desenvolvimento rápido das linguagens interpretadas e um pouco da velocidade das linguagens compiladas. Existem várias maneiras possíveis de combinar essas abordagens, e diferentes idiomas o fazem de maneira diferente. Se você quiser se aprofundar neste tópico, pode ler sobre linguagens de programação compilando em "bytecode" (que é como compilar em sua própria "linguagem de máquina" inventada)
Você perguntou sobre o bit de execução: na verdade, o bit executável existe apenas para informar ao sistema operacional que esse arquivo pode ser executado. Suspeito que o único motivo pelo qual os scripts bash funcionem para você sem a permissão de execução seja porque você os está executando dentro de um shell bash. Normalmente, o sistema operacional, quando solicitado a executar um arquivo sem o bit de execução definido, retornará apenas um erro. Porém, alguns shells como o bash verão esse erro e executam o arquivo de qualquer maneira, basicamente imitando as etapas que o sistema operacional normalmente executaria (procure a linha "#!" No início do arquivo e tente para executar esse programa para interpretar o arquivo, com um padrão próprio ou /bin/sh
se não houver "#!" linha).
Às vezes, um compilador já está instalado no seu sistema e, às vezes, os IDEs vêm com seu próprio compilador e / ou executam a compilação para você. Isso pode fazer com que uma linguagem compilada pareça uma linguagem não compilada, mas a diferença técnica ainda está lá.
Uma linguagem "compilada" não é necessariamente compilada no código da máquina, e toda a compilação deste é um tópico em si. Basicamente, o termo é amplamente utilizado: na verdade, pode se referir a algumas coisas. Em um sentido específico, um "compilador" é apenas um tradutor de um idioma (normalmente um idioma de "nível superior", mais fácil de ser usado por humanos) para outro idioma (normalmente um idioma de "nível inferior", mais fácil de usar pelos computadores - às vezes, mas na verdade não com muita frequência, esse é o código da máquina). Além disso, às vezes, quando as pessoas dizem "compilador", estão realmente falando de vários programas trabalhando juntos (para um compilador C típico, na verdade são quatro programas: o "pré-processador", o próprio compilador, o "assembler" e o " vinculador ").
Linguagens de programação / script podem ser compiladas ou interpretadas.
Os executáveis compilados são sempre mais rápidos e muitos erros podem ser detectados antes da execução.
Os idiomas interpretados são geralmente mais simples de escrever e adaptar, sendo menos rigorosos que os idiomas compilados, e não requerem compilação, o que os torna mais fáceis de distribuir.
Imagine que o inglês não é sua língua nativa (isso pode ser bastante fácil para você se o inglês não for sua língua nativa).
Existem três maneiras de ler isso:
Os computadores têm uma espécie de "idioma nativo" - uma combinação de instruções que o processador entende e instruções que o sistema operacional (por exemplo, Windows, Linux, OSX etc.) entende. Esta linguagem não é legível por humanos.
As linguagens de script, como o Bash, geralmente se enquadram nas categorias 1 e 2. Elas pegam uma linha de cada vez, convertem essa linha e executam-na e depois passam para a próxima linha. No Mac e Linux, muitos intérpretes diferentes são instalados por padrão para diferentes idiomas, como Bash, Python e Perl. No Windows, você deve instalá-los você mesmo.
Muitas linguagens de script fazem um pouco de pré-processamento - tente acelerar a execução compilando pedaços de código que serão executados com frequência ou que de outra forma retardariam o aplicativo. Alguns termos que você pode ouvir incluem compilação AOT (Antecipação de Tempo) ou Just-in-time (JIT).
Por fim, linguagens compiladas - como C - traduzem o programa inteiro antes que você possa executá-las. Isso tem a vantagem de que a tradução pode ser feita em uma máquina diferente da execução; portanto, quando você dá o programa ao usuário, enquanto ainda pode haver erros, vários tipos de erros já podem ser limpos. Como se você desse isso ao seu tradutor, e eu mencionei como garboola mizene resplunks
isso pode parecer um inglês válido para você, mas o tradutor pode dizer que estou falando bobagem. Quando você executa um programa compilado, ele não precisa de um intérprete - ele já está no idioma nativo do computador
No entanto, há uma desvantagem nos idiomas compilados: mencionei que os computadores têm um idioma nativo, composto por recursos do hardware e do sistema operacional - bem, se você compilar seu programa no Windows, não esperará que o programa compilado seja executado. um Mac. Alguns idiomas resolvem isso compilando para um tipo de idioma intermediário - um pouco como o inglês Pidgin - dessa maneira, você obtém os benefícios de um idioma compilado, além de um pequeno aumento de velocidade, mas isso significa que você precisa agrupar um intérprete com seu código (ou use um que já esteja instalado).
Por fim, seu IDE provavelmente estava compilando seus arquivos e poderia informar sobre erros antes de executar o código. Às vezes, essa verificação de erro pode ser mais aprofundada do que o compilador fará. Um compilador geralmente verifica apenas o necessário para produzir código nativo sensato. Um IDE geralmente executa algumas verificações extras e pode informar, por exemplo, se você definiu uma variável duas vezes ou se importou algo que não usou.
Muitas pessoas estão falando sobre interpretação versus compilação, mas acho que isso pode ser um pouco enganador, se você olhar de perto, já que algumas linguagens interpretadas são realmente compiladas em um bytecode intermediário antes da execução.
No final, a verdadeira razão pela qual os programas C precisam ser compilados para o formato executável é que o computador precisa fazer muito trabalho para transformar o código em um arquivo de origem C em algo que possa ser executado, por isso faz sentido salvar o produto de tudo isso funciona em um arquivo executável, para que você não precise fazer isso novamente novamente sempre que quiser executar seu programa.
Por outro lado, o interpretador Shell precisa fazer muito pouco trabalho para converter um script de shell em "operações da máquina". Basicamente, você só precisa ler o script linha por linha, dividi-lo em espaço em branco, configurar alguns redirecionamentos de arquivos e pipelines e, em seguida, executar um fork + exec. Como a sobrecarga de analisar e processar a entrada de texto de um script de shell é muito pequena em comparação com o tempo necessário para iniciar os processos no script de shell, seria um exagero compilar os scripts de shell em um formato de máquina intermediário, em vez de apenas interpretar o código fonte diretamente.