介绍
如果你有经验,你的Linux服务器上安装从源代码的软件,你有可能遇到的make
工具。此工具主要用于自动编译和构建程序。它允许应用程序的作者轻松地布置构建该特定项目所需的步骤。 虽然make是为了自动化软件编译而创建的,但是该工具以足够灵活的方式设计,可以用来自动化几乎任何可以从命令行完成的任务。在本指南中,我们将讨论如何重新使用make来自动执行按顺序执行的重复任务。 我们将在Ubuntu 12.04 VPS上演示这一点,但是它在几乎任何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中,我们遵循特定的格式。 Make以这种方式使用目标,源和命令的概念:
target: source
command
这种对齐和格式非常重要,因为make是挑剔的。我们将在这里讨论这些组件的格式和含义:
目标
目标是用户指定的名称,用于引用一组命令。认为它类似于编程语言中的简单函数。 目标在左侧列上对齐,是连续字(无空格),并以冒号(:)结尾。 当调用make时,我们可以通过键入指定一个目标:make target_name然后Make将检查Makefile并执行与该目标相关的命令。
资源
源是对文件或其他目标的引用。它们表示与其关联的目标的先决条件或依赖关系。 例如,你可以有你的make文件的一部分,看起来像这样:target1: target2
target1_command
target2:
target2_command
在这个例子中,我们可以这样调用target1:
make target1
Make然后转到Makefile并搜索“target1”目标。然后它将检查以查看是否指定了任何来源。 它会找到“target2”源依赖关系,并临时跳转到该目标。 从那里,它将检查target2是否列出了任何源。没有的话,那么它将继续执行“TARGET2
命令”。
此时,make将到达“target2”命令列表的末尾,并将控制权返回到“target1”目标。
然后,它会执行“目标1的命令”,然后退出。 源可以是文件或目标本身。 Make使用文件时间戳来查看自上次调用以来文件是否已更改。如果对源文件进行了更改,则重新运行该目标。否则,它将依赖关系标记为已满足,并继续到下一个源,或命令,如果这是唯一的源。 一般的想法是,通过添加源,我们可以构建一系列依赖,必须在当前目标之前执行。您可以在任何目标后指定多个源,以空格分隔。你可以开始看到如何指定精心设计的任务序列。
命令
什么给make命令这样的灵活性是命令部分的语法是非常开放的。您可以指定要在目标下运行的任何命令。您可以添加任意数量的命令。 命令在目标声明之后的行上指定。它们由一个 制表符缩进。一些版本的make对于缩进命令部分的方式很灵活,但是一般来说,你应该坚持使用单个选项卡,以确保make能够识别你的意图。 使目标定义下的每个缩进线成为一个单独的命令。您可以根据需要添加任意多个缩进的线条和命令。 Make会一次只通过一个。 我们可以在命令之前放置一些东西,让它们以不同的方式处理它们:- - :一个命令之前破折号告诉make如果遇到错误不中止。例如,如果要对文件执行命令(如果它存在),这可能是有用的,如果不存在,则不执行任何操作。
- @:如果你带领的“@”符号的命令,该命令调用本身不会被打印到标准输出。这主要用于清理产生的输出。
附加功能
一些附加功能可以帮助您在Makefile中创建更复杂的规则链。变量
Make识别变量(或宏),它们作为简单的占位符在makefile中进行替换。最好在文件的顶部声明这些。 每个变量的名称完全大写。名称之后,等号将名称分配给右侧的值。举例来说,如果我们想定义安装目录/usr/bin
,我们可以在文件的顶部添加此:
INSTALLDIR=/usr/bin
稍后在文件中,我们可以使用以下语法引用此位置:
$(INSTALLDIR)
逃离换行
另一个有用的事情,我们可以做的是允许命令跨越多行。 我们可以在命令部分使用任何命令或shell功能。这包括通过用“\”结束行来转义换行符: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
这将执行此块,就像它是一个单行命令。事实上,我们可以把它写成一行,但它提高了可读性,大大打破了这样。 如果你要逃避行尾字符,确保在“\”之后没有任何额外的空格或制表符,否则你会得到一个错误。
文件Stapling规则
如果进行文件处理,您可以使用的一个附加功能是文件Stapling。这些是提供基于其扩展处理文件的方式的一般规则。 例如,如果你想处理一个目录中的所有.jpg文件,并使用ImageMagick套件将它们转换为.png文件,我们可以在我们的Makefile中这样:.SUFFIXES: .jpg .png
.jpg.png:
@echo converting $< to $@
convert $< $@
这里有一些我们需要看的东西。 第一部分是
.SUFFIXES:
声明。这告诉我们将在文件Stapling中使用的所有Stapling。在编译源代码中经常使用的一些Stapling,例如“.c”和“.o”文件被默认包含,并且不需要在此声明中标记。 下一部分是实际Stapling规则的声明。这基本上采取以下形式:
original_extension.target_extension:这不是一个实际的目标,但它将匹配任何调用具有第二个扩展名的文件,并从第一个扩展中的文件中构建它们。 在我们的例子中,如果我们的目录中有一个“file.jpg”,我们可以调用make来构建一个名为“file.png”的文件:
make file.png
使会看到PNG文件
.SUFFIXES
声明,看到了规则打造“巴纽”文件。然后它将在目录中查找“.png”替换为“.jpg”的目标文件。然后它将执行以下命令。 Stapling规则使用一些我们尚未介绍的变量。这些帮助使得基于当前处理的过程的不同信息来替代不同的信息:
- $:此变量包含比目标更近的依赖关系为当前的目标的清单。这些将是在执行此目标下的命令之前必须重新完成的目标。
- $ @:此变量是当前目标的名称。这允许我们引用您尝试创建的文件,即使此规则通过模式匹配。
- $ <:这是当前的依赖性的名称。在Stapling规则的情况下,这是用于创建目标的文件的名称。在我们的示例中,这将包含“file.jpg”
- $ *:该文件是与匹配分机剥下电流依赖性的名称。考虑这是目标文件和源文件之间的中间阶段。
创建转换Makefile
我们将创建一个Makefile,它将做一些图像处理,然后将文件上传到我们的文件服务器,以便我们的网站可以显示它们。 如果你想跟随,在开始之前,下载ImageMagick转换工具。这些是用于操作图像的容易的命令行工具,我们将在我们的脚本中使用它们:sudo apt-get update
sudo apt-get install imagemagick
在当前目录中,创建一个名为
Makefile
:
nano Makefile
在此文件中,我们将开始实施我们的转化目标。
将所有JPG文件转换为PNG
我们的服务器已设置为专门提供.png图片。因此,在上传之前,我们需要将任何.jpg文件转换为.png。 正如我们上面学到的,Stapling规则是一个伟大的方式做到这一点。我们将开始脱离与.SUFFIX
声明将列出我们之间的转换格式:
.SUFFIXES: .jpg .png
之后,我们可以制定一个规则,将.jpg文件更改为.png文件。我们可以用做
convert
命令来自ImageMagick套件。 convert命令很简单,其语法是:
convert from_file to_file要实现此命令,我们需要Stapling规则,指定我们开始的格式和我们结束的格式:
.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文件到当前目录。如果你没有手头的文件,你可以从DigitalOcean网站获得一个文件,输入:
wget https://www.youcl.com/uploads/assets/v2/badges/digitalocean-badge-blue.jpg
mv digitalocean-badge-blue.jpg badge.jpg
您可以测试你是否化妆文件被要求它创建一个远工作从而
badge.png
的文件:
make badge.png
converting badge.jpg to badge.png using ImageMagick...
conversion to badge.png successful!
请必去的Makefile文件,看巴纽在
.SUFFIXES
声明,然后去匹配Stapling规则。然后运行列出的命令。
创建文件列表
在这一点上,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
声明和Stapling规则,以反映我们现在处理的.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添加到Stapling列表中,并且还包括了我们规则的另一个Stapling匹配。
创建一些目标
我们现在有很多在我们的Makefile,但我们还没有任何正常的目标。让我们来解决这个问题,使我们可以通过我们的PNG
名单我们的Stapling规则:
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文件,并使用Stapling规则这样做。 现在,我们可以使用此命令将所有.jpg和.jpeg文件转换为.png文件:
make convert
让我们添加另一个目标。通常在将图像上传到服务器时执行的另一个任务是调整它们的大小。将您的图片设置为正确的大小可以确保用户在请求时能够随时调整图片大小。 一个ImageMagick的命令调用
mogrify
可以在我们需要的方式调整影像。假设我们的网站上显示的图片区域宽度为500像素。我们可以使用命令转换此区域:
mogrify -resize 500\> file.png
这将调整大于500px宽的任何图像以适应此区域,但不会Touch较小的图像。这是我们想要的。作为目标,我们可以添加此规则:
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目标现在可以转换和调整图像大小。
将文件上传到远程服务器
现在我们已经为网络准备好了我们的图片,我们可以创建一个目标,将它们上传到我们服务器上的静态图片目录。我们可以通过传递我们对转换后的文件列表要做到这一点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!有了这些最后一次Touch,如果你输入Makefile和.jpg或.jpeg文件的目录,你可以调用make没有任何参数来处理你的文件,将它们发送到你的服务器,然后删除你上传的.png文件。
make
正如你所看到的,很容易将任务串在一起,也可以在一个点上挑选一个进程。例如,如果您只想转换文件并需要在不同的服务器上托管它们,则只需使用webify目标。
结论
在这一点上,你应该有一个很好的一般如何使用Makefile。更具体地说,你应该知道如何使用make作为自动化大多数过程的工具。 虽然在某些情况下,编写一个简单的脚本可能更容易,但Makefile是一种在进程之间建立结构化,层次关系的简单方法。学习如何利用这个工具可以帮助使重复的任务简单。
作者:Justin Ellingwood