🐧 Linux 総合学習プラットフォーム
自動化/定期実行 ・ 中級

シェルスクリプトと組み合わせた自動化

定期実行では、1行のコマンドではなく複数の処理をまとめたシェルスクリプトを呼び出すのが定石です。スクリプト先頭に #!/bin/bash のシバンを書き、chmod +x で実行権を付けてからファイルとして登録します。何度動いても結果が変わらない(冪等な)作りにし、出力やエラーをログへリダイレクトしておくと、後から動作を追えて安全です。cron や timer からはこのスクリプトの絶対パスを指定します。

定期実行に登録する処理は、複数の手順から成ることが多い。たとえば、データベースを dump して tar で固めて別サーバへ scp で送り、古い世代を消す、といった具合だ。

これらを crontab の1行に && でつないで詰め込むこともできなくはない。だが1行が長く読みにくくなる。途中で失敗したときの処理も書けない。修正のたびに crontab を編集することになって扱いづらい。

では、どうまとめるのが筋がいいのか。定石は、一連の処理を1つのシェルスクリプトファイルにまとめ、cron や timer からはそのスクリプトを呼ぶだけにする、という形だ。

💡
ポイント一連の処理は1つのスクリプトにまとめ、cron や timer からはそれを呼ぶだけにする。スケジューラは「いつ」、スクリプトは「何を」に役割を分ける。
cron / timerいつ起動するかbackup.sh何をするかdump → tar → scp → 掃除絶対パスで呼ぶだけ処理本体はファイルに集約。手元でテストでき、git にも乗せやすい

処理の本体をファイルとして独立させると、いいことが多い。手元で何度でも実行テストできる。変更履歴をバージョン管理(git など)に乗せやすい。スケジュールと処理内容を分けて考えられる。

スケジューラはあくまで「いつ起動するか」だけを担当し、「何をするか」はスクリプトに任せる。この役割分担が、見通しのよい自動化の基本形だ。

🔑 作ったスクリプトが、なぜ実行できないのか

スクリプトファイルを作るときの作法が2つある。

1つ目は、ファイルの先頭行にシバン(shebang)を書くことだ。#!/bin/bash のように #! に続けてインタプリタの絶対パスを書く。こうすると、このファイルを ./backup.sh のように直接実行したとき、どのプログラムで解釈・実行すべきかが決まる。

bash 固有の機能(配列や [[ ]] など)を使わない汎用的なスクリプトなら、より移植性の高い #!/bin/sh とすることもある。

2つ目は、実行権限を付けることだ。Linuxは拡張子ではなく実行権(x)の有無でファイルを実行できるかを判断する。だから作ったばかりのスクリプトには chmod +x backup.sh で実行権を与える。

先頭に #!/bin/bash(シバン)を書き、chmod +x backup.sh で実行権を付ける。この2つでスクリプトを直接実行できる。

これを忘れて ./backup.sh を実行し「Permission denied」となるのは、初学者がほぼ必ず一度は通る道だ。覚えておくと回り道を減らせる。

つまずき実行権を付け忘れて ./backup.sh が「Permission denied」になるのは定番のつまずき。chmod +x を思い出す。

なお、cron からフルパスで bash /usr/local/bin/backup.sh のように明示的にインタプリタを指定して呼ぶ場合は、実行権が無くても動かせる。だが、どんな呼ばれ方でも動くよう実行権は付けておくのが無難だ。

🔄 何度走っても同じ結果になる、という性質

定期実行されるスクリプトでは、冪等(べきとう、idempotent)であることをとくに意識する。

💡
ポイント冪等(べきとう)とは、何回実行しても1回だけ実行したときと同じ最終状態になる性質。繰り返しても害が出ない。

冪等とは、同じ処理を何回実行しても、1回だけ実行したときと同じ最終状態になり、繰り返しても害が出ない性質のことだ。

なぜそれが要るのか。cron が何らかの理由で二重に起動してしまったときや、動作確認のために手動でもう一度走らせたときにも、安心して任せられるからである。

具体的には、こんな工夫をする。ディレクトリ作成は既に在っても失敗しない mkdir -p を使う。処理の前にファイルの有無を if [ -f ファイル ] で確かめる。追記ではなく上書きで常に一定の状態を作る。

mkdir -p(既にあっても失敗しない)、if [ -f ファイル ] で有無を確認、追記でなく上書き。これらが冪等な書き方の基本。

逆に避けたい作りもある。実行のたびに同じ行を追記し続ける、連番を無条件に増やしていく、といったものだ。これらは冪等ではなく、繰り返すほど状態が膨らんだり壊れたりする。

🔗
たとえ冪等は「何度押してもエレベーターが同じ階に止まるボタン」。非冪等は「押すたびに1階ずつ余分に動くボタン」。後者は繰り返すほどズレる。

「もう一度動いても大丈夫か?」を一行ごとに自問しながら書く。これが自動化スクリプトの基本姿勢だ。

🛡 途中で失敗したのに気づかない、を防ぐには

スクリプト冒頭に set -euo pipefail と書いておくのは、自動化では広く使われる安全策だ。

set -euo pipefail ……-e=失敗で即停止、-u=未定義変数でエラー、-o pipefail=パイプ途中の失敗も見逃さない。

-e はコマンドが失敗(非ゼロ終了)したらそこで即座に止める。-u は未定義の変数を使ったらエラーにする。-o pipefail はパイプの途中のコマンドが失敗してもそれを見逃さない。

これらを付けると「途中で失敗したのに気づかず最後まで走り、中途半端で壊れた結果を残す」事故を防げる。

コツ処理の節目に echo \"backup start\" のようにメッセージを出しておくと、後でログを読んだとき「どこまで進んでどこで失敗したか」が一目で分かる。

あわせて、処理の節目に echo \"backup start\" のようにメッセージを出しておくとよい。後でログを読んだときに「どこまで進んで、どこで失敗したか」が一目で分かる。

さらに、成功・失敗を呼び出し側へ正しく伝えるため、スクリプトの最後で適切な終了コード(成功なら exit 0、失敗なら非ゼロ)を返すことも意識する。

systemd の service から呼ぶ場合は、この終了コードを見て成功・失敗が journal に記録される。終了コードを正しく返すことが、監視のしやすさに直結する。

📂 画面に出ない結果を、どこへ残し、どう呼ぶのか

cron や timer から動かす処理は画面に結果が出ない。だから出力を必ずログに残す。

リダイレクトの基本を押さえておこう。>> はファイルへの追記。> は上書き(既存内容を消して書き直す)。2>&1 は標準エラーを標準出力と同じ送り先へ合流させる指定だ。

>> /var/log/backup.log 2>&1 ……通常の出力もエラーも同じログに時系列で貯める。>>=追記、>=上書き、2>&1=エラーを合流。

これらを組み合わせ、コマンドの末尾に >> /var/log/backup.log 2>&1 と書けば、通常の出力もエラーメッセージも同じログファイルに時系列で貯まる。

日付ごとにログを分けたいなら、スクリプト内で logfile=/var/log/backup-$(date +%F).log のように変数を組み立てて使う手もある。

最後に、cron や systemd の service からスクリプトを呼ぶときは、必ず絶対パスで指定する。たとえば crontab には 0 3 * * * /usr/local/bin/backup.sh >> /var/log/backup.log 2>&1 のように書く。

つまずきcron は実行時のカレントディレクトリやPATHが対話シェルと異なる。相対パスで書くと「手では動くのに cron では見つからない」失敗に直結する。

cron は実行時のカレントディレクトリやPATHが対話シェルと異なる。相対パスで書くと「手では動くのに cron では見つからない」という典型的な失敗に直結する。

処理はスクリプトに集約し、呼び出しは絶対パスで、出力はログへ。この3点を守るだけで、自動化の安定度は大きく上がる。次の項では、その「手では動くのに cron では動かない」の正体である環境変数と PATH の問題を掘り下げる。

この項目に出てくる用語

冪等性べきとうせい
同じ操作を何度実行しても結果が変わらない性質。自動化処理の安全性の鍵。

関連コマンド

crontabsystemctl list-timers

▶ 学習アプリでこの続きを学ぶ・演習する