Cron で動かすスクリプトの開発方法例
(2015年に書いたものをコピペ)
普通の使い捨てスクリプトと同じ作り方をしてませんか?
そういう人がよくぶち当たるのが,cronで実行したらコマンドが見つからないだとか環境変数が設定されてなくてもうダメという状況.
そんな状況を回避するには以下の2つの手法を取りましょう.
- ジョブ実行前に環境変数を設定する
- cronでの実行環境を再現してジョブを作成する
結論だけ見る方はこちらのテンプレート.
ひとまず,環境変数の確認方法
環境変数を出力するジョブを追加します.
* * * * * env >/tmp/cron_env
/tmp/cron_env
の中身を見てみましょう.下記は筆者の端末(Mac)で実行した場合です.
SHELL=/bin/sh USER=jmatsu PATH=/usr/bin:/bin PWD=/Users/jmatsu SHLVL=1 HOME=/Users/jmatsu LOGNAME=jmatsu _=/usr/bin/env
少ないなんてレベルではありませんね.
PATH
に至っては正直使い物になりません.コマンドが見つからないわけです.
cronで実行するときのみ有効になるよう環境変数を設定する
なければ設定してしまえば良い,という形です. 複数の方法があり,それぞれ少しずつ特徴があります.
ログインシェルとしてシェルを起動し,それぞれのprofileを読み込む
lオプションを渡してログインシェルとしてシェルを起動させ,~/.bash_profile
等を読み込ませる方法です.
* * * * * /usr/local/bin/bash -l -c 'env > /tmp/cron_env_2'
自分馴染みの環境変数を設定することが可能になります.
ただしそのジョブごとに必要最小限の環境変数を設定するといった行為はできませんし,他人と共有するジョブで取る手段としては良いと言い切れない部分があります. 当然のことながら,lオプションでログインシェル化できないシェルは利用できません.
また読み込みに行く設定ファイルはBashだと /etc/profile, ~/.bash_profile, ~/.bash_login, ~/.profile
であり,明示的に呼び出さない限り ~/.bashrc
は読み込みにいきません.
インタラクティブシェルとログインシェルの違いですね.
crontabに直接環境変数を設定する
こちらも各ジョブに対する設定ではなく,全体になります.
PATH=/usr/local/bin:/usr/bin:/bin SOME_VAR="cron_env_3 and cron_env_4" * * * * * env > /tmp/cron_env_3 SOME_VAR2="cron_env_4 only" * * * * * env > /tmp/cron_env_4
明示的に指定することができ,影響範囲も前述の方法と違って完全にcrontab内に収まる形です.
しかしながら,PATH=/usr/local/bin:$PATH
や PATH=$HOME/bin:/usr/local/bin:/usr/bin:/bin
と記述することはできません.
まだ変数が設定されていないため・・・とかではなく,そもそもシェルスクリプトではないからです.
コマンド実行時に環境変数を明示的に指定する
こちらはシェルでお馴染みの記法です.
* * * * * PATH="/usr/local/bin:$PATH" env > /tmp/cron_env_5
各ジョブごとに設定することが可能ですし,シェルに解釈させる部分に記述できるので非常に書きやすいでしょう. 個人的にも正攻法だと思いますが,記述が長くなってくるとどうしても厳しいものがあります.
コマンド実行前に指定した設定ファイルを読み込んでおく
ジョブごとにファイルを用意すれば,柔軟に環境変数を定義しておくことが可能です.
export SOME_VAR="Hey"
# ;の次にスペースを入れると正しく動作しません. * * * * * . ~/cron.env;env > /tmp/cron_env_6 # or * * * * * source ~/cron.env;env > /tmp/cron_env_7
外部ファイルに必要な変数を分離しておくと非常に管理がしやすいです. 特にVCS管理下であれば,同じリポジトリ上に置いておくだけで非常に共有しやすいでしょう.
指定した外部設定ファイルを読み込んでから実行するジョブにする
上記の記法ではスペースを入れると動かないといったヒューマンエラーが避けられませんね.
ならばその自体をスクリプトとしてラップしましょう. 個人的に最良だと思っている選択肢がこれです.
* * * * * ~/run_with_env.sh > /tmp/cron_env_8
#!/bin/sh # 外部ファイルから環境変数を読み込む source ~/.some_profile # 環境変数の設定を記述する export SOME_ENV_VAR="Hey" # job env
cronで実行するときの環境変数を再現してデバッグする
前提
- cronジョブ実行時の環境変数を前述した方法で取得し,
/tmp/cron_env
(任意)に保存しておくこと
また実行したジョブは ~/somejob.sh
で実行できるとします.
直接再現する
env - $(cat /tmp/cron_env) ~/somejob.sh
として起動することで直接cronにおける環境変数状況を再現することができます.
これは最初に環境変数を空にし,
env -
次にcronジョブ実行時の環境変数を読み込むよう設定し,
env - $(cat /tmp/cron_env)
その環境変数の状態でジョブを実行することを表します.
その上で足りない環境変数は上記で紹介した いずれかの方法で設定する必要があります.
インタラクティブシェル env - $(cat /tmp/cron_env) /bin/sh
上でのデバッグやaliasの設定を行うと楽でしょう.
ただしこれはジョブを実行するステートメントで効くものであり,使い勝手が良いとは個人的には思いません.
間接的に再現する
環境変数をcronジョブ実行時と同様のものに設定し,なんらかの方法で環境変数を設定,ジョブ実行をするようなハブとなるスクリプトを用いてデバッグしましょう.
#!/bin/sh # reset env while read line; do eval "$line" done < <(diff <(env) /tmp/cron_env|grep "^[><]"|grep -v " _="|sed -e 's/^< \([^=]*\)=.*/unset \1/' -e 's/^>/export/'|sort -r) ~/somejob.sh
環境変数リセット部分は以下の方針になります.
- 現在の環境変数とcron実行時の環境変数の差分を取る
- 差分行情報はいらないので実差分情報だけ取得する.また _ は除いておく.
- 差分形式を置換する.現在の環境変数をすべてunsetし,cron実行時の環境変数をすべてexportする形に整形する
- 先にunsetするようにソートする
- 整形した文字列をwhileに流し込み,1行ずつevalすることでexportあるいはunsetする.
この形式でデバッグを行うと,実行時には環境変数リセットの部分を除いた hub.sh
をcronのジョブとして指定すれば良いわけですから非常に楽ですね.
直接的な再現と間接的な再現の差異
外部ファイルからの環境変数設定を考えます.
間接的に再現するケースでは,外部に環境変数用のスクリプトを用意し,source
あるいは .
による読み込みを行うことで実現されます.
#!/bin/sh # reset env while read line; do eval "$line" done < <(diff <(env) /tmp/cron_env|grep "^[><]"|grep -v " _="|sed -e 's/^< \([^=]*\)=.*/unset \1/' -e 's/^>/export/'|sort -r) source ~/.some_profile ~/somejob.sh
export EXT_VAR1="one" export EXT_VAR2="two"
これは完全にシェルスクリプトですね.必要なように実行シェルをカスタマイズするわけです.
直接的に再現するケースでも当然のことながら不可能ではありません.
env - $(cat /tmp/cron_env) $(cat ~/ext_vars) ~/somejob.sh
EXT_VAR1="one" EXT_VAR2="two"
しかしながら外部ファイルの中身の記述を見れば分かる通り,これはシェル変数の記述となります.
つまり実行コマンドと同時に設定する方法以外では,環境変数用のファイルとしてそのまま用いることができません.
まあ一手間加えて,source <(awk '$0="export " $0')
のようにすればできますが.
間接的再現の仕組みの方が記法としても環境変数として直感的ですし,個人的に軍配は間接的再現側にあがると思っています.
結論
~/somejob.sh
というジョブを実行するという前提.
デバッグ時の環境
% ~/for_debug.sh
#!/bin/sh # reset env while read line; do eval "$line" done < <(diff <(env) /tmp/cron_env|grep "^[><]"|grep -v " _="|sed -e 's/^< \([^=]*\)=.*/unset \1/' -e 's/^>/export/'|sort -r) # 外部ファイルから環境変数を読み込む source ~/.some_profile # 環境変数の設定を記述する export SOME_ENV_VAR="Hey" ~/somejob.sh
実行時の環境
* * * * * ~/for_runtime.sh
#!/bin/sh # 外部ファイルから環境変数を読み込む source ~/.some_profile # 環境変数の設定を記述する export SOME_ENV_VAR="Hey" ~/somejob.sh
何か間違いやアドバイス等ありましたらコメントにてお願い致します.
劇場版SHIROBAKOを見た
不要不急の外出がどうだの色々言われているけれど、これくらい行っていいはずだよ経済は回していこうなと自分に言い聞かせて見てきた。
こんふぁーさんにほとんど言われてしまったのウケるけど、雑に感想を書く。もちろんネタバレがあるので注意。
- はー、良い
- 最初なんかツラい気持ちになって、こっからどうやったら良くなるの?これもしかして不要不急だった?って思いながら見てた
- みゃーもり、幻想見ちゃうのはさすがにヤバいかもしれない
- 絵麻の生活レベルが爆上がりしててビビる
- 4文字以上で久乃木さんが会話出来るようになってて凄い、えらい
- 鳴き両面清老頭をツモアガリでなんであんなに平然としてられるの?
- 興津さん変わらなくていいね
- 矢野さんも変わらなくていいね
- クリームにライチリキュール入れてテンション高い本田さんの後ろで女性店員が超ジト目しててめちゃ好き
- ねいちゃん子ども産んでんじゃんマジかよ
- 杉江さんのヒゲはいらないけど、ワイフ呼びは草
- ミムジーの最初の声で「あれ?声忘れちゃった?」と思ったのに、2回目以降の声でアジャストしてきて最高だった
- ミュージカル始まって真顔になった*1
- 宮井激推し人材じゃん
- 松亭のメニュー見てると「それそれ」ってなった。TVではフライドポテト出てきたけど、言わないと出てこないメニューだし、鳥豆腐は美味しいし大体頼むし本当に優秀。あと青い皿は刺し身とかフライ系でよく使うよね分かる。
- 松亭のレイアウト、あれ相変わらず団体貸切とかしないとならないやつだよね?マニアックすぎひん?
- モンブランをヨーロッパ最高峰とするのは意見が割れるのでノーコメント
- 瀬川さんいいよ瀬川さん
- 下柳さんと遠藤さんのマブダチ感マジすこ
- 下柳さんの「お前の絵を動かす」あたりのマインド、めちゃくちゃかっけーって思った
- 遠藤くん呼びはあかんし、まゆみさん最高だよ。ところでOKストアいいよね
- 太郎の声聞くとイラッとするけど今回は良いことしか言ってないよな、ごめんなイラッとして
- 平岡マジか、お前ただのイケメンじゃん。「お、ラッキー」みたいな感じでjoinしたくせにキッチリお礼を言うのマジ偉い
- 絵麻が小笠原さんの口調学習してて、しかも井口さんの押しの強さも学習してて、あんな感じで詰めるの攻撃力高すぎて笑っちゃう
- 円さん、デジタルコンテなんだなぁ*2
- 変な話ィ、うざすぎてキレそう
- 今回の悪役も心置きなく嫌いになれる系でいい
- 師匠-弟子からの商売敵っていうの最高か?
- 渥美さんの雲、まさかの星雲に到達するの草生える
- さらっという「あと一分見たかったかな」という言葉が本質すぎて相変わらず本質を捉える天才か?
- 浜松ロケの話がコアすぎて超好き。鰻くれ
- 最後の爆発のスモッグ、最高のエフェクトじゃん。そういうとこだぞ
- 瀬川さんはスティック逆手操作なんですね
- 堂本さんの息子よーちゃん、屋台でご飯食ってるけど飲んでないよね?4年前に中学生だよね?
- よーちゃんが働いてるところ実はあったのかなぁ、見逃したなぁ
- これもしシクって公開延期してたらいくつかのセリフあかんやん、って感じで締め切りとクオリティに対する最高のドライブを感じた
- ところで打ち合わせのカフェ、なんでソファボロボロなの
- 堀田さん呼ばれないのじわじわくる(超飛空要艦マジダス大好きっ子で、遠藤さんの後輩で3Dボロクソにいってた人)
つまり松亭に行きたい、感想を語らいたい、お互いに「よかった」「それな」って言い合いたい。って気持ちで胸がいっぱいです
App Shortcuts の targetPackage/targetClass に string resource も manifest placeholder も使えない
DroidKaigi 2020 アプリで App Shortcuts を設定する際、色々と試行錯誤をしてくださった方がいたんですが、僕の理解が足りずに手を煩わせてしまったのでざっと調査しました。
当然ハードコーディングすれば起動しますし、string resource を使えば preference などからは targetPackage/targetClass も動作します。ただ App Shotcuts がどうしても出来ず、main/resources/xml/shortcuts.xml
に対して、次の3通りを試してみました。
- 動的に定義した string resource
- main/resources/values/strings.xml で定義した string resource
- Manifest Placeholder
動的に定義した string resource
上記の通り、string resource が展開されず resource の id が表示されています。*1
main/resources/values/strings.xml で定義した string resource
動的に生成しない場合、つまり main resource からは確実に見える resource を使ってみました。
こちらもやはり展開されませんでした。
Manifest Placeholder
App Shortcuts 用のファイルは Android Manifest に meta-data として埋め込むため、Manifest Merger か Injector が頑張ってくれるかも?と思い、試してみます。
無理でした。Placeholder (放置)。
余談
com.android.tools.build:gradle:3.6.0-rc01
(3.5.3 でも同様でした) では以下の方法で動的に application id を resource に差し込む必要が出てきました。API breaking changes があり、設計思想及び変更の propagation が変わったため注意した方が良さそうです。
android { applicationVariants.configureEach { variant -> variant.resValue('string', 'application_id', variant.applicationId) } }
resource value に限らず、今までは mergedFlavor を利用する以下の方法があったかと思いますが、現在は動作しません。(下記は3.4.x時代の公式ドキュメントサンプルから転載)*2
android.applicationVariants.all { variant -> def mergedFlavor = variant.getMergedFlavor() // Defines the value of a build variable you can use in the manifest. mergedFlavor.manifestPlaceholders = [hostName:"www.example.com/${variant.versionName}"] }
*1:多分。なんか昔はミスりまくって時々見かけたはずなんですが、今は逆にこれを再現することができないでいます。
*2:https://google.github.io/android-gradle-dsl/current/com.android.build.gradle.AppExtension.html#com.android.build.gradle.AppExtension:applicationVariants
Keynote のテキスト貼り付けがストレスすぎるので、Karabiner Elements の complex modifications 機能を使って解決する
2020/01/08 追記: 結局以下のようになった。
もう令和も2年となるというのに、Keynote で資料を作るときのスタイル維持テキストペーストがストレスすぎる。スタイルマッチテキストペースト*1もできるけれど、押しづらいなぁ・・・と思ったので、Key Mapping を設定することにした。
ざっと見た限り、macOS標準のキーボード設定では自由に Key Mapping が作れそうになかった。そこで Karabiner Elements の Complex Modifications 機能を使って対応できたのでメモ代わりに残しておく。
作成したもの
Keynote のときのみ、Command + Shift + Option + V と Command + V を入れ替えるようにした。
or import to Karabiner-Elements
メモ
README やサンプルが超充実してたので、ほとんど困らなかった。それでも追加で調べた/わかったことのみ記述する。
- GUI から設定できるのは有効/無効しかないので、独自定義時は構成ファイル作成が必須
- 公式ドキュメント https://pqrs.org/osx/karabiner/json.html
- pqrs-org/KE-complex_modifications にある構成ファイルを適宜利用することが可能だけど、サンプルとして利用するだけにしたほうが良さそう
- 上記リポジトリ内のスクリプトが generator/linter として動作するので、利用するべき
~/.config/karabiner/assets/complex_modifications/
以下に配置すれば利用できるので、わざわざ本家にPR を出す必要はない- Hot Reload がないため、Rule の Disable -> Enable を行わないと動作確認ができない
- ERB の helper method が呼び出し側から見ると非直感的だったので、生で json を書いてしまった方が楽
*1:Command + Shift + Option + V
Beer Advent Calendar Day 9: Gruut & BrewDog Brussels
Beer Advent Calendar Day 9、完全に投稿を忘れていました。2019年もいっぱい飲みましたね。
2019/12/09 はベルギーはブリュッセルとゲントからお届けしたかった。
ゲント
Brouwerij Gruut というめちゃくちゃ有名な醸造所にいきました。ツアーは9人以上からのようで、残念。
Winter beer を飲みました。チリパウダーとメープルシロップを後足ししてるのか、樽で入れてるのか、ちょっと聞き取れなかったんですがメープルシロップの燻したような匂いと甘み、後味にチリパウダーの辛味が占めてくれて大変美味しい。
ブリュッセル
BrewDog Brussels へ。
おなじみ BrewDog ビールから Elvis Juice (Grape Fruits IPA)。グレープフルーツをまるごと飲んでるような果実味とスムーズさがたまらないビールです。IBU 60 なので IPA としては一般的な苦味ですが、ホップよりはグレープフルーツの皮の苦味を感じるので毛色が異なります。
続いて Hazy Jane (New England IPA) 。うまい(うまい)。ホップ感満載なのにぐびぐび飲める IBU 50 のモンスター。うまい(うまい)。
来年の抱負
もう5年目?何年目か忘れたけど毎年いってるので、来年もまたベルギー行こうと思います。
知らなかったことを知るのが好きなんです!
SHIROBAKO Advent Calendar Day4 です。もう5年目の Day4 なんですね、正直ウケる。
知らなかったことを知るのが好きなんです!
第14話、りーちゃんこと今井みどりのセリフです。この言葉はストーリーに対して大きく影響を与えるというものではないと思うのですが、個人的に好きなセリフのうちの1つです。
宮森が「武蔵境の駅にディーゼル車が通っていのかどうか」を調べる上でやや悩んでいたところ、率先してりーちゃんがそのタスクを引き取り、宮森の心配(一種の罪悪感とも言えましょうが)に対しての言った言葉です。お金も出ないのに。*1
りーちゃんは脚本家を目指しており、その延長、あるいは手前、もしくは道中、自分の引き出しを広げていきたいという思いを持っています。「知らないことを知るということ」、つまりは「好奇心を満たすこと」が自分のためになる、またそれを好きであるということで非常に好感を持てますね。かわいいよりーちゃん。
好奇心を満たすということ
ものづくりをする上で、好奇心を満たすということは非常に重要なことだと思っています*2。
現在自分はエンジニア(Android/Backend/SRE)という職業にいまして、個人的に何かと理屈をつけた方がしっかり地に足をつけて頑張れるということから、日々の業務や日常ルーティーン(生活含む)を以下のように区分しています。
- 経験知を利用した既知への対応と応用 e.g. bug fixes, 余った食材でシュッと料理を作る
- 外的要因による半ば強制力の存在する新領域への挑戦 e.g. iOS アプリ開発
- 個人の好奇心による新領域・既知領域での挑戦 e.g. terraform plugin, 創作料理
- 感情を捨ててでもやらないといけないもの e.g. 経費精算, 年末調整, 確定申告
- 脊髄反射で処理するもの e.g. プロテインドリンク作り
まあ例は適当なのでさておき、このうち、やはり「個人の好奇心による新領域・既知領域での挑戦」というのは日々のモチベーション、ひいてはその環境や習慣に関わり続ける上でのモチベーションへ絡むと感じています。お金が好きな僕ですが、仕事や日常生活ではこの度合いがどれだけ強いかで継続性が変わります。
例えば料理を食すのも作るのも好きであるということで、色んな国の色んなレストランでの食事を家で再現できるように研究したり、各国の調理方法を試しては他人で実験して好奇心を満たしているきらいがあります。業務では新しい言語への挑戦、インフラの経験が少ないので会社リソースを使って勉強・応用したり、旅行中は現地語でビールを頼んだり、バーで知らない人に話しかけたり、Airbnb やゲストハウスのホストと話したり・・・自分の行動のありとあらゆることが好奇心に根付いていると思っています。最近ダイエットをして走るときも「知らない道を走る」という行為だけで景色を楽しみつつ、いつもの倍走れるんだから不思議です。
もちろん人によってはつまらないかもしれません。ただ自分の好奇心が他人と同じである必要は全くありません。偶然被ったとしても、寄せる必要は一切ありません。そんな一種の強迫観念の中で良いものが作れるとは思いません。知らんけど。
作中における好奇心
SHIROBAKO はアニメづくりのアニメであると説明する必要はないかと思います。同じ「ものづくり」という面、そして好奇心に基づく場面はたくさんあります。 e.g. みーちゃんのタイヤ以外のモデリングへの欲求, 本田さんのお菓子職人への転身
ちょっと出張中はネットワーク弱すぎて全然見れなかったので適切な例が出てこないんですが、もっとあります・・・
おまけ
ロンドンの料理は絶対再現しません。好奇心に勝る理性というのも時には大事です。
飲酒プログラミング in Tokyo, Brussels, Madrid, Tel Aviv and London
飲酒プログラミングカレンダー2日目。現在海外出張中なので、ラウンジや機内などで飲酒プログラミング祭りをしました。なおプログラミング風景の写真等は一切ありません。
Tokyo
羽田のラウンジでカレーとビールを片手に terraform をモリモリやっていきます。
Brussels
Transit. ラウンジで飲みながら jmatsu/terraform-provider-slack を直していく。
おまけ
そういえばベルギーはフランス語とフラマン語が多く、ブリュッセル付近はフランス語が多いです。そのためビールがほしいときはフランス語で Une bière, s’il vous plaît (瓶ビール)とか Une pression, s’il vous plaît (ドラフト)といえば飲めるよ!って言う人がいるんですが、無理です。多分いったことがないフランス語話者なのかなーって思います。
1つのお店にはほぼ確実に複数種類あるので、銘柄を指定しないと頼めません。Une demi Leffe Bronde, s'il vous plaît. とか。なおフラマン語は全く分からないです。いつも行くお店が完全にフランス語なので必要に迫られず・・・しかも去年のアムステルダムではオランダ語を一切調べなかったので注文の仕方はわからず。ただ pintje? がビールってことだけ覚えてます。
Madrid
色々あって VPN を失ったことに気づき、かつネットワークが弱すぎたので terraform 関連の作業を諦めて、食事に専念します。
なおスペイン語でビールください、は Una cerveza por favor. おかわりは Una mas cerveza por favor. だということを今回学びました。Una copa de vino tinto por favor! で赤ワイン(グラス)くださいになることとかも覚えたので、2日目にはどや顔しながらスペイン語で飲み物だけ注文できるマンになりました。
話は戻ってご飯を食べます。スペイン語しか通じない老舗パエリア屋でうさぎとエスカルゴのパエリア。ほのかに香るクローブとターメリックがとても美味しい。
次の日のブランチはなんとなくハンバーガー。こっちはパンがやっぱり美味しい。
弊社 CEO と二人で Ubuntu での L2TP VPN を頑張りました。プログラミングではないけど本行程で一番本気で集中した気がします。超ハマったので、詳しい内容は別で記事にします。
さて、VPN 設定も復活したのでちょっとだけ・・・とはいかず、そのまま飯。イカとクロケット。
寝起きで作業した terraform で発狂したので朝からビール。
最後にラウンジからビールとパエリアです。
そういえばスペインの Cider は Lambic の Gueuze みたいで、甘みが抑えめでした。そういうのが好きならぜひ。
Tel Aviv
イスラエルに移動しました。イスラエルの国産ビールは goldstar, maccabee が2大巨頭らしいです。goldstar がblondeでやや重め、カラメルホップが聞いてます。maccabee は水系です。またその2種類を除くとドラフト含め、 carlsberg や stella artois といったヨーロッパのビールが多いです。時々 Jems というクラフトビールもありました。
WeWorkにはちゃんとビールが。ここは Jems。飲むのは我慢しました。なお本社ではないです。
そして夜に Taybeh というパレスチナビールを発見。数年前に外語大の学祭の世界のビールがいっぱいある出店で飲んで、めちゃくちゃ感動したことを覚えてます。まあ名前で調べて思い出したんですけど。Light がめちゃくちゃ好きなんですが、日本から買うと頭のおかしい値段がするので誰かおごってください。
Hebrew でのビールの頼み方は一切覚えられませんでした。残念。
イスラエルでは移動が多かったのでコード書いてない気がする。宿帰還即寝てた・・・
London
初ロンドン。ここはやはり Brewdog を攻めていきます。まず Brixton branch.
なんか近くでライブがあったのか、尋常じゃない治安の悪さで最高でした。作業なんてできるわけがない。
次に日は変わって Seven Dials branch.
Brewdog brand の品揃えは日本と変わらず、guest beer を飲むのが良さそう。
ということでこの記事は今 Seven Dials の Brewdog からシュッと書いてます。 (本番とかデータ触る系はやっちゃだめだけど)飲酒プログラミングはいいぞ。
というかこれはただの飲酒記事なのでは・・・