cronジョブでは実行時に環境変数が最小限の 環境変数しか設定されないため、例えばPATHとかも最小限で 普段使っているコマンドが使えなかったりします。
その場合には走らせるcronジョブのスクリプトの中で設定するか、 もしくはcronジョブの定義で変数を設定してあげる必要があります。
デフォルトの環境変数
$ cat a.sh
#!/bin/bash
env > $HOME/cron.log
こんな感じのスクリプトを作ってchmod 755
しておいて、
crontab -e
で
39 * * * * $HOME/a.sh
と設定し(39は分、すぐに実行できる時間に)調べてみます。
CentOS 7だと
XDG_SESSION_ID=<xxx>
SHELL=/bin/sh
USER=<user>
PATH=/usr/bin:/bin
_=/usr/bin/env
PWD=/home/<user>
LANG=en_US.UTF-8
HOME=/home/<user>
SHLVL=2
LOGNAME=<user>
XDG_RUNTIME_DIR=/run/user/<uid>
macOS Catalinaだと
SHELL=/bin/sh
USER=<user>
PATH=/usr/bin:/bin
_=/usr/bin/env
PWD=/Users/<user>
HOME=/Users/<user>
SHLVL=2
LOGNAME=<user>
みたいな感じで基本的にUSER
とHOME
、PATH
くらいです。
PATH
も/usr/bin:/bin
と必要最小限のものしか通ってません。
このまま普段使っているシェルスクリプトとかをcronジョブに設定すると 中で使っているコマンドにPATHが通って無くて失敗したりします。
環境変数の設定
環境変数は、cronのジョブの定義をする前に変数設定を書いてあげるとそれを使ってくれます。
MY_VAL=xxx
39 * * * * $HOME/a.sh
とすればMY_VAL=xxx
が見れるはずです。
もし、
$ cat b.sh
#!/bin/bash
env > $HOME/cron2.log
を用意して
MY_VAL=xxx
39 * * * * $HOME/a.sh
MY_VAL2=yyy
39 * * * * $HOME/b.sh
みたいなcronの定義をすると
cron.logの方にはMY_VAL=xxx
だけが出て、cron2.logの方にはMY_VAL=xxx
とMY_VAL2=yyy
の両方が出てきます。
上から順に見ていき、ジョブの定義の時点でどういうものが設定されているか、 がそのまま渡される形です。
ちょっと注意が必要なのは、cronの定義はシェルスクリプトではない、ということです。
シェルスクリプト(もしくは普段のシェル上での作業)で環境変数を設定するには
export
が必要で、使わないとただのシェル変数でサブプロセスに引き継がれません。
一方、cronの定義では上の様なたんなるX=Y
の形で環境変数を設定することになります。
export
とかは使えません。
また、前に定義した変数を使って変数を定義することはできません。
MY_VAL=xxx
MY_VAL2=$MY_VAL
とかしてもMY_VAL2
には$MY_VALという文字列そのものが設定されてしまいます。
一方、ジョブの定義では使えて
COMMAND_PATH=/path/to/command
39 * * * * $COMMAND_PATH/my_command
みたいなことはちゃんと$COMMAND_PAHT
を展開してくれます。
もちろん、my_command
の中でも$COMMAND_PATH
を使えます。
cronの特殊な変数
cronで良く設定する変数としては
[email protected]
の様にMAILTO
を設定して出力をメールに送るものだと思います。
設定しないと出力はローカルマシンの該当ユーザーのメールに送られます。
勿論これを設定しておけばこの値をスクリプトの中で使うことも出来ます。
もう一つ、SHELL
の設定。
上を見ても設定されているのがわかりますが、このシェルでコマンドが実行されます。
通常/bin/sh
がデフォルトなので、普段/bin/bash
を使ってる感覚でコマンドを定義すると
失敗することがあります。
出力をファイルに出そうとして>& log
とかしたら>&
が使えなかった、とか。
なるべくcronジョブに設定するものは専用のスクリプトを用意して、
出力とかも全部中で処理するようにすると、
外側でのSHELL
の影響を受けなくなるので良いかとは思います。
cronジョブの書き方
ついでによくわからなくなるcronジョブの書き方のまとめ。
ジョブの定義としてはユーザー単位で定義する場合には通常crontab -e
で定義ファイルを開いて編集。
システムとして登録する場合には
/etc/cron.d/の中に定義ファイルを配置。
どちらも書き方は同じようなものですが、基本的に
<分> <時> <日> <月> <曜日> my command
と書いていつ実行するか、を定義します。 後ろに実行コマンドを書きますが、/etc/cron.d/の場合にはその前にユーザーを指定する必要があります。
<分> <時> <日> <月> <曜日> user my command...
*
を指定すれば全ての場合、になります。
10 * * * *
なら毎時10分に実行。
10 1 * * *
なら毎日1時10分に実行。
曜日、は0~7で0と7が日曜、間の1から6が月曜から土曜になります。
月と曜日に関してはJan
、Sun
など名前の3文字でも指定できます(大文字小文字は関係なし)。
また、1時間のうちに複数回実行したいなら
10,40 * * * *
の様にコンマで区切って複数指定することが出来ます。
10 1-3 * * *
の様にハイフンでつなぐと、上野場合は1時、2時、3時の全てで10分にじっこうすることになります。
または、
*/10 * * * *
の様な書き方をすると10分ごとに実行してくれます。
もしくは
15-35/10 * * * *
とかすると、 15分、25分、35分に実行されます。(15-35分の中で10分おき)
もし、日にちと曜日の両方を*
以外で指定していた場合、どちらかが満たされている場合にOR
で実行されます。
*
が全部を表すのに使うので、指定するとAND
になりそうなものですがちょっと注意が必要です。
これ以外にも 以下の様にリブート時を指定したり、毎時、などを簡略化してかける方法もあります。
@reboot Run once, at startup.
@yearly Run once a year, "0 0 1 1 *".
@annually (same as @yearly)
@monthly Run once a month, "0 0 1 * *".
@weekly Run once a week, "0 0 * * 0".
@daily Run once a day, "0 0 * * *".
@midnight (same as @daily)
@hourly Run once an hour, "0 * * * *".
%をエスケープしないといけない
cronジョブの定義で%
は特殊文字となり
それ以降の文字列がコマンドへの標準入力として使われる様になります。
コマンドの中で%
を使いたい場合には\
でエスケープする必要があります。
10 * * * * date +%s
だと
date + < <(echo s)
というコマンドを実行するのと同じになります。
この場合は何も出力されないだけですが、普通はなにか変なエラーになるかと。
正しくやりたい通りにするためには
10 * * * * date +\%s
の様に%
をエスケープしてやる必要があります。