多虧了這篇文章,我的開發效率遠遠領先於我的同事

騰訊雲加社群發表於2018-09-05

歡迎大家前往騰訊雲+社群,獲取更多騰訊海量技術實踐乾貨哦~

本文由獨木橋先生發表於雲+社群專欄

介紹

如果您有從Linux伺服器上的原始碼安裝軟體的經驗,您可能會遇到make實用程式。該工具主要用於自動編譯和構建程式。它允許應用程式的作者輕鬆地佈置構建該特定專案所需的步驟。

儘管make是為自動化軟體編譯而建立的,但該工具的設計靈活性足以使其可以自動執行幾乎任何可以從命令列完成的任務。在本教程中,我們將討論如何重新調整make以自動執行按順序發生的重複性任務。

我們將在Ubuntu上進行演示,但它應該在幾乎任何Linux伺服器上以類似的方式執行。

安裝Make

在我們開始使用make之前,我們需要安裝它。

雖然我們可以按名稱安裝它,但它通常與其他幫助您編譯軟體的工具一起安裝。我們將安裝所有這些因為它們總體上非常有用。有一個名為“build-essential”的包,其中包含make和其他程式:

sudo apt-get update
sudo apt-get install build-essential
複製程式碼

現在,您擁有的工具可以讓您以通常的容量利用make。

瞭解Makefile

make命令接收指令的主要方法是使用Makefile。

從手冊頁中,我們可以看到make將查詢名為GNUmakefile的檔案,然後查詢makefile,然後查詢Makefile。它建議您使用Makefile,因為GNUmakefile是針對GNU特定的命令,而makefile並不突出。

Makefile是特定於目錄的,這意味著make將在呼叫它的目錄中搜尋以查詢這些檔案。因此,我們應該將Makefile放在我們將要執行的任務的根目錄中,或者呼叫我們將要編寫的指令碼最有意義的地方。

在Makefile中,我們遵循特定的格式。以這種方式使用目標,源和命令的概念:

target: source
	command
複製程式碼

這種對齊和格式非常重要,因為make很挑剔。我們將在這裡討論每個元件的格式和含義:

target

target是使用者指定的名稱,用於引用一組命令。可以認為它類似於程式語言中的簡單函式。

target在左側列上對齊,是連續的單詞(無空格),以冒號(:)結尾。

呼叫make時,我們可以通過輸入以下內容來指定target:

make target_name
複製程式碼

然後,Make將檢查Makefile並執行與該target關聯的命令。

source

source是對檔案或其他target的引用。它們代表與之關聯的目標的準備或依賴關係。

例如,您可以將make檔案的一部分看起來像這樣:

target1: target2
    target1_command

target2:
    target2_command
複製程式碼

在這個例子中,我們可以像這樣呼叫target1:

make target1
複製程式碼

然後Make將轉到Makefile並搜尋“target1”目標。然後它會檢查是否有指定的來源。

它會找到“target2”源依賴項並暫時跳轉到該目標。

從那裡,它將檢查target2是否列出了任何來源。它沒有,所以它將繼續執行“target2命令”。此時,make將到達“target2”命令列表的末尾,並將控制權傳遞迴“target1”目標。然後它將執行“target1命令”並退出。

source可以是檔案或目標本身。使用檔案時間戳來檢視自上次呼叫以來檔案是否已更改。如果已對原始檔進行了更改,則重新執行該目標。否則,它將依賴關係標記為已完成並繼續到下一個源,或者命令(如果這是唯一的源)。

一般的想法是,通過新增源,我們可以構建一組必須在當前目標之前執行的順序依賴項。您可以在任何目標之後指定多個以空格分隔的源。您可以開始瞭解如何指定精細的任務序列。

command

make命令具有這種靈活性的原因是語法的命令部分是非常開放的。您可以指定要在目標下執行的任何命令。您可以根據需要新增任意數量的命令。

命令在目標宣告後的行上指定。它們由一個製表符縮排。某些版本的make對於縮排命令部分的方式很靈活,但一般來說,您應該堅持使用單個選項卡以確保make能夠識別您的意圖。

Make將目標定義下的每個縮排行視為單獨的命令。您可以根據需要新增任意數量的縮排行和命令。Make會一次一個地瀏覽它們。

在命令告訴make以不同的方式處理它們之前,我們可以放置一些東西:

  • -:命令前的破折號告訴make如果遇到錯誤則不中止。例如,如果要對檔案執行命令(如果存在),則此操作可能很有用,如果不存在則不執行任何操作。
  • @:如果使用“@”符號引導命令,則命令呼叫本身不會列印到標準輸出。這主要用於清理產生的輸出。

附加功能

一些其他功能可以幫助您在Makefile中建立更復雜的規則鏈。

變數

Make識別變數(或巨集),它在makefile中作為替換的簡單佔位符。最好在檔案頂部宣告這些內容。每個變數的名稱都完全大寫。在名稱後面,等號將名稱分配給右側的值。例如,如果我們想要將安裝目錄定義為/usr/bin,我們可以在檔案頂部新增:

INSTALLDIR=/usr/bin
複製程式碼

稍後在檔案中,我們可以使用以下語法引用此位置:

$(INSTALLDIR)
複製程式碼

跨越多行

我們可以做的另一個有用的事情是允許命令跨越多行。

“\”來表示跨行:

target: source
    command1 arg1 arg2 arg3 arg4 \
    arg5 arg6
複製程式碼

如果你利用shell的一些更多程式設計功能,比如if-then語句,這就變得更加重要:

target: source
    if [ "condition_1" == "condition_2" ];\
    then\
        command to execute;\
        another command;\
    else\
        alternative command;\
    fi
複製程式碼

這將執行此塊,就好像它是一行命令一樣。事實上,我們可以把它寫成一行,但它提高了可讀性,大大地將其分解為這樣。

如果要轉義行尾字元,請確保在“\”後面沒有任何多餘的空格或製表符,否則您將收到錯誤。

檔案字尾規則

如果進行檔案處理,可以使用的另一個功能是檔案字尾。這些是一般規則,提供了一種基於副檔名處理檔案的方法。

例如,如果您想要處理目錄中的所有.jpg檔案並使用ImageMagick套件將它們轉換為.png檔案,我們可以在Makefile中使用以下內容:

.SUFFIXES: .jpg .png

.jpg.png:
    @echo converting $< to $@
    convert $< $@
複製程式碼

第一部分是.SUFFIXES:宣告。這告訴make我們將在檔案字尾中使用的所有字尾。預設情況下包含一些常用於編譯原始碼的字尾,如“.c”和“.o”檔案,不需要在此宣告中標記。

下一部分是實際字尾規則的宣告。這基本上採取以下形式:

original_extension.target_extension:
複製程式碼

這不是一個實際的目標,但它將匹配任何具有第二個副檔名的檔案的呼叫,並在第一個副檔名中將它們構建出檔案。

在我們的例子中,如果在我們的目錄中有“file.jpg”,我們可以呼叫make這樣構建一個名為“file.png”的檔案:

make file.png
複製程式碼

Make將在.SUFFIXES宣告中看到png檔案,並檢視用於建立“.png”檔案的規則。然後它將在目錄中查詢“.png”替換為“.jpg”的目標檔案。然後它將執行後面的命令。

字尾規則使用了一些我們尚未介紹的變數。這些幫助根據當前流程的哪個部分替換不同的資訊:

  • $?:此變數包含當前目標的比目標更新的依賴項列表。這些將是在執行此目標下的命令之前必須重新完成的目標。
  • $@:此變數是當前目標的名稱。這允許我們引用您嘗試製作的檔案,即使此規則通過模式匹配。
  • $<:這是當前依賴項的名稱。對於字尾規則,這是用於建立目標的檔案的名稱。在我們的示例中,這將包含“file.jpg”
  • $*:此檔案是剝離匹配副檔名的當前依賴項的名稱。將此視為目標檔案和原始檔之間的中間階段。

建立轉換Makefile

我們將建立一個Makefile,它將執行一些影像處理,然後將檔案上傳到我們的檔案伺服器,以便我們的網站可以顯示它們。

如果您想跟隨,請在開始之前下載ImageMagick轉換工具。這些是用於操作影像的簡單命令列工具,我們將在指令碼中使用它們:

sudo apt-get update
sudo apt-get install imagemagick
複製程式碼

在當前目錄中,建立一個名為Makefile的檔案:

nano Makefile
複製程式碼

在此檔案中,我們將開始實施轉化目標。

將所有JPG檔案轉換為PNG

我們的伺服器已設定為專門為.png影像提供服務。因此,我們需要在上傳之前將任何.jpg檔案轉換為.png。

如上所述,字尾規則是一種很好的方法。我們將從.SUFFIX宣告開始,列出我們之間轉換的格式:

.SUFFIXES: .jpg .png
複製程式碼

之後,我們可以制定一個將.jpg檔案更改為.png檔案的規則。我們可以使用ImageMagick套件中的convert命令來完成此操作。convert命令很簡單,其語法是:

convert from_file to_file
複製程式碼

要實現此命令,我們需要字尾規則來指定我們開始使用的格式以及我們結束的格式:

.SUFFIXES: .jpg .png

.jpg.png:           ## This is the suffix rule declaration
複製程式碼

現在我們有了匹配的規則,我們需要實現實際的轉換步驟。

因為我們不確切知道這裡將匹配什麼檔名,所以我們需要使用我們學到的變數。具體來說,我們需要引用$<作為原始檔案,以及$@作為我們要轉換的檔案。如果我們將此與我們對convert命令的瞭解結合起來,我們就會得到以下規則:

.SUFFIXES: .jpg .png

.jpg.png:
    convert $< $@
複製程式碼

讓我們新增一些功能,以便明確告訴我們echo語句發生了什麼。我們將在新命令和我們已經擁有的命令之前包含“@”符號,以便在執行時列印實際命令:

.SUFFIXES: .jpg .png

.jpg.png:
    @echo converting $< to $@ using ImageMagick...
    @convert $< $@
    @echo conversion to $@ successful!
複製程式碼

此時,我們應該儲存並關閉檔案,以便我們可以測試它。

獲取jpg檔案到當前目錄。如果您手邊沒有檔案,可以輸入以下內容從騰訊雲網站獲取:

wget https://ask.qcloudimg.com/raw/qctrain/yehe-b5f4bb2e421e9/1kp7y81up9.jpg
mv digitalocean-badge-blue.jpg badge.jpg
複製程式碼

您可以通過要求它建立badge.png檔案來測試您的make檔案是否正常工作:

make badge.png
複製程式碼
converting badge.jpg to badge.png using ImageMagick...
conversion to badge.png successful!
複製程式碼

Make將轉到Makefile,請參閱.SUFFIXES宣告中的.png,然後轉到匹配的字尾規則。然後執行列出的命令。

建立檔案列表

此時,如果我們明確告訴它我們想要該檔案,make可以建立一個.png檔案。

如果它只是在當前目錄中建立一個.jpg檔案列表然後轉換它們會更好。我們可以通過建立一個包含要轉換的所有檔案的變數來實現。

執行此操作的最佳方法是使用wildcard指令:

JPG_FILES=$(wildcard *.jpg)
複製程式碼

我們可以用這樣的bash萬用字元指定一個目標:

JPG_FILES=*.jpg
複製程式碼

但這有一個缺點。如果沒有.jpg檔案,這實際上會嘗試在名為“*.jpg”的檔案上執行轉換命令,這將失敗。

我們上面提到的萬用字元語法編譯當前目錄中的.jpg檔案列表,如果不存在,則不會將變數設定為任何內容。

雖然我們這樣做,但我們應該嘗試處理常見的.jpg檔案的輕微變化。這些影像檔案通常使用.jpeg副檔名而不是.jpg。為了以理智的方式處理這些問題,我們可以將程式中的名稱更改為.jpg檔案,以便我們的處理行更簡單:

我們將使用以下兩個代替上述內容:

JPEG=$(wildcard *.jpg *.jpeg)     ## Has .jpeg and .jpg files
JPG=$(JPEG:.jpeg=.jpg)            ## Only has .jpg files
複製程式碼

第一行編譯當前目錄中的.jpg和.jpeg檔案列表,並將它們儲存在一個名為JPEG的變數中。

第二行引用此變數並執行簡單的名稱轉換,將JPEG變數中以.jpeg結尾的名稱轉換為以.jpg結尾的名稱。

這是通過以下語法完成的:

$(VARNAME:.convert_from=.convert_to)
複製程式碼

在這兩行的末尾,我們將有一個名為JPG的新變數,它只包含.jpg檔名。其中一些檔案可能實際上並不存在於系統上,因為它們實際上是.jpeg檔案(沒有發生實際的重新命名)。這沒關係,因為我們只使用此列表來建立我們要建立的.png檔案的新列表:

JPEG=$(wildcard *.jpg *.jpeg)
JPG=$(JPEG:.jpeg=.jpg)
PNG=$(JPG:.jpg=.png)
複製程式碼

現在,我們有一個我們想要在變數PNG中請求的檔案列表。此列表僅包含.png檔名,因為我們進行了另一個名稱轉換。現在,此目錄中的每個.jpg或.jpeg檔案都用於編譯我們要建立的.png檔案列表。

我們還需要更新.SUFFIXES宣告和字尾規則,以反映我們現在正在處理.jpeg檔案:

JPEG=$(wildcard *.jpg *.jpeg)
JPG=$(JPEG:.jpeg=.jpg)
PNG=$(JPG:.jpg=.png)
.SUFFIXES: .jpg .jpeg .png

.jpeg.png .jpg.png:
    @echo converting $< to $@ using ImageMagick...
    @convert $< $@
    @echo conversion to $@ successful!
複製程式碼

如您所見,我們已將.jpeg新增到字尾列表中,併為我們的規則新增了另一個字尾匹配項。

建立一些Targets

我們現在在Makefile中有很多,但我們還沒有任何正常的目標。讓我們解決這個問題,以便我們可以將PNG列表傳遞給字尾規則:

JPEG=$(wildcard *.jpg *.jpeg)
JPG=$(JPEG:.jpeg=.jpg)
PNG=$(JPG:.jpg=.png)
.SUFFIXES: .jpg .jpeg .png

convert: $(PNG)

.jpeg.png .jpg.png:
    @echo converting $< to $@ using ImageMagick...
    @convert $< $@
    @echo conversion to $@ successful!
複製程式碼

所有這些新目標都列出了我們收集的.png檔名作為要求。然後看看是否有一種方法可以獲取.png檔案並使用字尾規則來執行此操作。

現在,我們可以使用此命令將我們所有的.jpg和.jpeg檔案轉換為.png檔案:

make convert
複製程式碼

讓我們新增另一個目標。將影像上傳到伺服器時通常要完成的另一項任務是調整它們的大小。使影像具有正確的大小將使使用者無需在請求時動態調整影像大小。

ImageMagick的mogrify命令可以按照我們需要的方式調整影像大小。假設我們的圖片將在我們的網站上顯示的區域是500px寬。我們可以使用以下命令轉換此區域:

mogrify -resize 500\> file.png
複製程式碼

這將調整大於500px寬的任何影像以適應此區域,但不會觸控較小的影像。這就是我們想要的。作為目標,我們可以新增以下規則:

resize: $(PNG)
    @echo resizing file...
    @mogrify -resize 648\> $(PNG)
    @echo resizing is complete!
複製程式碼

我們可以像這樣新增到我們的檔案中:

JPEG=$(wildcard *.jpg *.jpeg)
JPG=$(JPEG:.jpeg=.jpg)
PNG=$(JPG:.jpg=.png)
.SUFFIXES: .jpg .jpeg .png

convert: $(PNG)

resize: $(PNG)
    @echo resizing file...
    @mogrify -resize 648\> $(PNG)
    @echo resizing is complete!

.jpeg.png .jpg.png:
    @echo converting $< to $@ using ImageMagick...
    @convert $< $@
    @echo conversion to $@ successful!
複製程式碼

現在,我們可以將這兩個目標串在一起作為另一個目標的依賴關係:

JPEG=$(wildcard *.jpg *.jpeg)
JPG=$(JPEG:.jpeg=.jpg)
PNG=$(JPG:.jpg=.png)
.SUFFIXES: .jpg .jpeg .png

webify: convert resize

convert: $(PNG)

resize: $(PNG)
    @echo resizing file...
    @mogrify -resize 648\> $(PNG)
    @echo resizing is complete!

.jpeg.png .jpg.png:
    @echo converting $< to $@ using ImageMagick...
    @convert $< $@
    @echo conversion to $@ successful!
複製程式碼

您可能會注意到隱式調整大小將執行與convert相同的命令。我們將指定它們兩者,儘管並非總是如此。轉換可以在將來包含更精細的處理。

webify目標現在可以轉換影像並調整其大小。

將檔案上載到遠端伺服器

現在我們已經為Web準備好了映象,我們可以建立一個目標,將它們上傳到我們伺服器上的靜態影像目錄。

我們可以通過將轉換後的檔案列表傳遞給scp來實現:

我們的目標看起來像這樣:

upload: webify
    scp $(PNG) root@ip_address:/path/to/static/images
複製程式碼

這會將我們的所有檔案上傳到遠端伺服器。我們的檔案現在看起來像這樣:

JPEG=$(wildcard *.jpg *.jpeg)
JPG=$(JPEG:.jpeg=.jpg)
PNG=$(JPG:.jpg=.png)
.SUFFIXES: .jpg .jpeg .png

upload: webify
    scp $(PNG) root@ip_address:/path/to/static/images

webify: convert resize

convert: $(PNG)

resize: $(PNG)
    @echo resizing file...
    @mogrify -resize 648\> $(PNG)
    @echo resizing is complete!

.jpeg.png .jpg.png:
    @echo converting $< to $@ using ImageMagick...
    @convert $< $@
    @echo conversion to $@ successful!
複製程式碼

清理

讓我們新增一個清理選項,以便在將所有本地.png檔案上傳到遠端伺服器後將其刪除:

clean:
    rm *.png
複製程式碼

現在,我們可以在頂部新增另一個目標,在我們將檔案上傳到遠端伺服器之後呼叫此目標。這將是最完整的目標,也是我們想要預設的目標。

為了指定這一點,我們將把它作為第一個可用的目標。這將用作預設值。我們將按慣例稱之為“全部”:

JPEG=$(wildcard *.jpg *.jpeg)
JPG=$(JPEG:.jpeg=.jpg)
PNG=$(JPG:.jpg=.png)
.SUFFIXES: .jpg .jpeg .png

all: upload clean

upload: webify
    scp $(PNG) root@ip_address:/path/to/static/images

webify: convert resize

convert: $(PNG)

resize: $(PNG)
    @echo resizing file...
    @mogrify -resize 648\> $(PNG)
    @echo resizing is complete!

clean:
    rm *.png

.jpeg.png .jpg.png:
    @echo converting $< to $@ using ImageMagick...
    @convert $< $@
    @echo conversion to $@ successful
複製程式碼

通過這些最後的操作,如果您使用Makefile和.jpg或.jpeg檔案進入目錄,您可以呼叫make而不使用任何引數來處理檔案,將它們傳送到您的伺服器,然後刪除您上傳的.png檔案。

make
複製程式碼

正如您所看到的,很容易將任務串聯在一起,並且可以選擇一個流程到某一點。例如,如果您只想轉換檔案並需要在不同的伺服器上託管它們,則可以使用webify目標。

結論


此時,您應該很好地瞭解如何使用Makefile。更具體地說,您應該知道如何使用make作為自動執行大多數過程的工具。

雖然在某些情況下編寫一個簡單的指令碼可能更容易,但Makefile是在流程之間建立結構化的層次關係的簡單方法。學習如何利用這個工具可以幫助簡化重複性任務。更多Makefile的教程請前往騰訊雲+社群學習更多知識。


參考文獻:《How To Use Makefiles to Automate Repetitive Tasks on an Ubuntu VPS》

問答

BeautifulSoup和Scrapy爬蟲之間的區別? 相關閱讀

騰訊雲資料庫回檔解決方案

大資料在教育行業的研究與應用

看看上下文對映的清晰檢視

【每日課程推薦】新加坡南洋理工大學博士,帶你深度學習NLP技術

此文已由作者授權騰訊雲+社群釋出,更多原文請點選

搜尋關注公眾號「雲加社群」,第一時間獲取技術乾貨,關注後回覆1024 送你一份技術課程大禮包!

海量技術實踐經驗,盡在雲加社群

相關文章