2017/06/14


Windows で hosts ファイル(C:\Windows\System32\drivers\etc\hosts) を編集するには管理者権限が必要です。またコマンドラインから IP アドレスを変更するのにも管理者権限が必要です。管理者権限で hosts ファイルを編集するにはメモ帳を管理者権限で起動する必要があります。管理者権限でメモ帳を起動する為にはメニューから「メモ帳」を出し、右クリックして「管理者として実行」を選ぶ必要があります。そして実行したメモ帳のメニューから「開く」でファイルを選択します。管理者として開かないのであれば hosts ファイルを右クリックして「送る」等に登録したエディタを単に選べば済む話なのに、随分と手間ですね。

notepad

UNIX だと

$ sudo vi /etc/hosts

とだけタイプすれば良いのにこの手数の多さはちょっとゲンナリします。特にマウスに手を伸ばしたくないからコマンドプロンプトを使っている僕の様な変な人には辛さしかありません。runas というコマンドを使えば sudo の様な事は出来るのですが、これはパスワード入力を要求されます。メモ帳を管理者として起動する場合だと UAC (User Account Control) のダイアログが表示されるだけなのに、いちいちパスワードなんか打ちたくありません。世の中には C言語で実装した物、タスクスケジューラを使って sudo ぽい事する物、powershell を使って実現している物、いろいろありますがそれぞれ難があるし出来ればキビキビ動くのが欲しかったので作りました。

GitHub - mattn/sudo: sudo for windows

README.md sudo for windows Usage C:\>sudo cmd /c dir Then, you'll see the UAC dialog. Tutorials Disp...

https://github.com/mattn/sudo

標準入出力を扱えるので

sudo cmd /c type secret-file.txt > accessible-file.txt

リダイレクトしたり

echo 123 | sudo my-command.exe | more

パイプで繋げたり出来ます。コマンドプロンプト内のコマンド(例: typeecho)は cmd /c echo の様に起動する必要があります。普段は管理者権限が無いと実行出来ない netsh による IP アドレスの変更も簡単。

sudo netsh interface ip add address "ローカルネットワーク" 33.33.33.33 255.255.255.255

やりたかったメモ帳での hosts ファイル編集も

sudo notepad c:\windows\system32\drivers\etc\hosts
と簡単に出来る様になりました。これを実現する為に、ShellExecuteEx という API の Verb 指定に runas を付けて起動するという方法を取ったのですが、この API で起動したプロセスは、それを起動した同じコンソールを共有する事が出来なかったのでコマンドプロンプトから sudo vim を実行できる様にする事は諦めました。一応、sudo -spawn vim foo.txt で新しいコマンドプロンプトを起動するという機能を付けてありますので、どうしても vim じゃないと嫌だという方はそちらをお使い下さい。間違って起動してしまった場合も CTRL-C するとプロセスを終了する仕組みが入っています。NTサービスをコマンドプロンプトから操作する事も出来るのでずいぶんと楽になりました。
ntservice

Windows でコマンドプロンプトで生活してて sudo したいという、かなり絞られたユーザ層に届けばいいなと思います。


2017/06/09


Goでコマンドライン引数と環境変数の両方からflagを設定したい - Qiita

Goで実装したプログラムでオプションをコマンドライン引数から取るには標準の `flag` パッケージを使いますが、値を環境変数からも読みたいことがあります。(特に Docker で動かす場合) htt...

http://qiita.com/sfujiwara/items/f177d85e9c10f4c34fb6

実は結構簡単に出来ます。github.com/namsral/flag に依存したくない場合や github.com/namsral/flag が実は flag 互換で無かった、なんて問題が見付かった場合に使えるハックです。

オリジナルのコード

package main

import (
    "flag"
    "fmt"
)

func main() {
    var age int
    flag.IntVar(&age, "age"20"your age")
    flag.Parse()
    fmt.Println(age)
}

これに5行(importも入れると実際は7行)足します。

package main

import (
    "flag"
    "fmt"
    "os"
    "strings"
)

func main() {
    var age int
    flag.IntVar(&age, "age"20"your age")
    flag.VisitAll(func(f *flag.Flag) {
        if s := os.Getenv(strings.ToUpper(f.Name)); s != "" {
            f.Value.Set(s)
        }
    })
    flag.Parse()
    fmt.Println(age)
}

こうする事で

flagenv

簡単に環境変数から値を上書きできる様になります。


2017/06/03


幾らか言いたい事があったので。

Go言語感想文 - なるせにっき

序 最近、敵情視察を兼ねた仕事ととしてGoでアプリケーションを書いていた。このアプリケーションがどんなものかはそのうち id:tagomoris さんがどこかで話すと思うけれど、この コンポーネント ...

http://naruse.hateblo.jp/entry/2017/06/02/203441

GoroutineとChannel

Goroutineはようするにスレッドなんですが、文法と実装の支援でより気軽に使えるのが他の言語との違いでしょうか。なので、Goroutineをどれだけほいほい使うべきかというコスト感覚を身につけることがとても大事な気がします。Rubyなどとは気持ちを切り替えていく必要があるでしょう。ぼくはまだ切り替えきれていません。

Goroutine はスレッドではありません。Goroutine はコルーチンでありスレッドです。ランタイムが必要に応じてスレッドで実行するかコルーチンで実行するかをインテリジェントに切り替えます。ユーザが意識する必要はありません。無秩序に大量に作る様な事がないのであれば気軽に作成して良いはずです。

テストについて

アサーションをコピーしなければならない理由は一つのテストケースの中で異なるテストが混在しているか、単に同様のテストがコピーして作られているのが原因ではないでしょうか。

極端な例かもしれませんが、例えば以下の FizzBuzz 関数をテストするとします。

package fizzbuzz

import "fmt"

func FizzBuzz(n int) (stringerror) {
    if n < 1 || n > 100 {
        return "", fmt.Errorf("invalid number: %v", n)
    }
    switch {
    case n%15 == 0:
        return "FizzBuzz"nil
    case n%3 == 0:
        return "Fizz"nil
    case n%5 == 0:
        return "Buzz"nil
    default:
        return fmt.Sprint(n), nil
    }
}

1未満や100を超える値の場合はエラーとなり、それ以外は通常通り FizzBuzz の結果を返します。これをのっぺりとテストすると

func TestFizzBuzz(t *testing.T) {
    var input int

    got, err := FizzBuzz(-1)
    if err == nil {
        t.Fatalf("should be error for %v but not:"-1)
    }
    got, err := FizzBuzz(1)
    if err != nil {
        t.Fatalf("should not be error for %v but: %v"1, err)
    }
    if got != "1" {
        t.Fatalf("want %q, but %q:""1", got)
    }
    got, err := FizzBuzz(3)
    if err != nil {
        t.Fatalf("should not be error for %v but: %v"1, err)
    }
    if got != "Fizz" {
        t.Fatalf("want %q, but %q:""Fizz", got)
    }
}

この様に Fatalf のコピーになりかねません。まだ Buzz や FizzBuzz のテストも出来ていませんから、これからコピペが大量に作られる訳です。確かに Ruby の DSL は強力で、この様な退屈なテストを短い構文で記述する事が出来ます。しかし例外のない Go においては if 文が頻発し得ます。そこで Go ではテーブルドリブンテスト(Table Driven Tests)が推奨されています。

TableDrivenTests · golang/go Wiki · GitHub

Home Articles Blogs Books BoundingResourceUse cgo ChromeOS CodeReview CodeReviewComments CodeTools C...

https://github.com/golang/go/wiki/TableDrivenTests
以上のテストを Table Driven Tests に置き換えると以下の様になります。
func TestFizzBuzz(t *testing.T) {
    tests := []struct {
        input int
        want  string
        err   bool
    }{
        {input: -100, want: "", err: true},
        {input: -1, want: "", err: true},
        {input: 0, want: "", err: true},
        {input: 1, want: "1", err: false},
        {input: 2, want: "2", err: false},
        {input: 3, want: "Fizz", err: false},
        {input: 4, want: "4", err: false},
        {input: 5, want: "Buzz", err: false},
        {input: 6, want: "Fizz", err: false},
        {input: 14, want: "14", err: false},
        {input: 15, want: "FizzBuzz", err: false},
        {input: 16, want: "16", err: false},
        {input: 100, want: "Buzz", err: false},
        {input: 101, want: "", err: true},
    }

    for _, test := range tests {
        got, err := FizzBuzz(test.input)
        if !test.err && err != nil {
            t.Fatalf("should not be error for %v but: %v", test.input, err)
        }
        if test.err && err == nil {
            t.Fatalf("should be error for %v but not:", test.input)
        }
        if got != test.want {
            t.Fatalf("want %q, but %q:", test.want, got)
        }
    }
}

このテストでは今後テストケースを増やしても if が増える事はありません。つまり t.Fatal も増えません。一つのテストケースの中でテストされる結果はおおよそ同様の物となるはずです。そうでないならばそれはテストがユニットテストになっていないのだと思います。

その他

switchべんり ← おまえほんとうにそれでいいのか ** selectやswitchの中でbreakすると外側のforまで届かないのでbreak すればよいけど、結局goto使う

Golang に限らずですが、最近の言語では Labeled Break という物があります。

exit_loop:
    for {
        s := foo()
        switch s {
        case "exit":
            break exit_loop
        }
    }
Goはnull安全ではない←構造体のポインタを扱い始めると気になってくる

重箱ぽくなりますが、構造体フィールドに直接アクセスしなければレシーバが nil かどうかで判定出来ます。

package main

import "fmt"

type Foo struct {
    v int
}

func (f *FoodoSomething() string {
    if f == nil {
        return "ぬるぽ"
    }
    return "のっとぬるぽ"
}

func main() {
    var f *Foo

    fmt.Println(f.doSomething()) // ぬるぽ

    f = new(Foo)
    fmt.Println(f.doSomething()) // のっとぬるぽ
}

まぁ、f が nil である事も条件を切り分ける為の一つの状態なので、これはあまり使わない手法ではあります。Go が null 安全だとは言ってないです。