~/.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

*1:安直に、どのOSにもデフォルトで入っているBashを使っています。本物のプログラマならバギーで文法が汚く補完が中途半端なbashではなく、堅牢で高速なdashですよね?分かっています

Goplay: Goで手軽に書捨て環境を作るツール

goreの説明を追記。

f:id:doloopwhile:20040712133434j:plain

LLから、Goに移るときの不満の一つに「標準のREPL(インタラクティブシェル)が無い」があります。

「ポインタはmapのキーになるんだっけ?」のような、つまらない(けど、ありがちな)疑問を解消するには、ドキュメントを漁るより、コードを書いてしまう方が、手っ取り早いですよね?

goplayを作った!

goplayは、書捨てGoコードを書くためのシンプルなツールです。

https://camo.githubusercontent.com/db725471459165d7d24b6e6f234b02267b187746/68747470733a2f2f7261772e6769746875622e636f6d2f646f6c6f6f707768696c652f676f706c61792f6d61737465722f64656d6f2e676966

やっているのは、 1. 一時ディレクトリを/tmp/goplay/以下に作る 2. main.goを一時ディレクトリに作る 3. 一時ディレクトリで新しいシェルを起動 4. 環境変数GOPATHに一時ディレクトリを追加。 これだけです。

インストールはgo getするだけです。

go get github.com/doloopwhile/goplay

他の方法との比較

ちゃんとしたREPL(gore)を使う

いや、正直、普通はgoreのほうがいいと思います。良い子はgoreを使いましょう。

エディタで書捨てコードを書きたい子はgoplayを使いましょう。

github.com

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をつなぐ的なアプリを書いてみた

プログラマーは往々にしてCUI世界の住人なのですが、

ごくたまに、ファイルをドラッグ&ドロップで指定できると便利、ということがあります。

doloopwhile/dap · GitHub

スクリーンショット

f:id:doloopwhile:20141005180042p:plain

インストール

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-setSet型は、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に渡す

今のコードのtitletextを直接渡す方法では、 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.Sortsort.InterfaceというStrategyを取るようになっています。