~/.bashrcは何も出力してはいけない(するならエラー出力に)
知ってて当たり前の超凡ミス。
起こったこと:rsyncが失敗する
rsyncが何故か失敗してしまう。"protocol incompatibility"と出ていますが、--protocol
オプションを指定しても、最新版のrsyncをインストールしても、同じエラーが出ます。なぜでしょう?
Tue Nov 4 12:15:46 2014 Normal: recursive startup rsync: /home/xxxxxxx/Documents/src/xxxxxxx/ -> vagrant@deva:xxxxxxx/ excluding /cucumber/vendor /cucumber/.bundle ~/.bashrc loaded protocol version mismatch -- is your shell clean? (see the rsync man page for an explanation) rsync error: protocol incompatibility (code 2) at compat.c(174) [sender=3.1.0] Tue Nov 4 12:15:47 2014 Error: Failure on startup of "/xxxxxxxxx".
原因:~/.bashrcでechoしている
上のログに "~/.bashrc loaded" という行があります。これが原因です。.bashrc*1の最後の行で"echo"していました。もちろん、echo以外でも、何かしらの出力をしてしまうとscpやrsyncはコケます。
# ~/.bashrcの最後の行 echo "~/.bashrc loaded"
これと同じ問題です。
bash - SCP doesn't work when echo in .bashrc? - Stack Overflow
How the SCP protocol works (Jan Pechanec's weblog)によると、 scpは初めにリモート側にプロトコルを問い合わせるのですが、~/.bashrcが何かを出力するとそれが混乱してしまうようです。多分rsyncも同様なのでしょう。
回避策:Greetingは標準エラー出力に出す
echo "~/.bashrc loaded" >&2
Goplay: Goで手軽に書捨て環境を作るツール
goreの説明を追記。
LLから、Goに移るときの不満の一つに「標準のREPL(インタラクティブシェル)が無い」があります。
「ポインタはmapのキーになるんだっけ?」のような、つまらない(けど、ありがちな)疑問を解消するには、ドキュメントを漁るより、コードを書いてしまう方が、手っ取り早いですよね?
goplayを作った!
goplayは、書捨てGoコードを書くためのシンプルなツールです。
やっているのは、
1. 一時ディレクトリを/tmp/goplay/
以下に作る
2. main.go
を一時ディレクトリに作る
3. 一時ディレクトリで新しいシェルを起動
4. 環境変数GOPATH
に一時ディレクトリを追加。
これだけです。
インストールはgo get
するだけです。
go get github.com/doloopwhile/goplay
他の方法との比較
ちゃんとしたREPL(gore)を使う
いや、正直、普通はgoreのほうがいいと思います。良い子はgoreを使いましょう。
エディタで書捨てコードを書きたい子はgoplayを使いましょう。
Go Playground
Go Playgroundはブラウザ上でGoを書いて実行できます。 しかし、
- 外部ライブラリをimportできない
- 好きなエディタが使えない という欠点があります。
goplayは、ローカルで実行するので、外部ライブラリもエディタも自由に使えます。
その場でvi a.go
する
まぁ、それでも動くは動くんですが、
package main
の*.go
ファイルが同一ディレクトリに複数あると、
エディタのシンタックスチェッカーが、
「main redeclared in this block」
を出してくるのが五月蝿い。
そして、作ったスクリプトを削除するのも面倒くさい。
Let's go!
Go-GTKでCUIとGUIをつなぐ的なアプリを書いてみた
ごくたまに、ファイルをドラッグ&ドロップで指定できると便利、ということがあります。
スクリーンショット
インストール
go get github.com/doloopwhile/dap
使い方
ドラッグアンドドロップでファイルをバックアップ・ディレクトリにコピーする
mkdir -p $HOME/backup dap | xargs cp {} $HOME/backup
なぜ作ったか
Debian系OSには(dragbox)https://packages.debian.org/ja/sid/dragboxというツールがあるようです。しかし、どういうわけか私のXubuntuでは動きませんでした。
そこで、(Go-GTKのD&Dの例)https://github.com/mattn/go-gtk/blob/master/example/dnd/dnd.goを元に作ってみました。Go-GTKは、デモが充実しており、実用的に使えそうな段階に入っているようです。
Goライブラリを作ったら、とりあえずgodocdown
小さなGoライブラリをつくった時、ほとんど自分しか使わないような規模なら、わざわざREADMEに「Usage」「Example」「Functions」なんて書くのは面倒ですよね。
そんな時は、godocdownで自動生成してしまうのがオススメです。 godocと同等の内容をGitHub用Markdownで生成してくれます。
例:doloopwhile/go-merror · GitHub
go get github.com/robertkrimen/godocdown/godocdown godocdown > README.md
まぁ、わざわざgodocdownでREADMEを作らなくても、 ほっておいてもgodocは、自動で生成されるのですが、READMEを空っぽにしておくのも寂しいので。
地味に待ち遠しいDockerの新機能
DockerfileのENV
コマンドで、複数の環境変数を一度に指定できるようになるらしい。
Allow ENV to set multiple variables in one layer · Issue #2333 · docker/docker · GitHub
従来は複数の環境変数を設定するには、ENVを繰り返さなければなりませんでした。
ENV GOPATH /root/go ENV CGO_CFLAGS-I/opt/fzero/include ENV CGO_LDFLAGS -L/opt/fzero/lib64 -Wl,-rpath=/opt/fzero/lib64
この場合、各ENV
ごとにレイヤーが生成されてしまい、docker push
の際に通信回数が増えるなど無駄でした。
それが1行で書ける=1レイヤーで済むようになるらしいです。 イメージ:
ENV GOPATH=/root/go CGO_CFLAGS=-I/opt/fzero/include CGO_LDFLAGS="-L/opt/fzero/lib64 -Wl,-rpath=/opt/fzero/lib64"
Goでも独自型をrangeしたい!chanを使う
Pythonでは__iter__
メソッドを定義すれば((Rubyでは.each
メソッドを定義してEnumerable
をincludeすれば、C#ではGetEnumerator()
メソッドを定義すれば、
PHPではIterator
を実装すれば))、
晴れて独自のコレクション型でも、foreach文などでループできるようになるわけですが、でもGoにはそんな仕組みはありません。
まあ、妥協案としては、関数を引数にとる.Each
を作ることはできます。
sum := 0 myCollection.Each(func(x int) { sum += x })
「でも、ぼくがつくったさいきょうの○○型をfor - range
文で使いたいんだ!」
解法:channelを返す.Iter()
知ってる人は知っている、range
にはchannelが渡せます。
A "for" statement with a "range" clause iterates through all entries of an array, slice, string or map, or values received on a channel.
he Go Programming Language Specification - The Go Programming Language
これを利用して、deckarep/golang-setのSet
型は、for
文で使えるように.Iter()
メソッドを定義してます。
package main import ( "fmt" mapset "github.com/deckarep/golang-set" ) func main() { s := mapset.NewSet() s.Add(7) s.Add(11) s.Add(13) s.Add(11) // Setだから、重複は排除される c := s.Iter() fmt.Printf("%V\n", c) // %!V(<-chan interface {}=0xXXXXXXXXXX) for x := range s.Iter() { // range文で使える! fmt.Println(x) } // 7 // 11 // 13 }
やりましたね!
genericsが無いGolangで独自コレクションを作る機会は少ないでしょうけど、
channelがrangeできること自体は、知っていると得かもしれませんよ。
Goによるデザインパターン - Strategy パターン (2)
Strategy設計の失敗
前回で、HTMLとテキストでレポートを出力するコードを書きました。その中でFormatter
interfaceを定義し、その具象型としてPlainTextFormatter
HTMLFormatter
を定義するというStrategyパターンを採用しました。
しかし、気になる点が無いではありません。
package main import ( "fmt" ) type Formatter interface { OutputStart() OutputHead(text string) OutputBodyStart() OutputLine(line string) OutputBodyEnd() OutputEnd() } type Report struct { Title string Text []string Formatter Formatter } func (r *Report) Output() { r.Formatter.OutputStart() r.Formatter.OutputHead(r.Title) r.Formatter.OutputBodyStart() for _, line := range r.Text { r.Formatter.OutputLine(line) } r.Formatter.OutputBodyEnd() r.Formatter.OutputEnd() } type PlainTextFormatter struct{} // (メソッドの実装は中略) type HTMLFormatter struct{} // (メソッドの実装は中略)
Formatter
interface には6つのメソッドが含まれます。しかし、この切り分け方は適切だったのでしょうか? 6つというのは多すぎる気がします。また、今後新しいフォーマットを採用する際に、不備が発覚するかもしれません(フッタ―にもタイトルを出力したくなるかも!)。
Strategyパターンを使う際は、ストラテジの範囲と渡すべきデータを見極める必要があります。
ContextをStrategyに渡す
今のコードのtitle
とtext
を直接渡す方法では、
1. Strategyのどのメソッドがどのデータを必要とするか、覚えなくてはならない
2. 必要ないと思ったデータも実は必要になるかもしれない(フッタ―にもタイトルを出力)
3. 全く新しいデータが登場するかもしれない(日付や提出者も記載したい)
という問題があります。
そんな時は、Context(呼び出し側)をStrategyの引数として渡します。
Contextをそのまま渡すこともできますが(下のコードで言えば*report
)、Contextもinterface
にして渡した方がテストなどで便利になるでしょう(Report
)。
// template_method.4.go package main import ( "fmt" "time" ) type Formatter interface { OutputStart(r Report) OutputHead(r Report) OutputBodyStart(r Report) OutputLine(r Report, line string) OutputBodyEnd(r Report) OutputEnd(r Report) } type Report interface { Title() string Text() []string Date() time.Time } type report struct { title string text []string date time.Time formatter Formatter } func (r *report) Title() string { return r.title } func (r *report) Text() []string { return r.text } func (r *report) Date() time.Time { return r.date } func (r *report) Output() { r.formatter.OutputStart(r) r.formatter.OutputHead(r) r.formatter.OutputBodyStart(r) for _, line := range r.text { r.formatter.OutputLine(r, line) } r.formatter.OutputBodyEnd(r) r.formatter.OutputEnd(r) } type HTMLFormatter struct{} func (*HTMLFormatter) OutputStart(r Report) { fmt.Println("<html>") } func (*HTMLFormatter) OutputHead(r Report) { fmt.Println("<head>") fmt.Printf("<title>%s</title>\n", r.Title()) fmt.Println("</head>") } func (*HTMLFormatter) OutputBodyStart(Report) { fmt.Println("<body>") } func (*HTMLFormatter) OutputLine(_ Report, line string) { fmt.Printf("<p>%s</p>\n", line) } func (*HTMLFormatter) OutputBodyEnd(r Report) { fmt.Printf("<p>Updated: %s</p>", r.Date().Format("2006-01-02 15:04:05")) fmt.Println("</body>") } func (*HTMLFormatter) OutputEnd(Report) { fmt.Println("</html>") } func main() { report := &report{ title: "月次報告", text: []string{"順調", "最高"}, formatter: &HTMLFormatter{}, } report.Output() }
Strategyの実例
標準ライブラリのsortパッケージは、Strategyパターンの一例です。
sort.Sort
はsort.Interface
というStrategyを取るようになっています。