blog.petitviolet.net

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

2017-05-04

QiitaZshshell

こんな感じ

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

cache-ttl-on-sh.gif

キャッシュの実装

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

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

from: https://qiita.com/petitviolet/items/6c68cf414050fa8b83d6