Makefileを使用してUbuntuVPSで繰り返しタスクを自動化する方法
序章
Linuxサーバーにソースコードからソフトウェアをインストールした経験がある場合は、おそらくmake
ユーティリティに出くわしたことでしょう。 このツールは、主にプログラムのコンパイルと構築を自動化するために使用されます。 これにより、アプリケーションの作成者は、その特定のプロジェクトを構築するために必要な手順を簡単にレイアウトできます。
makeはソフトウェアのコンパイルを自動化するために作成されましたが、ツールは、コマンドラインから実行できるほとんどすべてのタスクを自動化するために使用できる十分な柔軟性を備えて設計されています。 このガイドでは、makeを再利用して、順番に発生する反復タスクを自動化する方法について説明します。
これはUbuntu12.04VPSでデモンストレーションしますが、ほとんどすべてのLinuxサーバーで同様に動作するはずです。
Makeをインストールします
makeを使い始める前に、それをインストールする必要があります。
名前でインストールできますが、通常、ソフトウェアのコンパイルに役立つ他のツールと一緒にインストールされます。 これらはすべて、一般的に非常に便利なので、すべてインストールします。 makeとこれらの他のプログラムを含む「build-essential」と呼ばれるパッケージがあります。
sudo apt-get update sudo apt-get install build-essential
これで、通常の容量でもmakeを利用できるツールができました。
Makefileを理解する
makeコマンドが命令を受け取る主な方法は、Makefile
を使用することです。
マニュアルページから、makeがGNUmakefileという名前のファイルを検索し、次にmakefile、次にMakefileを検索することがわかります。 GNUmakefileはGNU固有のコマンド用であり、makefileはそれほど目立たないため、Makefileを使用することをお勧めします。
Makefileはディレクトリ固有です。つまり、makeは、これらのファイルを見つけるために呼び出されたディレクトリを検索します。 そのため、実行するタスクのルート、または作成するスクリプトを呼び出すのが最も理にかなっている場所にMakefileを配置する必要があります。
Makefile内では、特定の形式に従います。 Makeは、ターゲット、ソース、およびコマンドの概念を次のように使用します。
target: source command
makeは扱いにくいため、これの配置と形式は非常に重要です。 ここでは、これらの各コンポーネントの形式と意味について説明します。
目標
ターゲットは、コマンドのグループを参照するためのユーザー指定の名前です。 プログラミング言語の単純な関数に似ていると考えてください。
ターゲットは左側の列に配置され、連続した単語(スペースなし)であり、コロン(:)で終わります。
makeを呼び出すときは、次のように入力してターゲットを指定できます。
target_nameを作成します
次に、MakeはMakefileをチェックし、そのターゲットに関連付けられたコマンドを実行します。
ソース
ソースは、ファイルまたは他のターゲットへの参照です。 これらは、関連付けられているターゲットの前提条件または依存関係を表します。
たとえば、次のようなmakeファイルのセクションを作成できます。
target1: target2 target1_command target2: target2_command
この例では、次のようにtarget1を呼び出すことができます。
make target1
次に、MakeはMakefileに移動し、「target1」ターゲットを検索します。 次に、指定されたソースがあるかどうかを確認します。
「target2」のソース依存関係を見つけて、一時的にそのターゲットにジャンプします。
そこから、target2にソースがリストされているかどうかを確認します。 そうではないので、「target2_command」の実行に進みます。 この時点で、makeは「target2」コマンドリストの最後に到達し、制御を「target1」ターゲットに戻します。 次に、「target1_command」を実行して終了します。
ソースは、ファイルまたはターゲット自体のいずれかです。 Makeは、ファイルのタイムスタンプを使用して、ファイルが最後の呼び出し以降に変更されているかどうかを確認します。 ソースファイルに変更が加えられた場合、そのターゲットが再実行されます。 それ以外の場合は、依存関係が満たされているとマークされ、次のソース、またはそれが唯一のソースである場合はコマンドに進みます。
一般的な考え方は、ソースを追加することで、現在のターゲットの前に実行する必要のある依存関係のシーケンシャルセットを構築できるというものです。 ターゲットの後に、スペースで区切って複数のソースを指定できます。 手の込んだ一連のタスクを指定する方法を理解し始めることができます。
コマンド
makeコマンドにこのような柔軟性を与えるのは、構文のコマンド部分が非常にオープンであるということです。 ターゲットの下で実行したい任意のコマンドを指定できます。 コマンドはいくつでも追加できます。
コマンドは、ターゲット宣言の後の行で指定されます。 それらは1つのtab文字でインデントされます。 makeの一部のバージョンは、コマンドセクションをインデントする方法に柔軟性がありますが、一般に、makeが意図を認識できるように、単一のタブを使用する必要があります。
Makeは、ターゲット定義の下にあるすべてのインデントされた行を個別のコマンドと見なします。 必要な数のインデントされた行とコマンドを追加できます。 Makeはそれらを一度に1つずつ通過します。
コマンドの前に配置して、makeにそれらを異なる方法で処理するように指示できるものがいくつかあります。
- -:コマンドの前のダッシュは、エラーが発生した場合にnotを中止するようにmakeに指示します。 たとえば、ファイルが存在する場合はコマンドを実行し、存在しない場合は何もしない場合に便利です。
- @ :「@」記号を使用してコマンドをリードすると、コマンド呼び出し自体は標準出力に出力されません。 これは主に、makeが生成する出力をクリーンアップするためだけに使用されます。
追加機能
いくつかの追加機能は、Makefileでより複雑なルールチェーンを作成するのに役立ちます。
変数
Makeは、makefileで置換するための単純なプレースホルダーとして動作する変数(またはマクロ)を認識します。 これらはファイルの先頭で宣言するのが最善です。
各変数の名前は完全に大文字になっています。 名前に続いて、等号が右側の値に名前を割り当てます。 たとえば、インストールディレクトリを/usr/bin
として定義する場合は、ファイルの先頭に次のように追加できます。
INSTALLDIR=/usr/bin
ファイルの後半で、次の構文を使用してこの場所を参照できます。
$(INSTALLDIR)
改行をエスケープする
私たちができるもう1つの便利なことは、コマンドが複数の行にまたがることを許可することです。
コマンドセクション内で任意のコマンドまたはシェル機能を使用できます。 これには、行を「」で終了して改行文字をエスケープすることが含まれます。
target: source command1 arg1 arg2 arg3 arg4 \ arg5 arg6
if-thenステートメントなど、シェルのよりプログラム的な機能のいくつかを利用する場合、これはより重要になります。
target: source if [ "condition_1" == "condition_2" ];\ then\ command to execute;\ another command;\ else\ alternative command;\ fi
これにより、このブロックが1行のコマンドであるかのように実行されます。 実際、これを1行で書くこともできますが、このように分解すると読みやすさが大幅に向上します。
行末文字をエスケープする場合は、「」の後に余分なスペースやタブがないことを確認してください。そうしないと、エラーが発生します。
ファイルサフィックスルール
ファイル処理を行う場合に使用できる追加機能は、ファイルのサフィックスです。 これらは、拡張子に基づいてファイルを処理する方法を提供する一般的なルールです。
たとえば、ディレクトリ内のすべての.jpgファイルを処理し、ImageMagickスイートを使用してそれらを.pngファイルに変換する場合、Makefileに次のようなものを含めることができます。
.SUFFIXES: .jpg .png .jpg.png: @echo converting $< to $@ convert $< $@
ここで確認する必要があることがいくつかあります。
最初の部分は.SUFFIXES:
宣言です。 これは、ファイルのサフィックスで使用するすべてのサフィックスについてmakeに通知します。 「.c」や「.o」ファイルなど、ソースコードのコンパイルで頻繁に使用されるいくつかのサフィックスは、デフォルトで含まれているため、この宣言でラベル付けする必要はありません。
次の部分は、実際のサフィックスルールの宣言です。 これは基本的に次の形式を取ります。
original_extension 。 target_extension :
これは実際のターゲットではありませんが、2番目の拡張子を持つファイルの呼び出しと一致し、最初の拡張子のファイルからそれらを構築します。
この場合、ディレクトリに「file.jpg」がある場合は、次のようにmakeを呼び出して、「file.png」というファイルを作成できます。
make file.png
Makeは、.SUFFIXES
宣言でpngファイルを確認し、「。png」ファイルを作成するためのルールを確認します。 次に、ディレクトリ内で「.png」が「.jpg」に置き換えられたターゲットファイルを検索します。 その後、次のコマンドを実行します。
サフィックスルールは、まだ導入されていないいくつかの変数を使用します。 これらは、プロセスの現在の部分に基づいて、さまざまな情報を置き換えるのに役立ちます。
- $?:この変数には、ターゲットよりも新しい現在のターゲットの依存関係のリストが含まれています。 これらは、このターゲットの下でコマンドを実行する前に再実行する必要があるターゲットになります。
- $ @ :この変数は現在のターゲットの名前です。 これにより、このルールがパターンによって一致した場合でも、作成しようとしているファイルを参照できます。
- $ < :これは現在の依存関係の名前です。 サフィックスルールの場合、これはターゲットの作成に使用されるファイルの名前です。 この例では、これには「file.jpg」が含まれます。
- $ * :このファイルは、一致する拡張子が削除された現在の依存関係の名前です。 これは、ターゲットファイルとソースファイルの間の中間段階であると考えてください。
変換メイクファイルを作成する
画像操作を行うMakefileを作成し、ファイルをファイルサーバーにアップロードして、Webサイトで表示できるようにします。
フォローしたい場合は、始める前に、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コマンドは単純で、その構文は次のとおりです。
from_fileから_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: @エコー ImageMagickを使用して$<を$@に変換… @変換 $ <$ @ @エコー $ @への変換が成功しました!
この時点で、ファイルを保存して閉じ、テストできるようにする必要があります。
jpgファイルを現在のディレクトリに取得します。 手元にファイルがない場合は、DigitalOceanWebサイトから次のように入力してファイルを取得できます。
wget https://digitalocean.com/assets/v2/badges/digitalocean-badge-blue.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ファイルのリストを作成し、それらを変換した方がよいでしょう。 これを行うには、変換するすべてのファイルを保持する変数を作成します。
これを行う最良の方法は、ワイルドカードディレクティブを使用することです。
JPG_FILES=$(wildcard *.jpg)
次のように、bashワイルドカードを使用してターゲットを指定できます。
JPG_FILES=*.jpg
しかし、これには欠点があります。 .jpgファイルがない場合、これは実際には「* .jpg」というファイルに対して変換コマンドを実行しようとしますが、失敗します。
上記のワイルドカード構文は、現在のディレクトリにある.jpgファイルのリストをコンパイルします。存在しない場合は、変数を何にも設定しません。
これを行っている間、一般的な.jpgファイルのわずかな変化を処理するようにしてください。 これらの画像ファイルは、.jpgではなく.jpeg拡張子で表示されることがよくあります。 これらを適切な方法で処理するために、プログラムでそれらの名前を.jpgファイルに変更して、処理行を単純化することができます。
上記の行の代わりに、次の2つを使用します。
JPEG=$(wildcard *.jpg *.jpeg) ## Has .jpeg and .jpg files JPG=$(JPEG:.jpeg=.jpg) ## Only has .jpg files
最初の行は、現在のディレクトリにある.jpgファイルと.jpegファイルのリストをコンパイルし、JPEG
という変数に保存します。
2行目はこの変数を参照し、単純な名前変換を行って、.jpegで終わるJPEG
変数の名前を.jpgで終わる名前に変換します。 これは次の構文で行われます。
$(VARNAME:.convert_from=.convert_to)
これらの2行の終わりに、.jpgファイル名のみを含むJPG
という新しい変数があります。 これらのファイルの一部は、実際には.jpegファイルであるため(実際の名前変更は行われていません)、実際にはシステムに存在しない可能性があります。 このリストを使用して、作成する.pngファイルの new リストを作成するだけなので、これは問題ありません。
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を追加し、ルールに一致する別のサフィックスも含めました。
いくつかのターゲットを作成する
現在、Makefileにはかなりの数がありますが、通常のターゲットはまだありません。 PNG
リストをサフィックスルールに渡すことができるように、これを修正しましょう。
JPEG = $(wildcard * .jpg * .jpeg)JPG = $(JPEG:.jpeg = .jpg)PNG = $(JPG:.jpg = .png).SUFFIXES:.jpg .jpeg .png 変換:$(PNG) .jpeg.png .jpg.png: @エコー ImageMagickを使用して$<を$@に変換… @変換 $ <$ @ @エコー $ @への変換が成功しました!
この新しいターゲットは、要件として収集した.pngファイル名を一覧表示するだけです。 次に、Makeは、.pngファイルを取得する方法があるかどうかを確認し、サフィックスルールを使用して取得します。
これで、このコマンドを使用して、すべての.jpgファイルと.jpegファイルを.pngファイルに変換できます。
make convert
別のターゲットを追加しましょう。 サーバーに画像をアップロードするときに一般的に行われるもう1つのタスクは、画像のサイズを変更することです。 画像を正しいサイズにすることで、ユーザーが画像を要求したときにその場で画像のサイズを変更する必要がなくなります。
mogrify
と呼ばれるImageMagickコマンドは、必要な方法で画像のサイズを変更できます。 私たちのサイトで画像が表示される領域の幅が500pxだとします。 次のコマンドを使用して、この領域に変換できます。
mogrify -resize 500\> file.png
これにより、幅が500pxを超える画像はこの領域に合わせてサイズ変更されますが、小さい画像には影響しません。 これが私たちが望んでいることです。 ターゲットとして、次のルールを追加できます。
サイズ変更:$(PNG) @エコーファイルのサイズ変更… @mogrify -648のサイズを変更>$(PNG) @エコーサイズ変更が完了しました。
これを次のようにファイルに追加できます。
JPEG = $(wildcard * .jpg * .jpeg)JPG = $(JPEG:.jpeg = .jpg)PNG = $(JPG:.jpg = .png).SUFFIXES:.jpg .jpeg .png 変換:$(PNG) サイズ変更:$(PNG) @エコーファイルのサイズ変更… @mogrify -648のサイズを変更>$(PNG) @エコーサイズ変更が完了しました。 .jpeg.png .jpg.png: @エコー ImageMagickを使用して$<を$@に変換… @変換 $ <$ @ @エコー $ @への変換が成功しました!
これで、これら2つのターゲットを別のターゲットの依存関係としてつなぎ合わせることができます。
JPEG = $(wildcard * .jpg * .jpeg)JPG = $(JPEG:.jpeg = .jpg)PNG = $(JPG:.jpg = .png).SUFFIXES:.jpg .jpeg .png webify:サイズ変更を変換 変換:$(PNG) サイズ変更:$(PNG)@echoファイルのサイズ変更…@mogrify -resize 648> $(PNG) @echo サイズ変更が完了しました! .jpeg.png .jpg.png: @エコー ImageMagickを使用して$<を$@に変換… @変換 $ <$ @ @エコー $ @への変換が成功しました!
暗黙的にresizeがconvertと同じコマンドを実行することに気付くかもしれません。 常にそうであるとは限らない場合に備えて、両方を指定します。 変換には、将来、より複雑な処理が含まれる可能性があります。
webifyターゲットは、画像を変換およびサイズ変更するようになりました。
リモートサーバーにファイルをアップロードする
これで画像をWeb用に準備できたので、サーバー上の静的画像ディレクトリに画像をアップロードするためのターゲットを作成できます。 これを行うには、変換されたファイルのリストをscp
に渡します。
ターゲットは次のようになります。
アップロード: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 アップロード:webify scp $(PNG)root @ip _address:/ path / to / static / images webify:サイズ変更を変換 変換:$(PNG) サイズ変更:$(PNG)@echoファイルのサイズ変更…@mogrify -resize 648> $(PNG) @echo サイズ変更が完了しました! .jpeg.png .jpg.png: @エコー ImageMagickを使用して$<を$@に変換… @変換 $ <$ @ @エコー $ @への変換が成功しました!
掃除
リモートサーバーにアップロードされた後、すべてのローカル.pngファイルを削除するためのクリーニングオプションを追加しましょう。
クリーン:rm * .png
これで、ファイルをリモートサーバーにアップロードした後にこれを呼び出す別のターゲットを上部に追加できます。 これが最も完全なターゲットであり、デフォルトにしたいターゲットです。
これを指定するために、最初に使用可能なターゲットとして配置します。 これがデフォルトとして使用されます。 慣例により、これを「すべて」と呼びます。
JPEG = $(wildcard * .jpg * .jpeg)JPG = $(JPEG:.jpeg = .jpg)PNG = $(JPG:.jpg = .png).SUFFIXES:.jpg .jpeg .png すべて:クリーンにアップロード アップロード:webify scp $(PNG)root @ip _address:/ path / to / static / images webify:サイズ変更を変換 変換:$(PNG) サイズ変更:$(PNG)@echoファイルのサイズ変更…@mogrify -resize 648> $(PNG) @echo サイズ変更が完了しました! クリーン:rm * .png .jpeg.png .jpg.png: @エコー ImageMagickを使用して$<を$@に変換… @変換 $ <$ @ @エコー $ @への変換が成功しました!
これらの最後の仕上げで、Makefileと.jpgまたは.jpegファイルを使用してディレクトリに入ると、引数なしでmakeを呼び出してファイルを処理し、サーバーに送信してから、アップロードした.pngファイルを削除できます。
make
ご覧のとおり、タスクをつなぎ合わせたり、特定の時点までのプロセスを選択したりするのは簡単です。 たとえば、ファイルを変換するだけで、別のサーバーでホストする必要がある場合は、webifyターゲットを使用できます。
結論
この時点で、Makefileの一般的な使用方法を理解しているはずです。 具体的には、ほとんどの種類の手順を自動化するためのツールとしてmakeを使用する方法を知っておく必要があります。
単純なスクリプトを作成する方が簡単な場合もありますが、Makefileは、プロセス間に構造化された階層関係を設定する簡単な方法です。 このツールを活用する方法を学ぶことは、反復的なタスクを簡単にするのに役立ちます。