Golang 1.10 + Python 3 の Dockerfile を作ったときのメモ。
Golang 1.10 + Python 3 の Dockerfile を作ったときのメモ。
Golang は 1.10, Python は 3.7 (以降) のバージョン縛りがあって、Golang をベースに Python 3 を入れようとしたらいろいろ面倒くさそうだったので、Python 3 をベースに Golang を入れることにした。
FROM python:3 RUN set -ex && \ cd /tmp && \ curl -O https://dl.google.com/go/go1.10.4.linux-amd64.tar.gz && \ tar -C /usr/local -xzf go1.10.4.linux-amd64.tar.gz && \ rm /tmp/go1.10.4.linux-amd64.tar.gz ENV PATH $PATH:/usr/local/go/bin
docker build して動作確認。
root@4c80d3229411:/# go version
go version go1.10.4 linux/amd64
root@4c80d3229411:/# python --version
Python 3.7.0
よさそう。
pt コマンドでよく使うオプションのメモ。
pt コマンドでよく使うオプションのメモ。
したいこと
- 単語単位の検索
- 大小文字を無視
- 大小文字を無視 (スマートケース)
- 正規表現での検索
- 特定のファイルを検索
- 特定のファイルを除外して検索
単語単位の検索
単語単位で検索したいときは --word-regexp あるいは -w オプションを使う。
$ echo 'abcde abc' | pt abc abcde abc $ echo 'abcde abc' | pt --word-regexp abc abc
大小文字を無視
大小文字を無視したいときは --ignore-case あるいは -i オプションを使う。
$ echo 'abc Abc' | pt abc abc $ echo 'abc Abc' | pt --ignore-case abc abc Abc $ echo 'abc Abc' | pt --ignore-case ABC abc Abc
大小文字を無視 (スマートケース)
スマートケースで検索したいときは --smart-case あるいは -S オプションを使う。
$ echo 'abc Abc' | pt --smart-case abc abc Abc echo 'abc Abc' | pt --smart-case Abc Abc $ echo 'abc Abc' | pt --smart-case ABC
特定のファイルを検索
特定のファイルだけを検索したいときは --file-search-regexp あるいは -G オプションを使う。
$ cat a.txt abc $ cat b.txt abc $ cat c.txt abc $ pt abc ./c.txt 1:abc ./a.txt 1:abc ./b.txt 1:abc $ pt --file-search-regexp '[ab].txt' abc ./b.txt 1:abc ./a.txt 1:abc
特定のファイルを除外して検索
反対に特定のファイルを除外して検索したいときは --ignore オプションを使う。
# ファイルは上と同じ $ pt --ignore '[ab].txt' abc ./c.txt 1:abc
dt に計算結果の日付が無効になったときの調整オプションを追加したメモ。
3/31の1ヶ月後が4/30になったり5/1になったりする 動きをオプションで指定できるようにしたメモ。
dt は5/1になる動きだったのを、4/30になるようにオプションで指定できるようにした。
$ dt "2018/01/31" +1M 2018/03/03 $ dt --adjust-day "2018/01/31" +1M 2018/02/28
こんな感じ。
ただ、複数月を1回で足すときと複数回に分けて足すときとでは最終的な結果が変わることには注意が必要かもしれない。
$ dt --adjust-day "2018/01/31" +2M 2018/03/31 $ dt --adjust-day "2018/01/31" +1M +1M 2018/03/28
3/31の1ヶ月後が4/30になったり5/1になったりするメモ。
日付計算で月を加算 (減算) して、加算結果の日付が無効になったときの動きのメモ。
書くこと
- 加算結果が無効な日付になる例
- 各言語での例
- 仕様
- まとめ
加算結果が無効な日付になる例
日付計算で月を加算 (減算) したとき、加算結果の日付が無効になることがある *1
たとえば、3/31に1ヶ月を足すと4/31という無効な日付になるが、プログラミング言語により無効な日付の扱いが変わってくる。
いくつかの言語での例を書いてみる。
各言語での例
# Java $ jshell jshell> import java.time.LocalDate; jshell> LocalDate.of(2018, 3, 31).plusMonths(1); $2 ==> 2018-04-30 # Ruby $ irb irb(main):001:0> require "date" => true irb(main):002:0> Date.new(2018, 3, 31).next_month => #<Date: 2018-04-30 ((2458239j,0s,0n),+0s,2299161j)> # Golang $ gore -autoimport gore version 0.3.0 :help for help gore> time.Date(2018, 3, 31, 0, 0, 0, 0, time.Local).AddDate(0, 1, 0) 2018-05-01 00:00:00 Local
仕様
各言語の仕様を見てみる。
Java
This method adds the specified amount to the months field in three steps:
1. Add the input months to the month-of-year field
2. Check if the resulting date would be invalid
3. Adjust the day-of-month to the last valid day if necessary
For example, 2007-03-31 plus one month would result in the invalid date 2007-04-31. Instead of returning an invalid result, the last valid day of the month, 2007-04-30, is selected instead.
加算結果の日付が無効になるときは、月の日を有効な最後の日に調整するようだ。
Go
AddDate normalizes its result in the same way that Date does, so, for example, adding one month to October 31 yields December 1, the normalized form for November 31.
AddDateは、Dateと同じ方法で結果を正規化します。たとえば、10月31日に1か月を追加すると、11月31日の正規化された形式である12月1日が得られます。
まとめ
個人的には、Go のように3/31の1ヶ月後が5/1になるというのは変な感じがするが、4/31という無効な日付は4/30の1日後であるから、4/30の有効な1日後 = 5/1が返されるというのは納得できる。
むしろ、言語やライブラリの場合は仕様にはっきりと書かれているので使う側が気をつければいいが、お客さんや同僚と話しているときにこのあたりの "自然な加算結果" が違っていると、少し苦労しそう。
*1:うるう年が絡むと年の加算などでも同じことが起こる
Go での開発環境に Makefile を導入したメモ。
雑に始めた Go での開発環境のメモ。 - 全力で怠けたい で↓みたいなことを書いたのですが、
いくつかコマンドを Github で公開しているのですが、Github の Release に登録するためのバイナリの作成やアップロードが面倒です。
そこで、コンパイルには gox を、Github にアップロードするために ghr を利用しています。
実際のところは、↓こんな感じに雑なシェルスクリプトを書いて、パッケージディレクトリで叩いています。
やっぱり面倒だったので Makefile を導入しました。
今まで JVM 系の開発が多かったので Makefile を使ったことはないのですが、Go での開発ではよく使われているみたいですね。
https://github.com/ebc-2in2crc/dt/blob/master/Makefile
GOCMD=go GOBUILD=$(GOCMD) build GOCLEAN=$(GOCMD) clean GOTEST=$(GOCMD) test GOGET=$(GOCMD) get NAME := dt CURRENT := $(shell pwd) BUILDDIR=./build BINDIR=$(BUILDDIR)/bin PKGDIR=$(BUILDDIR)/pkg DISTDIR=$(BUILDDIR)/dist VERSION := $(shell git describe --tags --abbrev=0) LDFLAGS := -X 'main.version=$(VERSION)' GOXOS := "darwin windows linux" GOXARCH := "386 amd64" GOXOUTPUT := "$(PKGDIR)/$(NAME)_{{.OS}}_{{.Arch}}/{{.Dir}}" .PHONY: deps ## Install dependencies deps: $(GOGET) golang.org/x/tools/cmd/goimports $(GOGET) github.com/golang/lint/golint $(GOGET) github.com/codegangsta/cli $(GOGET) github.com/mitchellh/go-homedir $(GOGET) github.com/Songmu/make2help/cmd/make2help $(GOGET) github.com/tcnksm/ghr .PHONY: build ## Build binaries build: deps go build -ldflags "$(LDFLAGS)" -o $(BINDIR)/$(NAME) .PHONY: cross-build ## Cross build binaries cross-build: rm -rf $(PKGDIR) gox -os=$(GOXOS) -arch=$(GOXARCH) -output=$(GOXOUTPUT) .PHONY: package ## Make package package: cross-build rm -rf $(DISTDIR) mkdir $(DISTDIR) pushd $(PKGDIR) > /dev/null && \ for P in `ls | xargs basename`; do zip -r $(CURRENT)/$(DISTDIR)/$$P.zip $$P; done && \ popd > /dev/null .PHONY: release ## Release package to Github release: package ghr $(VERSION) $(DISTDIR) .PHONY: test ## Run tests test: deps $(GOTEST) -v ./... .PHONY: lint ## Lint lint: deps go vet ./... golint ./... .PHONY: fmt ## Format source codes fmt: deps find . -name "*.go" -not -path "./vendor/*" | xargs goimports -w .PHONY: clean clean: $(GOCLEAN) rm -rf $(BUILDDIR) .PHONY: help ## Show help help: @make2help $(MAKEFILE_LIST)
ハマったこと
"Each line in a makefile target recipe is run in its own shell session."
— えび🦐🍤 (@ebc_2in2crc) July 28, 2018
-> makefile のターゲットの各行は、それぞれのシェルセッションで実行されます。
完全にハマってた……https://t.co/UCDGUBdwLj
雑に始めた Go での開発環境のメモ。
少し前からちょっとしたことをするときに Go を使い始めたので、その開発環境のメモ。
書くこと
- エディター
- ビルド
- 公開
- 公開しているコマンドたち
- まとめ
ビルドと公開
ビルド
Goland 上で Run するか シェルから go run / go build するのがほとんどです。
コード量やファイルも少ないので、今のところはこれで事足りています。
公開
いくつかコマンドを Github で公開しているのですが、Github の Release に登録するためのバイナリの作成やアップロードが面倒です。
そこで、コンパイルには gox を、Github にアップロードするために ghr を利用しています。
実際のところは、↓こんな感じに雑なシェルスクリプトを書いて、パッケージディレクトリで叩いています。
#!/bin/sh CURRENT=`pwd` PGNAME=`basename ${CURRENT}` BUILD_DIR=build PKG_DIR=${BUILD_DIR}/pkg DIST_DIR=${BUILD_DIR}/dist if [ -z "$1" ]; then echo 'compile.sh <tag>' exit 1 fi TAG=$1 PACKAGE=. if [ -d ${PKG_DIR} ]; then rm -r ${PKG_DIR} fi mkdir -p ${PKG_DIR} gox \ -os="darwin windows linux" \ -arch="386 amd64" \ -output "${PKG_DIR}/${PGNAME}_{{.OS}}_{{.Arch}}/{{.Dir}}" \ $PACKAGE if [ -d ${DIST_DIR} ]; then rm -r ${DIST_DIR} fi mkdir ${DIST_DIR} pushd ${PKG_DIR} > /dev/null for P in `ls | xargs basename` do zip -r ${CURRENT}/${DIST_DIR}/$P.zip $P done popd > /dev/null ghr ${TAG} ${DIST_DIR}
まとめ
こんな感じの開発環境でやっていますが、gore や ghq や peco などといったとても便利なツールがあるみたいなので、これからもどんどん変わりそうです。
日付を計算したり、日付の書式を変換する dt コマンドを作りました。
日付を計算したり、日付の書式を変換するコマンドを作りました。
書くこと
- なにができる?
- 使い方
- インストール
- 動機
- まとめ
なにができる?
- 日付の加算と減算
- 日付の書式を変換
これだけをしてくれるコマンドです。
使い方
システム時刻の1年3ヶ月20秒後を求める
$ date "+%Y/%m/%d %H:%M:%S" 2018/05/12 17:30:00 $ dt now +1Y +3M +20s 2019/08/12 17:30:20
計算元の日付を指定
$ dt "2018/05/12 17:30:00" +1Y +3M +20s 2019/08/12 17:30:20
日付の加算
$ dt "2018/05/12 17:30:00" +1Y 2019/05/12 17:30:00 $ dt "2018/05/12 17:30:00" +1M 2018/06/12 17:30:00 $ dt "2018/05/12 17:30:00" +1D 2018/05/13 17:30:00 $ dt "2018/05/12 17:30:00" +1h 2018/05/12 18:30:00 $ dt "2018/05/12 17:30:00" +1m 2018/05/12 17:31:00 $ dt "2018/05/12 17:30:00" +1s 2018/05/12 17:30:01 # '+' は省略可能 $ dt "2018/05/12 17:30:00" 1Y 3M 20s 2019/08/12 17:30:20
日付の減算
$ dt "2018/05/12 17:30:00" -1Y -3M -20s 2017/02/12 17:29:40
入力フォーマット
# コマンド標準 $ dt "2018/05/12 17:30:00" +1Y +3M +20s 2019/08/12 17:30:20 # RFC822 $ dt -o def "12 May 18 17:30 MST" +1Y +3M +20s 2019/08/12 17:30:20 $ dt -o def 1526113800 +1Y +3M +20s 2019/08/12 17:30:20# unix 秒
自動判断されるフォーマット
入力フォーマットを指定
# unix ミリ秒 $ dt -i unixm -o def 1526113800000 +1Y +3M +20s 2019/08/12 17:30:20 # カスタムフォーマット $ dt -i "02-Jan-06 15:04:05" -o def "12-May-18 17:30:00" +1Y +3M +20s 2019/08/12 17:30:20
出力フォーマット
デフォルトは入力フォーマットと同じ。
$ dt "2018-05-12 17:30:00" +1Y +3M +20s 2019-08-12 17:30:20 $ dt 1526113800 +1Y +3M +20s 15300622820
出力フォーマットを指定
$ dt -o def 1526113800 +1Y +3M +20s 2019/08/12 17:30:20 $ dt -o "02-Jan-06 15:04:05" 1526113800 +1Y +3M +20s 12-Aug-19 17:30:20
ヘルプ
$ dt --help # ...
インストール
Homebrew
$ brew tap ebc-2in2crc/dt $ brew install dt
Developer
$ go get -u github.com/ebc-2in2crc/dt/...
手動
https://github.com/ebc-2in2crc/dt/releases からダウンロードした zip ファイルを展開した中にあるファイルを、パスの通ったディレクトリに入れる。
動機
DB に格納されている日付が unix 秒だったり、それに対する加減算をしたり、入力と出力で日付のフォーマットが異なっていたりするのが面倒くさくて作りました。date コマンドでも同じことはできなくもありませんが……
実装言語が Go なのは、以前チュートリアルをやったきり触っておらず文法そのほかすべてをきれいに忘れてしまったので、再入門するつもりで選びました。
まとめ
バグとかあると思いますし、「おそいよ!」とか「こーしたら使いやすくなる」とかあったら、イシュー作ったり声かけてもらえると嬉しいです。
ということで dt コマンド の紹介でした。