CentOS crontab -r実行時にcrontab削除確認メッセージ表示 + crontab情報バックアップするスクリプト

はじめに
===

Linuxのcrontabコマンドを「-r」オプションつきで実行すると、crontabコマンド実行ユーザのcrontab情報が削除されてしまいます。crontab削除確認のメッセージは表示されず、いきなり削除されます。

「crontab -ir」のように「-i」オプションもつければ、crontab情報を削除して良いか確認してくれますが、「-i」オプションを付け忘れたら、crontab情報は消えてしまいます。

crontabコマンドを「-r」オプションつきで実行する場合、本当にcrontab情報を削除して良いか確認するメッセージを表示し、削除をキャンセルする または 削除を実行する場合はcrontab情報をバックアップしてから削除するcrontabラッパースクリプトを作成してみました。

免責事項
===

ここに書かれているスクリプトや設定を使用して発生した問題につきまして、当方は一切責任を負いません。恐れ入りますが、スクリプトや設定をご利用される場合は自己責任でお願い致します。

crontabコマンドの「-r」オプションによるcrontab情報削除について
===

以下のように「-r」オプションをつけてcrontabコマンドを実行すると、そのユーザのcrontab情報は削除されてしまいます。

crontabコマンドの「-r」オプション実行する際には、本当にそのユーザのcrontabを消してしまって良いか非常に注意が必要になります。

しかし、サーバ操作中に誤って「-r」オプションつきでcrontabコマンドを実行し、crontab情報を削除してしまう可能性があるかもしれません。

今回「-r」オプションを付けてcrontabコマンドを実行する場合、確認メッセージ表示やcrontab情報をバックアップするcrontabコマンド用ラッパースクリプトを作成してみました。

[crontab削除前の状態例]

```
# id
uid=0(root) gid=0(root) groups=0(root) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
#

# /usr/bin/crontab -l
* * * * * /root/example.sh
#
```

　↓

[crontab削除例]

```
# /usr/bin/crontab -r　※(注) このオプションをつけてコマンド実行するとcrontab情報が消えてしまいますので十分ご注意下さい。
#

# /usr/bin/crontab -l
no crontab for root
#
```

検証した環境
===

・CentOS release 6.6 (2.6.32-504.3.3.el6.x86_64)
・GNU bash, version 4.1.2
・ログインシェルをbashにしているユーザでcrontabコマンド実行して検証しました。

crontabコマンドのラッパースクリプト作成
===

以下のようなcrontabコマンドのラッパースクリプトを任意のディレクトリに作成します。今回の例では/usr/local/binに作成します。

ラッパースクリプト内では、crontabコマンドのオプションで「-r」が指定された場合、本当にcrontab情報を削除するか確認メッセージを表示します。

「n」と入力したらcrontab情報削除は行わず処理を終了します。
「y」と入力したらcrontab情報削除の前に、crontab情報が格納される/var/spool/cronディレクトリごとバックアップを取ってから、ユーザのcrontab情報を削除します。

crontab情報のバックアップファイルは/var/spool/cron/rootのようなファイル単位で作成した方が無駄はないのですが、今回は念の為、/var/spool/cronディレクトリごとバックアップするようにしています。

スクリプト内で/usr/bin/crontab.binと記述していますが、こちらについては後述致します。

```
# vi /usr/local/bin/crontab_wrapper.sh
```

```shell-session:/usr/local/bin/crontab_wrapper.sh
#!/bin/sh

# crontabコマンドのパスを指定する
CRONTAB_COMMAND="/usr/bin/crontab.bin"

# crontab -r オプションでcrontabが削除される場合、削除前に/var/spool/cronディレクトリごとバックアップを取得する
CRONTAB_DIR="/var/spool/cron"
CRONTAB_BACKUP_TIME="`date '+%Y%m%d_%H%M%S'`"

# キー入力文字変数の初期化
INPUT_KEY=N

function PrintUsage {
        echo "指定されたパラメータは [$#] 個です。"
        echo "[${CRONTAB_COMMAND}] コマンドを実行するには [1 - 3] 個のパラメータが必要です。処理を終了します。"
        echo "crontabコマンドの第一パラメータとしては [-r] or [-l] or [-e] を指定して下さい。"
        echo "[-u] オプションは第ニパラメータで指定して下さい。"
        echo "Usage:"
        echo "  crontab -l"
        echo "  crontab -e"
        echo "  crontab -r"
        echo "  crontab -l -u ユーザ名"
        echo "  crontab -e -u ユーザ名"
        echo "  crontab -r -u ユーザ名"
}

# コマンド実行時にパラメータが指定されなかった場合の判定
if [ $# -eq 0 ] ; then
        PrintUsage
        exit 1
fi

# コマンド実行時のパラメータ数判定
# パラメータ数が 1 - 3 個指定された場合は処理を続行する
if [ $# -lt 1 ] || [ $# -gt 3 ] ; then
        PrintUsage
        exit 1
fi

# crontabコマンド実行時に指定されたパラメータ取得
CRONTAB_OPT1="$1"
CRONTAB_OPT2="$2"
CRONTAB_OPT3="$3"

# 第一パラメータで指定されたcrontabコマンドのオプションが [-r] か [-l] か [-e] であるか判定する
if [ ${CRONTAB_OPT1} != "-r" ] && [ ${CRONTAB_OPT1} != "-l" ] && [ ${CRONTAB_OPT1} != "-e" ] ; then
        PrintUsage
        exit 1
fi

# 第ニパラメータで指定されたcrontabコマンドのオプションが [-u] であるか判定する
if [ $# -gt 1 ] ; then
        if [ ${CRONTAB_OPT2} != "-u" ] ; then
                PrintUsage
                exit 1
        fi
        if [ -z ${CRONTAB_OPT3} ] ; then
                PrintUsage
                exit 1
        fi
fi

##### crontab -r が指定された場合、本当にcronエントリを削除してよいか確認する
if [ ${CRONTAB_OPT1} = "-r" ] ; then

        echo "[${CRONTAB_COMMAND} ${CRONTAB_OPT1}] を実行しようとしています。"
        echo "crontabコマンドで [-r] オプションをつけて実行すると、crontabコマンドを実行したユーザ [${USER}] のcronエントリが削除されます。"
        echo "本当に [${USER}] ユーザのcronエントリを削除してよろしいですか?"
        echo

        echo "cronエントリを削除する場合は [y]、キャンセルする場合は [n] と入力して下さい。"
        read INPUT_KEY

        if [ $INPUT_KEY = "Y" -o $INPUT_KEY = "y" ]; then
                echo "[${USER}] ユーザのcrontabエントリを削除します。"
        elif [ $INPUT_KEY = "N" -o $INPUT_KEY = "n" ]; then
                echo "[${USER}] ユーザのcrontabエントリは削除せず終了します。"
                exit 1
        else
                echo "[y] または [n]以外が入力されました。[${USER}] ユーザのcrontabエントリは削除せず終了します。"
                exit 1
        fi

        echo "crontabファイルが格納されているディレクトリ [${CRONTAB_DIR}] をバックアップします。"
        echo "cp -Rp ${CRONTAB_DIR} ${CRONTAB_DIR}.${CRONTAB_BACKUP_TIME}"
        cp -Rp ${CRONTAB_DIR} ${CRONTAB_DIR}.${CRONTAB_BACKUP_TIME}

        if [ ! -d "${CRONTAB_DIR}.${CRONTAB_BACKUP_TIME}" ] ; then
                echo "crontab削除前の [${CRONTAB_DIR}] ディレクトリバックアップに失敗しました。"
                echo "crontabエントリは削除せず終了します。"
                exit 1
        fi

fi

##### crontabコマンドをパラメータで指定したオプションつきで実行する
${CRONTAB_COMMAND} ${CRONTAB_OPT1} ${CRONTAB_OPT2} ${CRONTAB_OPT3}
```

スクリプトに実行権限を設定します。

```
# chmod 755 /usr/local/bin/crontab_wrapper.sh
# chown root:root /usr/local/bin/crontab_wrapper.sh
```

crontabコマンドラッパースクリプト(crontab_wrapper.sh)のalias設定
===

crontabコマンドが実行された場合、/usr/local/bin/crontab_wrapper.shが実行されるようaliasを設定します。

今回の例では、/etc/bashrcで全ユーザのbashにalias設定を適用するようにします。

```
# cp -p /etc/bashrc /etc/bashrc.ORG
# diff /etc/bashrc /etc/bashrc.ORG
#
```

```
# vi /etc/bashrc
　　(末尾に以下の設定を追加)
alias crontab='/usr/local/bin/crontab_wrapper.sh'
```

サーバからログアウトして、rootユーザでログインしなおします。
以下のようにcrontabのaliasとして/usr/local/bin/crontab_wrapper.shが設定されている事を確認します。

```
# id
uid=0(root) gid=0(root) groups=0(root) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
#

# echo $SHELL
/bin/bash
#

# alias crontab
alias crontab='/usr/local/bin/crontab_wrapper.sh'
#
```

/usr/bin/crontabコマンドのリネームとシンボリックリンク作成
===

/usr/bin/crontabを/usr/bin/crontab.binへリネームし、/usr/bin/crontabをcrontabコマンドラッパースクリプト(crontab_wrapper.sh)へのシンボリックリンクにします。

crontabコマンドではなく、フルパスで/usr/bin/crontabコマンドを実行した場合、aliasがきかないので、このように設定しています。
こうすると、フルパスで/usr/bin/crontabの「-r」オプションを指定した場合も、crontab情報削除確認メッセージが表示されるようになります。

まず、念の為、crontabのオリジナルファイルをバックアップします。

```
# cp -p /usr/bin/crontab /usr/bin/crontab.ORG
# diff /usr/bin/crontab /usr/bin/crontab.ORG
# 
```

crontabをリネームしてシンボリックリンクに変更します。

```
# mv /usr/bin/crontab /usr/bin/crontab.bin
# ll /usr/bin/crontab.bin
-rwsr-xr-x. 1 root root 51784 Nov 23  2013 /usr/bin/crontab.bin
#

# ln -s /usr/local/bin/crontab_wrapper.sh /usr/bin/crontab
# 
```

```
# ll /usr/bin/crontab
lrwxrwxrwx. 1 root root 33 Jan 24 23:56 /usr/bin/crontab -> /usr/local/bin/crontab_wrapper.sh

# ll /usr/bin/crontab.bin
-rwsr-xr-x. 1 root root 51784 Nov 23  2013 /usr/bin/crontab.bin

# ll /usr/local/bin/crontab_wrapper.sh
-rwxr-xr-x. 1 root root 3689 Jan 25 00:07 /usr/local/bin/crontab_wrapper.sh
# 
```

crontabコマンドラッパースクリプト(crontab_wrapper.sh)の使い方
===

crontabコマンドラッパースクリプト(crontab_wrapper.sh)の使い方ですが、「-r」オプション実行時以外は、基本的に通常のcrontabコマンドと変わりません。

・crontab登録情報の表示オプション

```
# crontab -l
```

・crontab情報の編集オプション

```
# crontab -e
```

・crontab情報の削除オプション

```
# crontab -r
```

crontabコマンドラッパースクリプト経由でのcrontab登録情報の編集
===

crontabコマンドラッパースクリプト経由での「crontab -e」コマンドの実行例です。
通常の「crontab -e」コマンドと同じくcrontab情報を編集可能です。

```
# which crontab
alias crontab='/usr/local/bin/crontab_wrapper.sh'
        /usr/local/bin/crontab_wrapper.sh
#

# crontab -l
no crontab for root
#
```

```
# crontab -e
　　(vi等のEDITORでcrontabを開けるので、通常のcrontab -eコマンドと同じように編集する。今回の例では以下のサンプル設定を追加する)

* * * * * /root/example.sh
10 20 * * * /root/example.sh
30 20 * * * /root/example.sh
```

crontabコマンドラッパースクリプト経由でのcrontab登録情報の表示
===

crontabコマンドラッパースクリプト経由での「crontab -l」コマンドの実行例です。
通常の「crontab -l」コマンドと同じくcrontabに登録した情報を表示可能です。

```
# which crontab
alias crontab='/usr/local/bin/crontab_wrapper.sh'
        /usr/local/bin/crontab_wrapper.sh
#

# crontab -l
* * * * * /root/example.sh
10 20 * * * /root/example.sh
30 20 * * * /root/example.sh
#
```

crontabコマンドラッパースクリプト経由でのcrontab情報削除 (crontab情報削除をキャンセルする例)
===

crontabコマンドラッパースクリプト経由での「-r」オプションつきcrontabコマンドの実行例です。

「cronエントリを削除する場合は [y]、キャンセルする場合は [n] と入力して下さい。」というメッセージの後で「n」と入力すると、crontab情報を削除せずに処理を終了します。

```
# crontab -l
* * * * * /root/example.sh
10 20 * * * /root/example.sh
30 20 * * * /root/example.sh
#
```

```
# crontab -r
[/usr/bin/crontab.bin -r] を実行しようとしています。
crontabコマンドで [-r] オプションをつけて実行すると、crontabコマンドを実行したユーザ [root] のcronエントリが削除されます。
本当に [root] ユーザのcronエントリを削除してよろしいですか?

cronエントリを削除する場合は [y]、キャンセルする場合は [n] と入力して下さい。
n　→　「n」と入力して、crontab情報削除をキャンセルします。
[root] ユーザのcrontabエントリは削除せず終了します。
#
```

以下のようにrootユーザのcrontab情報は削除されずに残っております。

```
# crontab -l
* * * * * /root/example.sh
10 20 * * * /root/example.sh
30 20 * * * /root/example.sh
#
```

crontabコマンドラッパースクリプト経由でのcrontab情報削除 (crontab情報削除をキャンセルせずに削除する例)
===

crontabコマンドラッパースクリプト経由での「-r」オプションつきcrontabコマンドの実行例です。

「cronエントリを削除する場合は [y]、キャンセルする場合は [n] と入力して下さい。」というメッセージの後で「y」と入力すると、/var/spool/cronディレクトリごとバックアップした上で、crontab情報を削除します。

今回の例ではrootユーザのcrontab情報(/var/spool/cron/root)が削除される事になります。削除前のcrontab情報は/var/spool/cron.20150125_002441にバックアップされます。

```
# id
uid=0(root) gid=0(root) groups=0(root) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
#
```

```
# crontab -r
[/usr/bin/crontab.bin -r] を実行しようとしています。
crontabコマンドで [-r] オプションをつけて実行すると、crontabコマンドを実行したユーザ [root] のcronエントリが削除されます。
本当に [root] ユーザのcronエントリを削除してよろしいですか?

cronエントリを削除する場合は [y]、キャンセルする場合は [n] と入力して下さい。
y　→　「y」と入力して、crontab情報削除を実行します。
[root] ユーザのcrontabエントリを削除します。
crontabファイルが格納されているディレクトリ [/var/spool/cron] をバックアップします。
cp -Rp /var/spool/cron /var/spool/cron.20150125_002441
#
```

以下のようにrootユーザのcrontab情報が削除されます。

```
# crontab -l
no crontab for root
#

# ll /var/spool/cron/root
ls: cannot access /var/spool/cron/root: No such file or directory
#
```

crontab削除前の/var/spool/cronディレクトリのバックアップが/var/spool/cron.20150125_002441として作成されている事を確認します。
rootユーザのcrontab情報も/var/spool/cron.20150125_002441/rootにバックアップされている事が確認出来ました。

```
# ll /var/spool/cron.20150125_002441/root
-rw-------. 1 root root 88 Jan 25 00:22 /var/spool/cron.20150125_002441/root
#

# cat /var/spool/cron.20150125_002441/root
* * * * * /root/example.sh
10 20 * * * /root/example.sh
30 20 * * * /root/example.sh
#
```

crontabコマンドラッパースクリプト経由での「-u」オプションでのユーザを指定したcrontab情報操作
===

以下のように「-u」オプションによるユーザ名を指定したcrontab情報操作も動作します。

・指定したユーザのcrontab登録情報の表示オプション

```
# crontab -l -u ユーザ名
```

・指定したユーザのcrontab情報の編集オプション

```
# crontab -e -u ユーザ名
```

・指定したユーザのcrontab情報の削除オプション

```
# crontab -r -u ユーザ名
```

以下の例では「crontab -e -u ユーザ名」コマンドで、example-userユーザのcrontabを編集しています。

```
# crontab -l -u example-user
no crontab for example-user
#
```

```
# crontab -e -u example-user
　　(vi等のEDITORでcrontabを開けるので、通常のcrontab -eコマンドと同じように編集する。今回の例では以下のサンプル設定を追加する)

* * * * * /home/example-user/example.sh
30 10 * * * /home/example-user/example.sh
50 10 * * * /home/example-user/example.sh
```

以下の例では「crontab -l -u ユーザ名」コマンドで、example-userユーザのcrontabを編集しています。

```
# crontab -l -u example-user
* * * * * /home/example-user/example.sh
30 10 * * * /home/example-user/example.sh
50 10 * * * /home/example-user/example.sh
#
```

```
# su - example-user
$ id
uid=501(example-user) gid=502(example-group) groups=502(example-group) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023

$ crontab -l
* * * * * /home/example-user/example.sh
30 10 * * * /home/example-user/example.sh
50 10 * * * /home/example-user/example.sh
$ exit
```

以下の例では「crontab -r -u ユーザ名」コマンドで、example-userユーザのcrontabを編集しています。crontab情報削除を途中でキャンセルする例です。

```
# crontab -r -u example-user
[/usr/bin/crontab.bin -r] を実行しようとしています。
crontabコマンドで [-r] オプションをつけて実行すると、crontabコマンドを実行したユーザ [root] のcronエントリが削除されます。
本当に [root] ユーザのcronエントリを削除してよろしいですか?

cronエントリを削除する場合は [y]、キャンセルする場合は [n] と入力して下さい。
n　→　「n」と入力して、crontab情報削除をキャンセルします。
[root] ユーザのcrontabエントリは削除せず終了します。
#
```

以下のようにexample-userユーザのcrontab情報は削除されずに残っております。

```
# crontab -l -u example-user
* * * * * /home/example-user/example.sh
30 10 * * * /home/example-user/example.sh
50 10 * * * /home/example-user/example.sh
#
```

以下の例では「crontab -r -u ユーザ名」コマンドで、example-userユーザのcrontabを編集しています。crontab情報削除を実行する例です。

```
# crontab -r -u example-user
[/usr/bin/crontab.bin -r] を実行しようとしています。
crontabコマンドで [-r] オプションをつけて実行すると、crontabコマンドを実行したユーザ [root] のcronエントリが削除されます。
本当に [root] ユーザのcronエントリを削除してよろしいですか?

cronエントリを削除する場合は [y]、キャンセルする場合は [n] と入力して下さい。
y　→　「y」と入力して、crontab情報削除を実行します。
[root] ユーザのcrontabエントリを削除します。
crontabファイルが格納されているディレクトリ [/var/spool/cron] をバックアップします。
cp -Rp /var/spool/cron /var/spool/cron.20150125_003442
#
```

以下のようにexample-userユーザのcrontab情報が削除されます。

```
# crontab -l -u example-user
no crontab for example-user
#

# su - example-user
$ id
uid=501(example-user) gid=502(example-group) groups=502(example-group) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
$ crontab -l
no crontab for example-user
$
```

削除前のcrontab情報もバックアップされます。

```
# ll /var/spool/cron.20150125_003442/example-user
-rw-------. 1 root root 124 Jan 25 00:33 /var/spool/cron.20150125_003442/example-user
# cat /var/spool/cron.20150125_003442/example-user
* * * * * /home/example-user/example.sh
30 10 * * * /home/example-user/example.sh
50 10 * * * /home/example-user/example.sh
#
```

最後に
===

今回の例ではcrontabバックアップを/var/spool/配下に複数世代保存するようにしています。

```
# ll /var/spool/
drwx------.  2 root root 4096 Jan 25 00:46 cron
drwx------.  2 root root 4096 Jan 24 23:09 cron.20150124_231713
drwx------.  2 root root 4096 Jan 24 23:09 cron.20150124_231908
drwx------.  2 root root 4096 Jan 25 00:19 cron.20150125_001904
drwx------.  2 root root 4096 Jan 25 00:22 cron.20150125_002441
drwx------.  2 root root 4096 Jan 25 00:24 cron.20150125_003051
drwx------.  2 root root 4096 Jan 25 00:33 cron.20150125_003442
```

crontab -rでcrontab情報を削除する度、バックアップファイルが増えていきますので、運用していくにあたっては、古い日付のバックアップファイルは自動で消すなどの対応が必要になるかもしれませんね。

最後に、意図せずに誤ってcrontab情報を削除してしまうと、色々と問題が生じてしまう事が多いかと思います。気をつけていきたいですね。

長くなりましたが、以上になります。