我是 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
自從你的真正的目標似乎是自動化運行程式所需的任何操作,我建議採用不同的方法。您可以使用產生檔案。如果您願意,可以在 makefile 中編寫一條規則,以便在建置可執行檔後執行該規則。然後,您將擁有兩個檔案——您的 C++ 原始程式碼檔案和 makefile——並且您將能夠有效地執行單一命令:
- 建立或重建您的 C++ 程序,當且僅當有必要時。
- 運行你的程式。
本文的後續部分解釋了為什麼不能.cpp
直接呼叫檔案(但必須先從中建立可執行檔);如何安裝make
、如何使用它以及它在幕後做什麼;以及如何避免常見的陷阱。但是,如果您想在深入研究之前了解這種方法的樣子,那麼您將make run
在放置此方法後運行0在Makefile
:
all: myProg
run: all
./myProg
與 shell 腳本相比,我更喜歡用它來實現此目的,我想您也可能會喜歡。
背景
不像有些解釋性語言與 Python 和 Bash 一樣,C++ 是編譯語言。1用 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
而不是 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請求或抑制警告的選項在裡面海灣合作委員會參考手冊.)
要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 製作手冊, 尤其2.1規則是什麼樣的。
- 簡單的 Makefile 教學課程經過布魯斯·麥克斯韋,了解其他使用方法的資訊
make
。 - 「使用 Makefile」(第 15 頁)和「Makefile 與 Shell 腳本」(第 62 頁)21世紀C經過本·克萊門斯。頁碼為第一版的頁碼。 (第二版可能更好,但我只有第一版。)
筆記
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