【図解】cronの仕組み

## はじめに

定期的に実施する作業は、自動的に実行されるようにcrontabコマンドで`/etc/crontab`ファイルに記述するだけと思っていたのですが・・・

```
# crontab -e
```
ある時、`/var/spool/cron/[user名]`というファイルの存在に気づき、この2つの違いを調べまとめてみました。
→書きながら全く違う方向に進んでしまったので、今回はcrontabの基礎的な使い方にまとめた。

## 同記事で理解できること
①crontabファイルの記述方法
②シェルスクリプトの書き方
③環境変数パス追加
④リダイレクト・/dev/null・2>&1の使い方


## cronの仕組み

![cron_structure.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2935967/4eb97eb2-0a29-fe39-aa2e-3cfcbe8d56e5.jpeg)

`/var/spool/cron`配下にユーザごとにcrontabファイルが用意されている。
そのcrontabファイルにユーザがスケジューリングしておけばcrondデーモンが1分おきにスケジュールをチェックしてくれるので、記述した時間にジョブが実行される。

## 覚えること
### crontabコマンドのオプション

| オプション       |       説明 |    
|:-----------------|:------------------|
| -e             |             エディタを使ってcrontabファイルを編集 |      
| -l           |            crontabファイルの内容を表示する |       
| -r             |             crontabファイルを削除する |     
| -i               |                crontabファイル削除時に確認する |         
| -u ユーザー名             |             ユーザを指定してcrontabファイルを編集する（rootのみ） |             
### crontabファイルのフィールド（スケジューリングの書き方）
| フィールド       |       内容 |    
|:-----------------|:------------------|
| 分           |             0~59までの整数 |      
| 時           |            0~23までの整数|       
| 日             |             1~31までの整数|     
| 月               |               1~12までの整数|         
| 曜日             |             0~7までの整数(1が月曜日)| 
| コマンド             |             実行すべきコマンド | 


### 複数の値を指定する場合

```:crontab -e
0 9,12 * * 1 date >> /root/prac/date.log
```

上の例では、毎週月曜日の9時と12時にdateコマンドの出力結果を/root/prac/date.logに追記します。

### 間隔を指定する場合

```:crontab -e
0 */2 * * 1 date >> /root/prac/date.log
```

上の例では、2時間ごとにdateコマンドの出力結果を/root/prac/date.logに追記します。

## 実践: 1分毎にdateコマンドを実行し実行結果を"date.log"ファイルに追記する

エディタを使ってcrontabファイル編集 ※viコマンドは使えないので注意！
```
[root@localhost ~]# crontab -e
```
編集作業
```
1 #１分毎に日時を対象ファイルに吐き出す
2 */1 * * * * date >> /root/prac/date.log
```
crontabファイル内容確認
```
[root@localhost ~]# crontab -l
#１分毎に日時を対象ファイルに吐き出す
*/1 * * * * date >> /root/prac/date.log
```
Catコマンドでも現在のrootユーザのcronの設定状況が確認できる。
```
[root@localhost ~]# cat /var/spool/cron/root 
#１分毎に日時を対象ファイルに吐き出す
*/1 * * * * date >> /root/prac/date.log
```
`tail -f`出力ログファイルをリアルタイム監視します。crontabファイルで記述した通り、1分毎にdateコマンドが実行されているのがわかります。

```
[root@localhost ~]# tail -f /root/prac/date.log 
2022年 10月 29日 土曜日 02:35:01 JST
2022年 10月 29日 土曜日 02:36:01 JST
2022年 10月 29日 土曜日 02:37:01 JST
2022年 10月 29日 土曜日 02:38:01 JST
2022年 10月 29日 土曜日 02:39:02 JST
2022年 10月 29日 土曜日 02:40:01 JST
2022年 10月 29日 土曜日 02:41:01 JST
2022年 10月 29日 土曜日 02:42:01 JST
2022年 10月 29日 土曜日 02:43:01 JST
2022年 10月 29日 土曜日 02:44:02 JST
```

設定済みのcronジョブを削除し確認
```
[root@localhost ~]# crontab -r
[root@localhost ~]# crontab -l
no crontab for root
```


## 応用: 1分毎にシェルスクリプトを実行しその結果を/prac dir内に保存する

スクリプトとログの両方を格納するdirの作成
```
[root@localhost ~]# pwd
/root
[root@localhost ~]# mkdir prac/sv

```

シェルスクリプトの作成(prac/sv/test.sh)

```:prac/sv/test.sh
#デフォルトゲートウェイにping。実行結果はディスプレイにもどっかのファイルにも保存させない。
#戻り値によって処理を変え、日付のログファイル作成。

ping -c 3 192.168.1.1 > /dev/null 2>&1

if [ "$?" -ne 0 ] ; then
( date ; echo "Can't reach to SV" ) >> /root/prac/sv/`date "+%Y%m%d"`.log
else
( date ; echo "SV reachable" ) >> /root/prac/sv/`date "+%Y%m%d"`.log
fi
```
上のシェルスクリプトの書き方参照先は以下の通り
・リダイレクション`> /dev/null 2>&1`の詳細：新しいLinuxの教科書P.190~191
・Ifの条件式`$?`はシェルスクリプトの引数を表す変数：LPIC小豆本P.316
・ログファイル名`date "+%Y%m%d"`の書き方：LPIC小豆本P.391
・/`date "+%Y%m%d"`.log = $(date "+%Y%m%d")でもいい

このシェルスクリプトは、直前に実行されたコマンド、つまりpingの実行結果の戻り値が０じゃない場合orそれ以外で処理を分けている。


### 作成したスクリプトに対し、パスの追加及び実行権の付与（小豆本P.113&179&181）


作成したスクリプトのPermissionは644なので実行権を付与する。(744)
```
[root@localhost ~]# ls -l /root/prac/sv/test.sh 
-rw-r--r--. 1 root root 383 10月 29 03:26 /root/prac/sv/test.sh
[root@localhost ~]# chmod u+x prac/sv/test.sh
[root@localhost ~]# ls -l /root/prac/sv/test.sh 
-rwxr--r--. 1 root root 383 10月 29 03:26 /root/prac/sv/test.sh
```

パスの追加
```
[root@localhost ~]# echo $PATH
/root/.local/bin:/root/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/dirsc
[root@localhost ~]# PATH=$PATH:/root/prac/sv
[root@localhost ~]# echo $PATH
/root/.local/bin:/root/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/dirsc:/root/prac/sv
```
※永続的な使用なら`.bashrc`に記述


### cronの設定

作ったシェルスクリプトを1分毎に走らせる設定し確認

```
[root@localhost ~]# crontab -l
#１分毎にsh実行
*/1 * * * * /root/prac/sv/test.sh
```

### 動作確認(リアルタイム監視)

```
[root@localhost ~]# tail -f  /root/prac/sv/20221029.log 
2022年 10月 29日 土曜日 04:43:03 JST
SV reachable
2022年 10月 29日 土曜日 04:44:03 JST
SV reachable
2022年 10月 29日 土曜日 04:45:03 JST
SV reachable
2022年 10月 29日 土曜日 04:46:03 JST
SV reachable
2022年 10月 29日 土曜日 04:47:03 JST
SV reachable
```

### cronの設定の消去

```
[root@localhost ~]# ls -l /var/spool/cron/
合計 4
-rw-------. 1 root root 56 10月 29 04:43 root
[root@localhost ~]# cat /var/spool/cron/root 
#１分毎にsh実行
*/1 * * * * /root/prac/sv/test.sh
[root@localhost ~]# crontab -r
[root@localhost ~]# crontab -l
no crontab for root
[root@localhost ~]# ls -l /var/spool/cron/
合計 0
```

以上。

## 反省点と課題
シェルスクリプトに対しパス（環境変数）の追加を忘れていて実行されなかった。
以下のログファイルを見てみたが、よくわからないまま。
```
[root@localhost ~]# tail -n 20 /var/log/cron
Oct 29 04:38:01 localhost CROND[14203]: (root) CMD (/root/prac/sv/test/sh)
Oct 29 04:39:02 localhost CROND[14274]: (root) CMD (/root/prac/sv/test/sh)
Oct 29 04:40:01 localhost CROND[14349]: (root) CMD (/root/prac/sv/test/sh)
Oct 29 04:41:01 localhost CROND[14427]: (root) CMD (/root/prac/sv/test/sh)
Oct 29 04:42:01 localhost CROND[14509]: (root) CMD (/root/prac/sv/test/sh)
Oct 29 04:42:46 localhost crontab[14572]: (root) LIST (root)
Oct 29 04:42:49 localhost crontab[14573]: (root) BEGIN EDIT (root)
Oct 29 04:43:02 localhost CROND[14600]: (root) CMD (/root/prac/sv/test/sh)
Oct 29 04:43:20 localhost crontab[14573]: (root) REPLACE (root)
Oct 29 04:43:20 localhost crontab[14573]: (root) END EDIT (root)
Oct 29 04:44:01 localhost crond[1289]: (root) RELOAD (/var/spool/cron/root)
Oct 29 04:44:01 localhost CROND[14688]: (root) CMD (/root/prac/sv/test.sh)

```
PSコマンドも確認
```
[root@localhost ~]# ps aux | grep 13584
root       15371  0.0  0.0 221940  1080 pts/0    R+   04:55   0:00 grep --color=auto 13584

```
## 課題

同タスクで環境変数のパス追加を忘れていた場合、どのようにすればその事実に気付けるのか？ログから「環境変数のパス追加してないから実行されない」と問題把握するまでの手順を調べる