Golang Cafe #17 まとめ osサブパッケージ
2014/02/16に開催された「Golang Cafe #17」についてのまとめです。
今回は、os.execパッケージ、os.signalパッケージ、os.userパッケージをまとめておきます。
+TakashiYokoyama氏が準備してくれたサンプルはexecsample、signalsample、usersample、になります。
私のサンプルは、exec、signal、userになります。
os.execパッケージ
os.execパッケージはプログラム中から外部プロセスを起動したり、起動したプロセスの終了を待機したり、起動したプロセスからの出力を取得するためのパッケージです。
+TakashiYokoyama氏のサンプルをWindowsコマンド用に変更してみました。
import ( "fmt" "io/ioutil" "os/exec" "code.google.com/p/go.text/encoding/japanese" "code.google.com/p/go.text/transform" ) func main() { path, err := exec.LookPath("cmd") if err != nil { fmt.Printf("Error %v\n", err) return } fmt.Printf("Path = %s\n", path) cmd := exec.Command("cmd", "/c", "dir", "c:\\golang\\go\\") stdoutpipe, err := cmd.StdoutPipe() if err != nil { fmt.Printf("StdoutPipe Error: %v\n", err) return } defer stdoutpipe.Close() err = cmd.Start() if err != nil { fmt.Printf("Command Start Error: %v\n", err) return } stdout, err := ioutil.ReadAll( transform.NewReader(stdoutpipe, japanese.ShiftJIS.NewDecoder())) if err != nil { fmt.Printf("Command Error: %v\n", err) return } err = cmd.Wait() if err != nil { fmt.Printf("Command Wait Error: %v\n", err) return } fmt.Printf("%s\n", stdout) }
$ go run sample01.go Path = c:\Windows\system32\cmd.exe ドライブ C のボリューム ラベルがありません。 ボリューム シリアル番号は FED1-AC65 です c:\golang\go のディレクトリ 2013/12/13 11:45. 2013/12/13 11:45 .. 2013/12/13 11:45 api 2013/11/28 21:54 13,577 AUTHORS 2013/12/13 11:45 bin 2013/12/13 11:45 blog 2013/11/28 21:54 19,578 CONTRIBUTORS 2013/12/13 11:45 doc 2013/11/28 21:54 1,150 favicon.ico 2013/12/13 11:45 include 2013/12/13 11:45 lib 2013/11/28 21:54 1,479 LICENSE 2013/12/13 11:45 misc 2013/11/28 21:54 1,303 PATENTS 2013/12/13 11:45 pkg 2013/11/28 21:54 1,112 README 2013/11/28 21:54 26 robots.txt 2013/12/13 11:45 src 2013/12/13 11:45 test 2013/11/28 21:56 5 VERSION 8 個のファイル 38,230 バイト 12 個のディレクトリ 25,666,240,512 バイトの空き領域
手順としては、
- exec.Command関数で実行するコマンドを定義します。
- 起動したコマンドからのアウトプットを受け取る場合には、StdoutPipeメソッドにてコマンドを実行する前にReaderを取得しておきます。
- StartメソッドまたはRunメソッドでコマンドを実行します。両者の違いはStartメソッドはこのメソッドを実行後処理を続行します。Runメソッドは実行したコマンドが終了するまで待機します。
- Startメソッドでコマンドを実行後、終了を待機するにはWaitメソッドを使用します。
Runメソッド使用時はStdoutPipeは使えません。
os.signalパッケージ
os.signalパッケージは指定したシグナルを受信するためのパッケージです。
+TakashiYokoyama氏のサンプルで説明します。
func main() { c := make(chan os.Signal) signal.Notify(c, os.Interrupt, syscall.SIGHUP) tc := time.After(5 * time.Second) // シグナルを受信していなければ、関数を抜ける。 defer signal.Stop(c) for { select { case s := <-c: fmt.Printf("Signal Receive: %v\n", s) if s == os.Interrupt { return } case <- tc: // Windowsにはsyscall.Kill()が定義されていないので、コンパイルエラーになる。 // (Windows用のソースファイルが無い!) // syscallパッケージに定義されている、Signalの定義も怪しいかも? syscall.Kill(syscall.Getpid(), syscall.SIGHUP) } } }
使い方は非常に簡単でSignal型のチャンネルを用意しておき、Notify関数で受信するシグナルを設定するだけです。
シグナルを受信するとチャンネルに値が設定されるのでこれを参照するようにします。
シグナルの受信を中止する場合はStop関数を実行します。
ただ、このサンプルはソースコード中のコメントにもあるようにWindows環境では動作しません。Kill関数が動作しません、というかWindows用にはソースコードレベルで関数自体が定義されていないようです。
os.userパッケージ
os.userパッケージはユーザアカウントの情報を取得するためのパッケージです。
こちらも+TakashiYokoyama氏のサンプルを私の環境用に変更したもので説明します。
func main() { u, err := user.Current() if err != nil { fmt.Println("Error: ", err) return } printf("Current User", u) u, err = user.LookupId("S-1-5-21-XXXXXX...........") if err != nil { fmt.Println("Error: ", err) return } printf("LookupId User", u) u, err = user.Lookup("hogeuser") if err != nil { fmt.Println("Error: ", err) return } printf("Lookup User", u) fmt.Printf("Getuid: %v\n", os.Getuid()) fmt.Printf("Getgid: %v\n", os.Getgid()) } func printf(method string, u *user.User) { fmt.Printf("#%s#\nUid: %s\nGid: %s\nUsername: %s\nName: %s\nHomeDir: %s\n\n", method, u.Uid, u.Gid, u.Username, u.Name, u.HomeDir) }
$ go run sample01.go #Current User# Uid: S-1-5-21-XXXXXX........... Gid: S-1-5-21-XXXXXX........... Username: hoge-PC\hogeuser Name: HomeDir: C:\Users\taknb2nch #LookupId User# Uid: S-1-5-21-XXXXXX........... Gid: unknown Username: hoge-PC\hogeuser Name: HomeDir: Unknown directory #Lookup User# Uid: S-1-5-21-XXXXXX........... Gid: unknown Username: hoge-PC\hogeuser Name: HomeDir: Unknown directory Getuid: -1 Getgid: -1
それぞれCurrent関数は現在のユーザ、LookupId関数はユーザIDを指定して取得、Lookup関数はユーザ名を指定して取得します。
以前Windows環境でos.Getuid関数やos.Getgid関数を試した時、強制的にreturn -1という実装だったので今回もダメかと思っていましたが、予想とは逆に取得することができるようです。
ただUser.Nameが取得できなかったり、関数によって取得できる内容が異なっているようです。
Go言語は簡単にクロスコンパイルができるとのことなので、userパッケージはWindowsXP 32bitではどうかと試したばっかりにまたハマってしまいました。
結果的には取得できたのですがそれまでにはいつもの様に苦労がありました。
詳しくは別エントリーでまとめます。