多虧了這篇文章,我的開發效率遠遠領先於我的同事
歡迎大家前往騰訊雲+社區,獲取更多騰訊海量技術實踐幹貨哦~
本文由獨木橋先生發表於雲+社區專欄
介紹
如果您有從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 送你一份技術課程大禮包!
海量技術實踐經驗,盡在雲加社區!
多虧了這篇文章,我的開發效率遠遠領先於我的同事