Я написал скрипт bash и выполнил его без предварительной компиляции. Это сработало отлично. Он может работать с разрешениями или без них, но когда речь идет о программах на C, нам нужно скомпилировать исходный код. Почему?
Я написал скрипт bash и выполнил его без предварительной компиляции. Это сработало отлично. Он может работать с разрешениями или без них, но когда речь идет о программах на C, нам нужно скомпилировать исходный код. Почему?
Ответы:
Это означает, что сценарии оболочки не скомпилированы, они интерпретируются: оболочка интерпретирует сценарии по одной команде за раз и каждый раз выясняет, как выполнить каждую команду. Это имеет смысл для сценариев оболочки, так как они все равно проводят большую часть времени, выполняя другие программы.
С другой стороны, программы на Си обычно компилируются: перед тем, как их можно будет запустить, компилятор преобразует их в машинный код полностью, раз и навсегда. В прошлом были C-интерпретаторы (такие как C-интерпретатор HiSoft на Atari ST), но они были очень необычными. В настоящее время компиляторы C очень быстрые; TCC настолько быстр, что вы можете использовать его для создания «C-сценариев», используя #!/usr/bin/tcc -run
шебанг, так что вы можете создавать C-программы, которые работают так же, как сценарии оболочки (с точки зрения пользователей).
В некоторых языках обычно есть как интерпретатор, так и компилятор: BASIC - один из примеров, который приходит на ум.
Вы также можете найти так называемые компиляторы сценариев оболочки, но те, что я видел, просто запутывают оболочки: они все еще используют оболочку для интерпретации сценария. Как указывает mtraceur , правильный компилятор сценариев оболочки был бы возможен, но не очень интересным.
Еще один способ думать об этом - учитывать, что возможность интерпретации сценариев оболочки является расширением ее возможностей обработки командной строки, что, естественно, приводит к интерпретируемому подходу. C, с другой стороны, был разработан для производства автономных двоичных файлов; это приводит к скомпилированному подходу. Языки, которые обычно компилируются, тоже имеют тенденцию к появлению интерпретаторов или, по крайней мере, анализаторов командной строки (известные как REPLs, циклы read-eval-print ; оболочка сама по себе является REPL).
execve
, open
, close
, read
, write
, и pipe
системные вызовы, вперемешку с некоторыми getenv
, setenv
и операциями внутреннего HashMap / массива (для неэкспортированных переменных ) и т. д. Оболочка Bourne и ее производные также не являются языками программирования, которые в такой же степени получают выгоду от низкоуровневых настроек компилятора, таких как переупорядочение кода и т. д.
Рассмотрим следующую программу:
2 Mars Bars
2 Milks
1 Bread
1 Corn Flakes
Таким bash
образом, вы бродите по магазину в поисках баров Марс, наконец, находите их, затем бродите вокруг в поисках молока и т. Д. Это работает, потому что вы запускаете сложную программу под названием «Опытный покупатель», которая может распознать хлеб, когда вы его видите. и все остальные сложности покупок. bash
это довольно сложная программа.
Кроме того, вы можете передать свой список покупок компилятору покупок. Компилятор немного подумает и выдаст вам новый список. Этот список длинный , но состоит из гораздо более простых инструкций:
... 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.
Как вы можете видеть, компилятор точно знает, где все находится в магазине, поэтому весь этап «поиска вещей» не нужен.
Это отдельная программа, и для ее выполнения не требуется «Опытный покупатель». Все, что ему нужно - это человек с «Базовой человеческой операционной системой».
Возвращаясь к компьютерным программам: bash
это «Опытный покупатель» и может взять сценарий и просто сделать это, ничего не компилируя. Компилятор AC создает автономную программу, которая больше не нуждается в какой-либо помощи для запуска.
И интерпретаторы, и компиляторы имеют свои преимущества и недостатки.
Все сводится к техническому различию между тем, как программа, которую вы можете читать / писать как человек, преобразуется в машинные инструкции, которые понимает ваш компьютер, и различными преимуществами и недостатками каждого метода является причиной того, что некоторые языки написаны так, что им нужны компиляторы. и некоторые написаны для интерпретации.
(Примечание: я здесь много упрощаю ради решения вопроса. Для более глубокого понимания технические примечания внизу моего ответа уточняют / уточняют некоторые из упрощений здесь, а комментарии к этому ответу имеют некоторые полезные разъяснения и обсуждения, а также ..)
Есть в основном две основные категории языков программирования:
C относится к первой категории ( компилятор C переводит язык C в машинный код вашего компьютера : машинный код сохраняется в файл, а затем, когда вы запускаете этот машинный код, он делает то, что вы хотите).
bash относится ко второй категории ( интерпретатор bash читает язык bash, а интерпретатор bash делает то, что вы хотите: поэтому сам по себе «модуль компилятора» не работает, интерпретатор выполняет интерпретацию и выполнение, тогда как компилятор выполняет чтение и перевод) ,
Возможно, вы уже заметили, что это значит:
С помощью C вы выполняете шаг «интерпретация» один раз , а затем, когда вам нужно запустить программу, вы просто указываете компьютеру выполнить машинный код - ваш компьютер может просто запустить его напрямую, не прибегая к каким-либо дополнительным «размышлениям».
С bash вы должны выполнять шаг «интерпретировать» каждый раз, когда запускаете программу - на вашем компьютере запускается интерпретатор bash, а интерпретатор bash делает дополнительные «размышления», чтобы выяснить, что нужно делать для каждой команды, каждый раз ,
Таким образом, программы на C требуют больше ресурсов процессора, памяти и времени для подготовки (этап компиляции), но меньше времени и работы для запуска. bash-программам требуется меньше ресурсов процессора, памяти и времени на подготовку, но больше времени и работы для запуска. Вы, вероятно, в большинстве случаев не замечаете этих различий, потому что в наши дни компьютеры работают очень быстро, но это действительно имеет значение, и эта разница складывается, когда вам нужно запускать большие или сложные программы или множество маленьких программ.
Кроме того, поскольку программы на C преобразуются в машинный код («родной язык») компьютера, вы не можете взять программу и скопировать ее на другой компьютер с другим машинным кодом (например, Intel 64-bit на Intel 32). -бит или от Intel до ARM или MIPS или что-то еще). Вы должны тратить время , чтобы собрать его для этого другого машинного языка снова . Но программу bash можно просто перенести на другой компьютер, на котором установлен интерпретатор bash, и она будет работать нормально.
Создатели C писали операционную систему и другие программы на оборудовании несколько десятилетий назад, что было довольно ограничено современными стандартами. По разным причинам преобразование программ в машинный код компьютера было для них лучшим способом достижения этой цели. Кроме того, они выполняли такую работу, где было важно, чтобы написанный ими код работал эффективно .
И создатели оболочки Bourne и bash хотели обратного: они хотели писать программы / команды, которые могли бы быть выполнены немедленно - в командной строке, в терминале, вы хотите просто написать одну строку, одну команду и получить ее. выполнить. И они хотели, чтобы скрипты, которые вы написали, работали везде, где у вас был установлен интерпретатор / программа оболочки.
Короче говоря, вам не нужен компилятор для bash, но нужен компилятор для C, потому что эти языки по-разному преобразуются в реальные действия компьютера, и эти разные способы выбора были выбраны, потому что у языков были разные цели.
Вы действительно можете создать интерпретатор C или bash- компилятор, Ничто не мешает этому быть возможным: просто эти языки созданы для разных целей. Часто проще просто переписать программу на другом языке, чем написать хороший интерпретатор или компилятор для сложного языка программирования. Особенно, когда у этих языков есть определенная вещь, в которой они были хороши, и были разработаны с определенным способом работы в первую очередь. C был разработан для компиляции, поэтому в интерактивной оболочке отсутствует множество удобных сокращений, которые вы хотели бы использовать, но он очень хорош для выражения очень специфических низкоуровневых манипуляций с данными / памятью и взаимодействия с операционной системой. , которые вы часто выполняете, когда хотите написать эффективно скомпилированный код. Между тем, Bash очень хорошо выполняет другие программы,
Более подробные сведения: на самом деле существуют языки программирования, которые представляют собой смесь обоих типов (они переводят исходный код «в большинстве случаев»), так что они могут выполнить большую часть интерпретации / «мышления» один раз и выполнить лишь немного интерпретации / «мышления» позже). Java, Python и многие другие современные языки на самом деле являются такими смесями: они пытаются дать вам некоторые преимущества переносимости и / или быстрой разработки интерпретируемых языков, а также некоторую скорость скомпилированных языков. Есть много возможных способов комбинировать такие подходы, и разные языки делают это по-разному. Если вы хотите углубиться в эту тему, вы можете прочитать о языках программирования, компилируемых в «байт-код» (что похоже на компиляцию в ваш собственный искусный «машинный язык»).
Вы спросили о бите выполнения: на самом деле, исполняемый бит только там, чтобы сообщить операционной системе, что этот файл разрешен для выполнения. Я подозреваю, что единственная причина, по которой скрипты bash работают для вас без разрешения на выполнение, - это то, что вы запускаете их из оболочки bash. Обычно операционная система, когда ее просят выполнить файл без установленного бита выполнения, просто возвращает ошибку. Но некоторые оболочки, такие как bash, увидят эту ошибку и в любом случае возьмут на себя задачу запустить файл, эмулируя в основном шаги, которые обычно делает операционная система (посмотрите строку «#!» В начале файла и попробуйте выполнить эту программу для интерпретации файла, по умолчанию либо сам, либо, /bin/sh
если нет строки "#!").
Иногда компилятор уже установлен в вашей системе, а иногда IDE поставляются со своим собственным компилятором и / или запускают компиляцию за вас. Это может заставить скомпилированный язык «чувствовать» себя как некомпилированный язык для использования, но техническое отличие все еще есть.
«Скомпилированный» язык не обязательно компилируется в машинный код, и вся компиляция - это тема сама по себе. По сути, этот термин используется широко: на самом деле он может относиться к нескольким вещам. В определенном смысле «компилятор» - это просто переводчик с одного языка (обычно это язык более высокого уровня, более простой в использовании для людей) на другой язык (обычно это язык более низкого уровня, который проще для компьютеров - иногда, но на самом деле не очень часто, это машинный код). Кроме того, иногда, когда люди говорят «компилятор», они на самом деле говорят о нескольких программах, работающих вместе (для типичного компилятора C это фактически четыре программы: «препроцессор», сам компилятор, «ассемблер» и « линкер ").
Языки программирования / написания сценариев могут быть скомпилированы или интерпретированы.
Скомпилированные исполняемые файлы всегда быстрее, и многие ошибки могут быть обнаружены перед выполнением.
Интерпретируемые языки, как правило, проще в написании и адаптации менее строгие, чем компилируемые языки, и не требуют компиляции, что облегчает их распространение.
Представьте, что английский не ваш родной язык (это может быть довольно легко для вас, если английский не ваш родной язык).
Есть три способа прочитать это:
Компьютеры имеют своего рода «родной язык» - комбинацию инструкций, которые понимает процессор, и инструкций, которые понимает операционная система (например, Windows, Linux, OSX и т. Д.). Этот язык не читается людьми.
Языки сценариев, такие как Bash, обычно попадают в категории 1 и 2. Они принимают строку по очереди, переводят эту строку и запускают ее, а затем переходят к следующей строке. В Mac и Linux по умолчанию установлено несколько разных интерпретаторов для разных языков, таких как Bash, Python и Perl. В Windows вы должны установить их самостоятельно.
Многие языки сценариев выполняют небольшую предварительную обработку - стараются ускорить выполнение, компилируя куски кода, которые будут часто выполняться или которые в противном случае замедлили бы работу приложения. Некоторые термины, о которых вы, возможно, слышали, включают компиляцию «опережающее время» (AOT) или «точное время» (JIT).
Наконец, скомпилированные языки, такие как C, переводят всю программу до того, как вы сможете их запустить. Преимущество этого заключается в том, что перевод может быть выполнен на другой машине по сравнению с выполнением, поэтому, когда вы даете программу пользователю, в то время как все еще могут быть ошибки, несколько типов ошибок уже могут быть устранены. Точно так же, как если бы вы дали это своему переводчику, и я упомянул, как garboola mizene resplunks
это может выглядеть для вас как действительный английский, но переводчик может сказать вам, что я говорю чепуху. Когда вы запускаете скомпилированную программу, ей не нужен переводчик - она уже на родном языке компьютера
Однако у компилируемых языков есть один недостаток: я упомянул, что на компьютерах есть родной язык, состоящий из функций аппаратного обеспечения и операционной системы. Что ж, если вы компилируете свою программу в Windows, вы не ожидаете, что скомпилированная программа будет работать на Mac. Некоторые языки обходят это путем компиляции на некий промежуточный язык - немного похожий на Pidgin English - таким образом, вы получаете преимущества скомпилированного языка, а также небольшое увеличение скорости, но это означает, что вам нужно объединить интерпретатор с вашим кодом (или используйте тот, который уже установлен).
Наконец, ваша IDE, вероятно, компилировала ваши файлы для вас и могла сообщить вам об ошибках до того, как вы запустили код. Иногда эта проверка ошибок может быть более глубокой, чем это делает компилятор. Компилятор часто проверяет только столько, сколько ему нужно, чтобы он мог генерировать разумный нативный код. Среда IDE часто выполняет несколько дополнительных проверок и может сообщить вам, например, дважды ли вы определили переменную или импортировали что-то, что вы не использовали.
Многие люди говорят о интерпретации против компиляции, но я думаю, что это может немного ввести в заблуждение, если вы внимательно посмотрите на нее, поскольку некоторые интерпретируемые языки фактически скомпилированы в промежуточный байт-код перед выполнением.
В конце концов, настоящая причина того, почему программы на C должны быть скомпилированы в исполняемый формат, заключается в том, что компьютеру нужно проделать большую работу, чтобы преобразовать код в исходном файле C в то, что он может запустить, поэтому имеет смысл сохранить продукт из всей этой работы в исполняемый файл, поэтому вам не нужно делать это снова каждый раз, когда вы хотите запустить вашу программу.
С другой стороны, интерпретатору оболочки требуется очень мало усилий для преобразования сценария оболочки в «машинные операции». По сути, нужно только построчно прочитать скрипт, разбить его на пробелы, настроить перенаправления файлов и конвейеры, а затем выполнить команду fork + exec. Поскольку накладные расходы на синтаксический анализ и обработку текстового ввода сценария оболочки очень малы по сравнению со временем, которое требуется для запуска процессов в сценарии оболочки, было бы излишним компилировать сценарии оболочки в промежуточный формат компьютера, а не просто интерпретировать исходный код напрямую.