Я новичок в программировании на Bash. Я хочу вызвать свою программу на C++ в файле Bash.
Моя программа myProg.cpp
:
#include<iostream>
using namespace std;
int main()
{
cout<<"Salam! World"<<endl;
return 0;
}
И мой bash-файл myBash.sh
. Как мне вызвать мою вышеуказанную .cpp-программу в myBash.sh
файле?
решение1
Сначала вам нужно скомпилировать его: сначала измените текущий рабочий каталог Терминала на путь к вашему исходному файлу:
cd <path_to_cpp_file>/
Затем скомпилируйте исходный файл:
g++ myProg.cpp -o myProg
Затем вы можете вызвать скомпилированный исполняемый файл из вашего bash
скрипта следующим образом:
#!/bin/bash
# ...
<path_to_compiled_executable>/myProg
# ...
решение2
Так как вашнастоящая цельКажется, для автоматизации всего, что необходимо сделать для запуска вашей программы, я предлагаю другой подход.Вместо написания скрипта оболочки вы можете использоватьmakefile.Если хотите, вы можете написать правило в вашем makefile для запуска вашего исполняемого файла после его сборки. Тогда у вас будет два файла — ваш исходный код C++ и ваш makefile — и вы сможете запустить одну команду, которая эффективно:
- Создает или перестраивает вашу программу на C++,если и только если необходимо.
- Запускает вашу программу.
Последующие разделы этого поста объясняют, почему вы не можете вызвать .cpp
файл напрямую (а должны сначала создать исполняемый файл из него); как установить make
, как его использовать и что он делает за кулисами; и как избежать распространенных ловушек. Но в случае, если вы хотите получить представление о том, как выглядит этот подход, прежде чем углубляться в него, вы запустите make run
после размещения этого0в Makefile
:
all: myProg
run: all
./myProg
Мне это нравится больше, чем скрипт оболочки, и я думаю, что вам тоже может понравиться.
Фон
В отличие от некоторыхинтерпретируемые языкикак и Python и Bash, C++ являетсяскомпилированный язык.1Программы, написанные на языке C++, должны бытьпостроенныйдо того, как они будут запущены. (Строительство также иногда называюткомпиляция, хотякомпиляция(точнее, относится к одному из этапов построения.) Вы не можете запустить C++исходный кодфайл; вместо этого его необходимо скомпилировать вобъектный код2, в этом случаемашинный язык. Затем объектные файлы должны быть связаны друг с другом, и даже если есть только один, он все равно должен быть связан с любымобщие библиотекион использует. Связывание производитисполняемыйкоторый может быть запущен.
Короче говоря, вам нужно построить свою программу, прежде чем запустить ее.первый раз. Перед последующими запусками это не обязательноповторнособран, если только вы не изменили его исходный код; в этом случае вам придется собрать его заново, если вы хотите, чтобы ваши изменения отразились в работающей программе.Утилитаmake
разработан специально для такого рода ситуаций, когда требуется выполнить действияусловнов зависимости от того, были ли они уже выполнены и когда именно.
Получающийmake
Возможно, у вас уже make
установлена команда; попробуйте запустить ее, чтобы узнать. Если она установлена, вы увидите что-то вроде:
$ make
make: *** No targets specified and no makefile found. Stop.
Чтобы получить make, вы можете установитьделать пакет, но я предлагаю установитьстроить-необходимо который предоставляет ряд других удобных инструментов. (Вы могли установитьстроить-необходимо g++
(что является одним из способов, который у вас, возможно, уже есть . make
) Вы можете использовать Центр программного обеспечения для его установки или выполнить команду:
sudo apt-get update && sudo apt-get install build-essential
make
Без Makefile
Чтобы увидеть, как make
это работает, я предлагаю сначала запустить его без make-файла, передав ему базовое имя вашего файла с исходным кодом:
$ make myProg
g++ myProg.cpp -o myProg
$ ./myProg
Salam! World
При последующих запусках make
сравнивает временные метки модификации (mtimes) для входных и выходных файлов, и не будет перестраивать вашу программу без необходимости:
$ make myProg
make: 'myProg' is up to date.
При изменении myProg.cpp
обновляется временная метка его модификации, поэтому make
будет известно, что его нужно перестроить. (Вы также можете обновить временную метку файла с помощьюtouch
(команда, если вам нужно или вы хотите принудительно перестроить какие-либо выходные файлы, зависящие от нее. И, конечно, удаление выходных файлов также гарантирует, что они будут перестроены при запуске make
-- только не удаляйте не тот файл!)
$ touch myProg.cpp
$ make myProg
g++ myProg.cpp -o myProg
Как make
узнать, что делать, когда бежишь make myProg
?
- Аргумент
myProg
называетсяmake
цель. - Цели часто, но не всегда, являются именами файлов, которые будут созданы. Цели могут быть определены явно в makefile.
- Если цель не определена в make-файле или (как в этом случае) make-файл отсутствует,
make
ищет входные файлы (т. е. исходный код), названные таким образом, чтобы было понятно, что они предназначены для сборки цели. make
определяет, какую утилиту и синтаксис использовать при создании файла, исходя из его суффикса (в данном случае.cpp
).
Все это можно настроить, но в таких простых случаях, как этот, в этом часто нет необходимости.
Создание Makefile для автоматизации сборки и запуска вашей программы
Чтобы автоматизировать более сложные задачи, чем создание программы из одного файла исходного кода, например, если имеется несколько входных файлов или (что более применимо к вашим непосредственным потребностям) действий, которые вы хотите выполнить помимо запуска компилятора, вы можете создать make-файл для определения целей make
и указания того, как они зависят от других целей.
Первая цель, определенная в make-файле, является целью по умолчанию: это то, что make
пытается собрать при запуске без аргументов командной строки (т. е. когда вы запускаете просто make
, а не что-то вроде make myProg
).
Обычный способ использования makefiles — создание каталога, содержащего все файлы исходного кода (и любые другие файлы), используемые для сборки вашей программы, а также makefile, который обычно называется Makefile
. По этому имени make
он будет автоматически найден.
Чтобы создать makefile, который вы можете использовать для запуска myProg
, и который автоматически построит его первым, когда это необходимо, поместите его myProg.cpp
в новый, иначе пустой каталог. Создайте еще один текстовый файл в этом каталоге с именем Makefile
.
Для этого можно использовать любой текстовый редактор, но рецепт правила — перечисленные под ним команды, которые будут выполнены для достижения его цели — должен иметь отступвкладкискорее, чемпространства.3Поэтому, если ваш текстовый редактор в настоящее время настроен на добавление пробелов при нажатии Tab, вам следует изменить это.
Например, в Gedit или Pluma вы должны зайти вРедактировать > Настройки, нажмитередакторвкладку и убедитесь, чтоВставляйте пробелы вместо табуляциине отмечено:
Во многих редакторах отступы по умолчанию создаются с помощью табуляции, а не пробелов, поэтому, если вы ранее не меняли эту настройку, она может быть уже правильно установлена для make-файлов.
После того, как вы откроете редактор и (при необходимости) настроите отступы с помощью табуляции, введите следующее:
all: myProg
run: all
./myProg
Если вы скопируете и вставите это, это будет неправильно.поскольку пробелы будут вставлены, даже если ваш текстовый редактор не создает их при нажатии Tab. (Это связано со способом, которым Ask Ubuntu отображает код.) Но вы можете просто удалить четыре предшествующих пробела ./myProg
и нажать Tab, чтобы создать на их месте табуляцию.
Некоторые текстовые редакторы по умолчанию отображают табуляцию как 8 пробелов или какое-то другое число. Это нормально.
Что делает этот Makefile и как его использовать
Используйте команду:
make
, для сборки программы, если она еще не собрана и исполняемый файл не соответствует исходному коду.Или,make run
, чтобы запустить программу,постройте его сначала, если необходимо(т.е. если нет текущего исполняемого файла).
Этот make-файл определяет две цели: all
и run
.
У
all
цели нет собственного рецепта, но она зависит отmyProg
цели. Эта цель явно не определена, поэтому она неявно указываетmake
на попытку сборкиmyProg
из любых файлов исходного кода, доступных для нее в текущем каталоге. (См.make
Без Makefileраздел выше для получения подробной информации.)Поскольку
all
это первая цель, явно определенная вMakefile
, она будет построена приmake
запуске из каталога, в которомMakefile
находится. Таким образом, мы настроили все так, что запускmake
сам по себе эквивалентен запускуmake all
.Цель
run
запускает программу. Ее рецепт состоит из команды, которая это делает,./myProg
.run
объявляетall
цель как зависимость. Это делает так, что когда вы запускаетеmake run
,myProg
перестраивается, если текущийmyProg
исполняемый файл не является текущим (или еще не существует).Мы могли бы точно так же сделать
run
depend onmyProg
вместо onall
, но нам все равно понадобилась бы явнаяall
цель (или эквивалентная цель с другим именем), чтобы неrun
быть целью по умолчанию. Конечно, если вы хотите, чтобы ваша программа была построенаи бегидаже если вы работаетеmake
сами по себе, вы можете это сделать.Еще одно преимущество зависимости от
all
цели заключается в том, что в случае, если позже потребуется выполнить больше действий, прежде чем ваша программа должна быть запущена, вы можете добавить рецепт к правилу дляall
.
Использование makefile для запуска программы выглядит следующим образом, если ее необходимо собрать:
$ cd myProg/
$ make run
g++ myProg.cpp -o myProg
./myProg
Salam! World
Или это, если его не нужно строить:
$ make run
./myProg
Salam! World
А если вы просто хотите убедиться, что программа собрана (поскольку файл исходного кода был последний раз изменен)без запуска программы, просто запустите make
без аргументов:
$ make # Here, I run make and myProg isn't current.
g++ myProg.cpp -o myProg
$ make # Running "make" again after "make" or "make run" does nothing.
make: Nothing to be done for 'all'.
( make myProg
тоже будет работать.)
Усовершенствование: пользовательские флаги компилятора
make
чрезвычайно мощный инструмент, удобный для простых целей, как эта, но также хорошо подходящий для больших, сложных проектов. Попытка подробно описать все, что вы можете сделать с помощью этого, make
заняла бы целую книгу (в частности,Вот этот).
Но мне пришло в голову, что вы, возможно, захотите увидеть предупреждения от компилятора, когда что-то не мешает завершению сборки, но все еще является потенциальной ошибкой. Они не поймают все ошибки в программах, которые вы пишете, но они могут поймать многие.
При использовании GCC (как и с g++
командой) я рекомендую передать хотя бы -Wall
компилятору. Это на самом деле не включаетвсепредупреждения, но вы можете включить большую часть остальных с помощью -Wextra
. Иногда вы также можете захотеть -pedantic
. (См.man gcc
и3.8Параметры запроса или подавления предупрежденийвСправочное руководство GCC.)
Чтобы вызвать g++
вручную с этими флагами, выполните:
g++ -Wall -Wextra -pedantic -o myProg myProg.cpp
Чтобы вызвать make
компилятор C++ ( g++
) с флагами -Wall
, -Wextra
, и -pedantic
, добавьте CXXFLAGS=
строку с ними в начало Makefile
.
CXXFLAGS=-Wall -Wextra -pedantic
all: myProg
run: all
./myProg
Даже если myProg
все еще существует и новее, чем myProg.cpp
, запуск make
или make run
после редактирования Makefile
все равно снова соберет программу, поскольку Makefile
теперь новее, чем myProg
. Это хорошо, потому что:
- В этом случае пересборка исполняемого файла приведет к появлению предупреждений, если таковые имеются (для данной конкретной программы их быть не должно).
- В более общем смысле, иногда при редактировании make-файла вы хотите, чтобы создавались разные файлы или чтобы они создавались с разным содержимым. (Например, если бы вы добавили флаг
-O3
для интенсивной оптимизации или-g
чтобы заставить компилятор генерировать отладочные символы, результирующийmyProg
исполняемый файл был бы другим.)
Дальнейшее чтение
- Упражнение 2: Make — это ваш Python сейчасвИзучайте язык C трудным способомкЗед Шоу.
- TheРуководство по GNU Make, особенно2.1Как выглядит правило.
- Простой учебник по MakefileкБрюс А. Максвелл, для получения информации о других способах использования
make
. - «Использование Makefiles» (стр. 15) и «Makefiles против сценариев оболочки» (стр. 62) в21 век СкБен Клеменс. Номера страниц указаны для первого издания. (Второе издание, вероятно, лучше, но у меня есть только первое издание.)
Примечания
0: Я рекомендую вам почитать дальше, чтобы увидеть, как это работает. Но если вы хотите попробовать сделать это самостоятельно: вы должны делать отступы строк с помощью табуляции, а не пробелов.
1: Строго говоря, практически любой язык программирования может быть либо интерпретирован, либо скомпилирован в зависимости от того,реализациидля него были написаны. Для некоторых языков существуют как интерпретаторы, так и компиляторы. Однако интерпретируемый C++ встречается редко, хотяне неслыханно.
2: Разделение здания накомпиляцияисвязываниеи вызов трансляции файла исходного кода C++ (.cc/.cpp/.cxx/.C) в объектный кодкомпиляция, это еще не все. Программы на C и C++ (и некоторых других языках) являются первымипредварительно обработанный. В вашей программепрепроцессор Cзаменяет #include<iostream>
содержимым заголовочного <iostream>
файла до начала фактической компиляции. И в самом узком смысле компиляция преобразует исходный код вязык ассемблераа не объектный код. Многие компиляторы (например, GCC/ g++
) могут объединять компиляцию и сборку в один шаг и не создают ассемблерный код, если вас об этом не попросят.
В то время как предварительная обработка является отдельным шагом, GCC и другие компиляторы автоматически запускают препроцессор. Аналогично, они могут автоматически запускать компоновщик, поэтому вся последовательностьпредварительная обработка,компиляция,сборка, исвязьиногда называется «компиляцией», а не «сборкой». (Обратите внимание, что сборка может включать в себя больше шагов, чем эти, например, она может включать в себя генерацию файлов ресурсов, запускскрипт для настройки того, как все будет построено, и т. д.)
3: Вам нужно только сделать отступ с помощью табуляции в самом makefile. Использование makefile не накладывает никаких требований на то, как вы пишете сами файлы исходного кода C++. Не стесняйтесь переключать отступы обратно с табуляции на пробелы, когда вы работаете с другими файлами. (И если выДействительноне нравится отступ с помощью табуляции в makefile, вы можете установить.RECIPEPREFIX
специальная переменная.)
решение3
Вот пример: из myBash.sh
#!/bin/sh
g++ myProg.cpp -o myProg
./myProg