我是 Bash 编程新手。我想在 Bash 文件中调用我的 C++ 程序。
我的程序是myProg.cpp
:
#include<iostream>
using namespace std;
int main()
{
cout<<"Salam! World"<<endl;
return 0;
}
我的 bash 文件是myBash.sh
。如何在myBash.sh
文件中调用上面的 .cpp 程序?
答案1
您需要先编译它:首先将终端的当前工作目录更改为源文件的路径:
cd <path_to_cpp_file>/
然后编译源文件:
g++ myProg.cpp -o myProg
然后你可以从bash
脚本中调用已编译的可执行文件,如下所示:
#!/bin/bash
# ...
<path_to_compiled_executable>/myProg
# ...
答案2
由于你的真正的目标似乎是要自动化运行程序所需要做的任何事情,我建议采用不同的方法。您可以使用以下方法,而不必编写 Shell 脚本:生成文件。如果您愿意,您可以在 makefile 中编写一条规则,以便在可执行文件构建后运行它。然后您将拥有两个文件 - 您的 C++ 源代码文件和 makefile - 并且您将能够高效地运行单个命令:
- 构建或重建你的 C++ 程序,当且仅当必要时。
- 运行你的程序。
本文的后续部分将解释为什么不能.cpp
直接调用文件(但必须先从中创建可执行文件);如何安装make
、如何使用以及它在幕后做什么;以及如何避免常见的陷阱。但如果你想在深入研究之前先了解一下这种方法是什么样子,那么make run
在把这个放进去之后,你就会运行0在Makefile
:
all: myProg
run: all
./myProg
与 shell 脚本相比,我更喜欢用它来达到这个目的,我想你可能也会这样。
背景
与一些不同解释型语言和 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
其工作原理,我建议先在没有 makefile 的情况下运行它,并将源代码文件的基本名称传递给它:
$ make myProg
g++ myProg.cpp -o myProg
$ ./myProg
Salam! World
在后续运行中,make
比较修改时间戳(时光网s) 在输入和输出文件上,并且不会不必要地重建你的程序:
$ 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 中明确定义。
- 当 makefile 中未定义目标或(如本例)没有 makefile 时,
make
会查找以某种方式命名的输入(即源代码)文件,以表明它们用于构建目标。 make
根据文件的后缀(在本例中为)推断构建文件时要使用什么实用程序和语法.cpp
。
所有这些都可以定制,但在这种简单情况下通常不需要。
创建 Makefile 来自动构建和运行你的程序
为了自动执行比从单个源代码文件构建程序更复杂的任务,例如如果有多个输入文件或(更适用于您的当前需求)您希望除了运行编译器之外完成的操作,您可以创建一个 makefile 来定义目标make
并指定它们如何依赖于其他目标。
makefile 中定义的第一个目标是默认目标:它是make
在不使用命令行参数运行时尝试构建的目标(即,当您只运行make
,而不是类似的make myProg
)。
使用 makefile 的通常方式是创建一个目录,其中包含用于构建程序的所有源代码文件(以及任何其他文件)以及 makefile,该目录通常称为Makefile
。根据该名称,make
将自动找到它。
要创建一个 makefile,您可以使用它来运行myProg
,并在必要时首先自动构建它,请将其放入myProg.cpp
新的(否则为空)目录中。在该目录中创建另一个名为 的文本文件Makefile
。
您可以使用任何文本编辑器来完成此操作,但规则的配方(其下列出的将运行以实现其目标的命令)必须缩进标签而不是空格。3因此,如果您的文本编辑器当前配置为按下时缩进空格Tab,则应该更改此设置。
例如,在 Gedit 或 Pluma 中,你可以进入编辑 > 首选项, 点击编辑标签,并确保插入空格而不是制表符未选中:
许多编辑器默认使用制表符而不是空格进行缩进,因此如果您之前没有更改过此设置,则可能已经为 makefile 正确设置了它。
进入编辑器后(如果需要)将其配置为使用制表符缩进,请输入:
all: myProg
run: all
./myProg
如果你复制粘贴这个,那将是错误的因为即使文本编辑器在您按下 时没有生成空格,空格也会被复制Tab。(这与 Ask Ubuntu 显示代码的方式有关。)但您可以简单地删除前面的四个空格./myProg
,然后按下Tab以在它们的位置生成一个制表符。
某些文本编辑器默认将制表符显示为 8 个空格或其他数字。这没问题。
Makefile 的作用及其使用方法
使用命令:
make
,除非程序已经构建并且可执行文件与源代码一致,否则构建该程序。或者,make run
,运行该程序,如果有必要的话先建造(即,如果当前没有可执行文件)。
这个 makefile 定义了两个目标:all
和run
。
目标
all
没有自己的配方,但取决于myProg
目标。此目标未明确定义,因此它隐式地告诉make
尝试myProg
从当前目录中可用的任何源代码文件进行构建。(请参阅make
没有 Makefile详细信息请参见上文部分。因为
all
是 中明确定义的第一个目标,所以当运行时,它将从 所在的目录Makefile
构建。因此,我们已设置好,因此单独运行 相当于运行。make
Makefile
make
make all
目标
run
运行程序。其配方由执行此操作的命令组成,./myProg
。将目标run
声明all
为依赖项。这样,当您运行 时make run
,myProg
如果当前myProg
可执行文件不是最新的(或尚不存在),则会重建。我们也可以将其设置
run
为依赖于myProg
而不是all
,但我们仍然需要显式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
。这是一件好事,因为:
- 在这种情况下,重建可执行文件会导致您看到警告(如果有的话)(对于该特定程序来说不应该有警告)。
- 更一般地,有时当您编辑 makefile 时,是因为您想要生成不同的文件,或者生成具有不同内容的文件。(例如,如果您添加了
-O3
重度优化标志或-g
让编译器生成调试符号,则生成的myProg
可执行文件将会有所不同。)
进一步阅读
- 练习 2:现在 Make 你的 Python在艰难地学习 C 语言经过泽德·肖。
- 这GNU Make 手册, 尤其2.1规则是什么样的。
- 简单的 Makefile 教程经过布鲁斯·A·麦克斯韦尔,了解其他使用方法的信息
make
。 - “使用 Makefile”(第 15 页)和“Makefile 与 Shell 脚本”(第 62 页)21 世纪经过本·克莱门斯。页码为第一版。(第二版可能更好,但我只有第一版。)
笔记
0:我建议继续阅读以了解如何实现这一点。但如果您想先自己尝试一下:您必须使用制表符而不是空格来缩进行。
1:严格来说,几乎任何编程语言都可以被解释或编译,具体取决于实现已经有人编写过。对于某些语言,解释器和编译器都存在。然而,解释型 C++ 并不常见——尽管并非闻所未闻。
2:将建筑划分为编译和链接,并调用将C++源代码文件(.cc/.cpp/.cxx/.C)翻译成目标代码编译,这还不是全部。C 和 C++(以及其他一些语言)的程序首先预处理。在你的程序中,C 预处理器#include<iostream>
在实际编译开始之前,用头文件的内容替换<iostream>
。从最狭义的意义上讲,编译将源代码转换为汇编语言而不是目标代码。许多编译器(如 GCC/ g++
)可以将编译和汇编合并为一个步骤,并且除非被要求,否则不会生成汇编代码。
虽然预处理是一个单独的步骤,但 GCC 和其他编译器会自动运行预处理器。同样,它们可以自动运行链接器,这就是为什么预处理,汇编,集会, 和连锁有时被称为“编译”而不是“构建”。(还请注意,构建可能包含更多步骤——例如,它可能涉及生成资源文件、运行用于配置如何构建的脚本, ETC。)
3:您只需要在 makefile 本身中使用制表符缩进。使用 makefile 不会对您编写 C++ 源代码文件本身的方式施加任何要求。当您处理其他文件时,可以随意将缩进从制表符切换回空格。(如果您真的不喜欢在 makefile 中使用制表符缩进,你可以设置.RECIPEPREFIX
特殊变量。
答案3
以下是一个例子:来自 myBash.sh
#!/bin/sh
g++ myProg.cpp -o myProg
./myProg