bash/dateによる時刻パース&演算

/ bash

bashシェルスクリプトとdateコマンドによって任意フォーマットの時刻タイムスタンプをパースして実数値演算するスクリプト例を掲載する。

はじめに

"$(date +"%Y%m%d-%H%M%S-%N") Start powerful bash-script now!"

# some wonderful bash-script!

"$(date +"%Y%m%d-%H%M%S-%N") End awesome bash-script now!"

こんな感じで適当な気持ちでフォーマットしたタイムスタンプ付きログを出力した後にその時刻差分を計算したくなる。

これからやる方法はいわゆるバッドノウハウであるのでどうしても必要に迫られたとき以外はこういうことをしてはいけない!良い子はPythonやPerlなどを最初から使うように!

スクリプト

#!/usr/bin/env bash

dateparse() {
    if [[ $1 =~ ^([0-9]{4})([0-9]{2})([0-9]{2})-([0-9]{2})([0-9]{2})([0-9]{2})-([0-9]{9}) ]]; then
        local YYYY=${BASH_REMATCH[1]}
        local mm=${BASH_REMATCH[2]}
        local DD=${BASH_REMATCH[3]}
        local HH=${BASH_REMATCH[4]}
        local MM=${BASH_REMATCH[5]}
        local SS=${BASH_REMATCH[6]}
        local N=${BASH_REMATCH[7]}
        echo "$(date --date="$YYYY/$mm/$DD $HH:$MM:$SS" "+%s")"."$N"
    fi
}

LINE="$(date +"%Y%m%d-%H%M%S-%N") Start powerful bash-script now!"
TIME1=$(dateparse "$LINE")

sleep 1.2

LINE="$(date +"%Y%m%d-%H%M%S-%N") End awesome bash-script now!"
TIME2=$(dateparse "$LINE")

echo TIME1="$TIME1"
echo TIME2="$TIME2"

DIFF=$(bc -l <<<"$TIME2 - $TIME1")

echo DIFF="$DIFF"

if [[ $(bc -l <<<"$DIFF > 1.2") == 1 ]]; then
    echo greater than 1.2
else
    echo less than 1.2
fi

実行結果

TIME1=1644589700.957228269
TIME2=1644589702.168540410
DIFF=1.211312141
greater than 1.2

ポイント1: 任意フォーマットのパース

基準時刻からの秒数への変換にはdateコマンドを使うのだが、どうやらdateコマンドは時刻の入力フォーマットを指定できないものが存在するようである。一方でこの例のようにナノ秒を%Nで出力できるものもあるが、繰り返すように入力フォーマットとして指定することは出来ない。仕方が無いのでbashの正規表現を使ってパースする。

bashの[[ ]]の中で=~を使った比較をすると右辺は正規表現として取り扱われる。マッチしたグループはBASH_REMATCH変数から参照できる。無駄にここだけKotlinやTwigのような今風である。

ただし\dを整数としては使えず[0-9]もしくは[:digit:]となることに注意が必要。単に文字数が短いほうを採用している。今風といったことは取消そう。BASH_REMATCHという名称は長すぎる。

ポイント2: bashに少数演算は出来ません

ただそれだけです。知っている方にとっては常識かと思われます。awkやらperlやらpythonやら外部プログラムが必要とのことでしたがdateも外部コマンドなので今更躊躇するところではないでしょう。時刻のパースでsedではなくbash組込みの正規表現を使っているあたり多少の未練があるようにも見えますけれども。

bcコマンドであれば比較的自然に済みます。<<<でヒアストリングが使えるのでechoしてパイプを使う必要はありません。echoは組み込みコマンドなので効率は同等かと思われますが文字数面でヒアストリングが勝っているように思えました。

以上!