LoginSignup
14
17

More than 5 years have passed since last update.

【シェル芸人への道】dateコマンドと親しむ

Last updated at Posted at 2016-02-02

dateコマンド

何気なく打っている date コマンド。
こいつで少し遊んでみた。

環境

[root@sandbox ~]# cat /etc/centos-release
CentOS release 6.7 (Final)

[root@sandbox ~]# bash --version
GNU bash, version 4.1.2(1)-release (x86_64-redhat-linux-gnu)

基本

[root@controller ~]# date
Tue Feb  2 16:41:59 GMT 2016

date コマンド。現在の日付が出ます。
明日や昨日も出せます。

[root@sandbox ~]# date --date="1 days"
Wed Feb  3 17:31:35 GMT 2016

[root@sandbox ~]# date --date="1 days ago"
Mon Feb  1 17:31:40 GMT 2016

え、英語が読めない? うるせぇ、黙ってろ。
date コマンドのデフォルト出力は環境変数で指定されているロケールに従います。
ロケールの確認には locale コマンド。

[root@sandbox ~]# locale
LANG=en_GB.UTF-8
LC_CTYPE="en_GB.UTF-8"
LC_NUMERIC="en_GB.UTF-8"
LC_TIME="en_GB.UTF-8"
LC_COLLATE="en_GB.UTF-8"
LC_MONETARY="en_GB.UTF-8"
LC_MESSAGES="en_GB.UTF-8"
LC_PAPER="en_GB.UTF-8"
LC_NAME="en_GB.UTF-8"
LC_ADDRESS="en_GB.UTF-8"
LC_TELEPHONE="en_GB.UTF-8"
LC_MEASUREMENT="en_GB.UTF-8"
LC_IDENTIFICATION="en_GB.UTF-8"
LC_ALL=

いろいろありますが、一旦 date コマンドということであれば LANG=en_GB.UTF-8 に注目です。
こいつを変えてもう一度 date コマンドを打ってみましょう。

[root@sandbox ~]# LANG="ja_JP.UTF-8"
[root@sandbox ~]# date
2016年  2月  2日 火曜日 16:50:39 GMT

え、次はフォーマットが気に入らない?

[root@sandbox ~]# date +"%Y/%m/%d %H:%M:%S"
2016/02/02 16:55:07

え、0埋めが邪魔?

[root@sandbox ~]# date +"%Y/%-m/%-d %H:%M:%S"
2016/2/2 16:56:48

よし。

小ネタ

月末日かどうか判定する

月末処理を書く都合で、月末日かどうか判定する必要がありました。

[root@sandbox ~]# date --date="1 days" +"%-d"
3

出力が1なら月末日です。
明日が1日なら今日は月末日。

先月末の日を取得する

[root@sandbox ~]# date +"%Y/%m/%d"
2016/02/02

[root@sandbox ~]# date --date="$(date +"%d") days ago" +"%Y/%m/%d"
2016/01/31

2月2日から2日戻ったら先月末。
月末処理をするバッチが遅延起動するケースを考えてこのコマンドを考えた記憶があります。

直近1年の素数日を取得する

[root@sandbox ~]# for i in {1..365} ; do date +"%Y%m%d" -d "${i} day" | factor | awk '{ if(NF==2) print $2 }' ; done | sort -n | sed -e "s/^\(....\)\(..\)\(..\)$/\1年\2月\3日/g"
2017年01月21日
2017年02月19日
2017年02月23日
...

何を言っているのかわからないかもしれませんが、ムショーに調べたくなったので書いてみました。
プログラミング言語で素数判定する場合、Mathクラスなんかで num.isPrime とかやると true/false 判定できたりしますが、シェル(芸界)では

[root@sandbox ~]# echo 4 | factor | awk '{ if(NF==2) print $2 }'
[root@sandbox ~]# echo 3 | factor | awk '{ if(NF==2) print $2 }'
3

などとすることで判定できたりします。素敵ですね。

なお、年号を含まない場合はこちらで。

[root@sandbox ~]# for i in {1..365} ; do date +"%m%d" -d "${i} day" | factor | awk '{ if(NF==2) print $2 }'; done | sort -n | sed -e "s/^\([0-9]\{3\}\)$/0\1/g" -e "s/^\(..\)\(..\)$/\1月\2日/g"
01月01日
01月03日
01月07日
...

うるう年の2月29日、229は素数なのですが直近1年に含まれていないと出てきません。残念ですね。

直近5分以内にあるerrorログを表示

/var/log/messages 内に error という文字列を含むログが直近5分以内にあるかどうかを知りたいです。
ワンライナー で。
ちなみに /var/log/messages の日付フォーマットは Feb 2 16:51:42 のような感じです。

[root@sandbox ~]# IFSBK=${IFS} ; IFS=$'\n' ; for record in $(cat /var/log/messages ) ; do if [ $(( $(date +"%s") - 300 )) -lt $(echo ${record} | cut -d" " -f 1,2,3 | date --date="$(cat -)" +"%s") ] ; then echo ${record
} ; fi ; done | grep error ; IFS=${IFSBK}

大体こんな流れで動きます。

  1. for文がスペース等に惑わされないようデリミタ(IFS)を改行(\n)に設定
  2. 対象ファイルのレコードをfor文でまわす
  3. 現在時刻をUNIX時間(秒)で出力し、300秒(5分)前を計算
  4. 各レコードの第3カラムまでを抜き出し、dateコマンドでUNIX時間(秒)に変換
  5. 現在時刻 - 300秒のほうが小さければ、レコード全体を出力する
  6. 最後に全体をerrorでgrepする
  7. デリミタを元に戻す

ログから抜き出した日付を date コマンドで整形して比較可能なUNIX時間に変換するのが大事なんですが、このときの --date オプションの有能さに救われました。

このコマンドをやりたいがためにいろいろ調べた結果をまとめたのがこの記事なのでした。おわり。

14
17
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
14
17