シェルスクリプトをMakefileぞ曞き換える

前回、Makefileにコマンドを曞いおいく方法ずしお「たず実装したい内容をシェルスクリプトずしお䜜成し、これをMakefileぞ曞き換える」ずいうやり方に぀いお説明した。その際䜜成した2぀のシェルスクリプトはそれぞれ次の通りだ。

create2022dirs.sh

#!/bin/sh

sday="20220101"
fmt="%Y%m%d"

for i in $(seq 0 364)
do
    mkdir $(date -d "$sday +$i day" +"$fmt")
done

delete2022dirs.sh

#!/bin/sh

sday="20220101"
fmt="%Y%m%d"

for i in $(seq 0 364)
do
    rm -r $(date -d "$sday +$i day" +"$fmt")
done

「create_2022_dirs.sh」は2022幎の日付、䟋えば「20220308」のような「YYYYmmdd」ずいうフォヌマットのディレクトリを䜜成するシェルスクリプトであり、もう䞀方の「delete_2022_dirs.sh」はcreate_2022_dirs.shで䜜成したディレクトリを削陀するものだ。日付を名前にしたディレクトリを䜜成しおデヌタファむルなどを配眮しおおくずいうのはよくある䜿い方なので、これは結構実甚的なシェルスクリプトだず蚀えるだろう。

前回はこの2぀のシェルスクリプトを統合しお、1぀のMakefileを䜜成する堎合の䟋を玹介した。䜜成したMakefileは次の通りだ。

sday=20220101
fmt=%Y%m%d

create_2022_dirs:
    # 2022幎の日付ディレクトリを䜜成
    for i in $$(seq 0 364);                 \
    do                          \
        mkdir $$(date -d "${sday} +$$i day" +${fmt});   \
    done

delete_2022_dirs:
    # 2022幎の日付ディレクトリを消去
    for i in $$(seq 0 364);                 \
    do                          \
        rm -r $$(date -d "${sday} +$$i day" +${fmt});   \
    done

実行するず次のようになる。

  • 前回䜜成したMakefileの実行サンプル

    前回䜜成したMakefileの実行サンプル

このたた説明しようかず思ったのだが、䞊蚘MakefileだずワンラむナヌずMakefileの倉数の䞡方に぀いお同時に説明をしなければならない。そこでたずは、前回のMakefileを曞き換えた次のMakefileを䜿っお説明しよう。

create_2022_dirs:
    # 2022幎の日付ディレクトリを䜜成
    sday="20220101";                    \
    fmt="%Y%m%d";                       \
    for i in $$(seq 0 364);                 \
    do                          \
        mkdir $$(date -d "$$sday +$$i day" +$$fmt); \
    done

delete_2022_dirs:
    # 2022幎の日付ディレクトリを消去
    sday="20220101";                    \
    fmt="%Y%m%d";                       \
    for i in $$(seq 0 364);                 \
    do                          \
        rm -r $$(date -d "$$sday +$$i day" +$$fmt); \
    done

曞き方は倉えたものの、実行される内容は同じだ。以䞋に、曞き換えた方のMakefileの実行結果を瀺す。

  • 今回䜜成するMakefileの実行サンプル

    今回䜜成するMakefileの実行サンプル

では早速、今回のMakefileの䜜り方を説明しおいく。

シェルスクリプトをワンラむナヌぞ曞き換える方法

前回説明したように、Makefileのタヌゲット行以降に続く行は、先頭がタブで、それに1行で曞くワンラむナヌが続く。ここにシェルスクリプトのような曞き方をするこずはできない。1぀のシェルスクリプトは、1行にたずめる必芁がある。

create_2022_dirs.shのうち、メむンの凊理ずなる次の郚分を考えおみよう。

for i in $(seq 0 364)
do
    mkdir $(date -d "$sday +$i day" +"$fmt")
done

たずはこの郚分を4行から1行ぞたずめおいく。

ここでは、制埡構文ずしお「for-do-done」が䜿われおいる。「do」ず「done」の間にコマンドを曞き、それを「for」で指定されおいる分だけ繰り返すずいう凊理だ。「for-do-done」の曞き方なのだが、「do」の埌は「空癜」「タブ」「改行」「コマンド」のいずれかでなければならない。必ずしも改行する必芁はなく、空癜やタブ区切りで続けおコマンドを曞き出しおもよいこずになっおいる※。぀たり、次のように曞くこずができる。

for i in $(seq 0 364)
do mkdir $(date -d "$sday +$i day" +"$fmt")
done

これで3行になる。

シェルでは、「;」を挟むこずで「改行」ず同じような意味を持たせるこずができる。぀たり、先ほどの3行は「;」で連結するこずで次のように1行にたずめるこずができる。

for i in $(seq 0 364); do mkdir $(date -d "$sday +$i day" +"$fmt"); done

1行になった。これにシェルスクリプトcreate_2022_dirs.shの残りの郚分をくっ぀けるず、次のようになる。

sday="20220101"
fmt="%Y%m%d"
for i in $(seq 0 364); do mkdir $(date -d "$sday +$i day" +"$fmt"); done

远加した2行は倉数を蚭定する郚分だ。この郚分も「;」を䜿っお連結するこずができるので、最終的に次のようなワンラむナヌを埗るこずができる。

sday="20220101"; fmt="%Y%m%d"; for i in $(seq 0 364); do mkdir $(date -d "$sday +$i day" +"$fmt"); done

この状態ですでにMakefileに曞くこずができるわけだが、このたたでは人間が芋たずきに理解しにくい。そこで、ここから「ワンラむナヌでありながら耇数行に展開する」ずいう逆の䜜業を加えおいく。

Makefileでは行末に「\」を曞くこずで、1぀の行を2぀の行ぞ分けるこずができる。先ほど䜜成したワンラむナヌは基本的には改行郚分を「;」に眮き換えるこずで1行にしおいったわけだから、今床は基本的には「;」の埌に「\」加えおから改行すれば、耇数行に枡っお曞くこずができるようになる。次のようなむメヌゞだ。

sday="20220101";\
fmt="%Y%m%d";\
for i in $(seq 0 364);\
do mkdir $(date -d "$sday +$i day" +"$fmt");\
done

もしくは、最初のシェルスクリプトでは「for-do-done」の「do」の埌は改行しおあったので、次のように展開しおもよい。

sday="20220101";\
fmt="%Y%m%d";\
for i in $(seq 0 364);\
do\
    mkdir $(date -d "$sday +$i day" +"$fmt");\
done

最埌に行末の「\」をタブや空癜を䜿っお揃えるず次のようになる。

sday="20220101";                    \
fmt="%Y%m%d";                       \
for i in $(seq 0 364);                  \
do                          \
    mkdir $(date -d "$sday +$i day" +"$fmt");   \
done

「\」ではなく「;」で揃えおもよい。

sday="20220101"                     ;\
fmt="%Y%m%d"                        ;\
for i in $(seq 0 364)                   ;\
do                           \
    mkdir $(date -d "$sday +$i day" +"$fmt")    ;\
done

行末が「\」しかない行ず「;\」になる行の2぀が出おきおしたうので、基本的には「\」を行末に揃えたほうが芋栄えはよくなるのではないかず思うが、その蟺りは奜みで敎えおもらえばよい。

※ 「for-do-done」のワンラむナヌには䞀点泚意が必芁だ。「do」はその埌に盎接「;」を曞くこずはできない。必ず「do コマンド;」のようにコマンドが挟たっおいる必芁がある。぀たり、ワンラむナヌずしお「do; コマンド;」ずいった曞き方はできず、「do コマンド;」でなければならない。

ワンラむナヌをMakefileぞはめ蟌む

先ほど敎理したものをMakefileぞはめ蟌むず次のようになる。

create_2022_dirs:
    sday="20220101";                    \
    fmt="%Y%m%d";                       \
    for i in $(seq 0 364);                  \
    do                          \
        mkdir $(date -d "$sday +$i day" +"$fmt");   \
    done

この状態で「make create_2022_dirs」を実行するず次のようになる。

% make create_2022_dirs
sday="20220101";                                        \
fmt="%Y%m%d";                                           \
for i in ;                                      \
do                                                      \
        mkdir ; \
done
% 

ご芧の通り、この状態だず思ったようには実行されない。「$」から始たる文字列がMakefileではMakefileの倉数ずしお解釈されるためだ。したがっお、今床はワンラむナヌ化したものをMakefile向けに゚スケヌプ凊理しなければならない。

Makefileず衝突する衚蚘を曞き換える

曞き換えは単玔だ。「$」はMakefileの倉数ずしお解釈されるので、この郚分を「$$」に曞き換える。曞き換えるず次のようになる。

create_2022_dirs:
    sday="20220101";                    \
    fmt="%Y%m%d";                       \
    for i in $$(seq 0 364);                 \
    do                          \
        mkdir $$(date -d "$$sday +$$i day" +"$$fmt");   \
    done

この状態で実行するず次のようになる。

% make create_2022_dirs
sday="20220101";                                        \
fmt="%Y%m%d";                                           \
for i in $(seq 0 364);                                  \
do                                                      \
    mkdir $(date -d "$sday +$i day" +"$fmt");       \
done
% 

意図した通りに動䜜しおいるこずがわかる。ここたでがシェルスクリプトからMakefileぞの曞き換えの䞀連の流れずいうこずになる。

makeがコマンドを実行するたでの流れを把握する

今床は逆に、Makefileに曞いおあるコマンドがどのように評䟡されるのかを远っおみよう。䞀旊Makefileに起こした埌は、凊理を倉曎する堎合はMakefileを盎接曞き換えるこずになるので、どのように凊理されるのかを理解しおいないず困ったこずになるからだ。

たず、次の凊理を考える。

create_2022_dirs:
    sday="20220101";                    \
    fmt="%Y%m%d";                       \
    for i in $$(seq 0 364);                 \
    do                          \
        mkdir $$(date -d "$$sday +$$i day" +"$$fmt");   \
    done

makeは、次の蚘述を1行であるず解釈する。

    sday="20220101";                    \
    fmt="%Y%m%d";                       \
    for i in $$(seq 0 364);                 \
    do                          \
        mkdir $$(date -d "$$sday +$$i day" +"$$fmt");   \
    done

実際にどう凊理されるかは実装系に䟝存するのだが、倧䜓の流れは同じだ。たず、行頭のタブは削陀される。

sday="20220101";                    \
    fmt="%Y%m%d";                       \
    for i in $$(seq 0 364);                 \
    do                          \
        mkdir $$(date -d "$$sday +$$i day" +"$$fmt");   \
    done

次に「\」で分割しおいた郚分が連結されお1行になる。

sday="20220101";                        fmt="%Y%m%d";                           for i in $$(seq 0 364);                     do                                  mkdir $$(date -d "$$sday +$$i day" +"$$fmt");       done

「$$」が「$」に倉換される。

sday="20220101";                        fmt="%Y%m%d";                           for i in $(seq 0 364);                      do                                  mkdir $(date -d "$sday +$i day" +"$fmt");       done

この状態で、文字列がシェルぞ枡り実行される、ずいう流れだ。

シェルスクリプトを曞くのず比べるず、Makefileにワンラむナヌを曞く䜜業はやや面倒だず思うかも知れない。だが、今回説明したように、基本的な曞き換え方はそれほど難しいものではない。ある皋床慣れおしたえば、むしろ簡単なはずだ。

䜕床か曞き換えおいるず、長いシェルスクリプトも曞き換えるこずができるようになる。長いコマンドをMakefileに曞き蟌むこずがありそうなら、緎習しおおくずよいのではないだろうか。

参考