Edited at

shellでTTL付きのキャッシュをしたい

More than 1 year has passed since last update.


こんな感じ

sleep 3 ; echoの結果を最大5秒間キャッシュしている様子。


キャッシュの実装

実装はこのあたりに置いてある。

TTL付きのキャッシュを実装するにあたり、シェルで実現するならファイルに保存しておいて、作成日時はそのファイルのメタ情報を利用すれば楽。


また、やろうと思えば更新日時と作成日時がどちらも取れるので、細かいTTLの実装も可能。

stat -c %Yコマンドで指定したファイルの最終更新日時のepoch秒を取得できる。

それと現在時刻を比べて指定した時間より経過していたら...という処理を書くことでTTLを実装する。

statを使ってファイルの有効期限判定するには以下のようにする。

$ touch -d '1day ago' hoge.txt

$ [ $(( $(date +%s) - $(stat -c %Y hoge.txt) )) -gt $((60 * 60 * 25)) ] && echo 'true' || echo 'false'
false
$ [ $(( $(date +%s) - $(stat -c %Y hoge.txt) )) -gt $((60 * 60 * 23)) ] && echo 'true' || echo 'false'
true

キャッシュを保存するファイルのパスとTTL、実行するコマンドを渡せるようにするとこんな感じになった。

# TTL的に無効かどうか

function is_too_old() {
local file=$1
local ttl=$2 # seconds
if [ -f $file ]; then
[ $(( $(date +%s) - $(stat -c %Y "$1") )) -gt $ttl ]
else
false
fi
}

# TTLを考慮して読み出す
function read_from_cache() {
local cache=$1
local ttl=$2 # seconds
local command=$3
if $(is_too_old $cache $ttl);then
local result=$(sh -c "$command")
if [ -z $result ]; then
return
else
echo $result > $cache
fi
fi
cat $cache
}

ちなみにMacだとgnubinを入れる必要がある。

テキスト処理のための標準的なコマンド群の OS X への導入手順 - Qiita

コードを読めばわかるが、例えばgcloud projects listの結果を1日キャッシュするには、

read_from_cache ./cache.txt $((60 * 60 * 24)) "gcloud projects list"

みたいにやれば良い。


"読み込み中"の表示

昔書いた記事に大体ある。

Zshで長い処理をしている間に読込中を表示する - Qiita

loadingコマンドはgithubにおいてある。

読み込み中を表示するあたりの実装はこうなる。

# job制御を無効化してバックグラウンド実行しているプロセスのIDが表示されるのを防ぐ

set +m
# loadingをバックグラウンド実行して表示
loading 1000 &
local loading_pid=$!

# 何か重い処理をする
local result=$(sh -c "$command")

# loadingを消す
kill -INT $loading_pid &>/dev/null
# loadingがkillされるのを待つ
sleep 0.1
# job制御を有効に戻す
set -m

ジョブ制御を無効にしてプロセスIDが表示されてしまうのを防ぐのがポイント。

$ sleep 10000 &  # IDが表示される

[1] 89535
$ kill $! # IDが表示される
[1] + terminated sleep 10000
$ set +m
$ sleep 10000 & # IDが表示されない
$ kill $! # IDが表示されない
$ set -m