Goでプロンプト簡単に出すやつ
twitterID := prompter.Prompt("Enter your twitter ID", "")
lang := prompter.Choose("Which language do you like the most?", []string{"Perl", "Golang", "Scala", "Ruby"}, "Perl")
passwd := prompter.Password("Enter your password")
var likeSushi bool = prompter.YN("Do you like sushi?", true)
var likeBeer bool = prompter.YesNo("Do you like beer?", false)
以下の様な要件を満たす。
$ go get github.com/Songmu/goshim/cmd/goshim
より良い go run
を実現
→ スクリプト的にgoを実行したい場合に便利
$ goshim ./script/ore/pkgdir [args...]
go run
だと複数ファイルが大変
$ go run *.go
だと _test
とかが含まれて動かない$ go run $(go list -f '{{join .GoFiles " "}}')
とかやる必要があるgoshim
は内部的に os.Exec
するので、シグナル受信時の挙動も安定goshim
は内容に変化が無い場合、同じバイナリを使い回すようになっているmap[string]
使いすぎ問題とかあるstrings
パッケージで十分なものも多い regexp.MustCompile
はトップレベルもしくはinit()内でのみ使う
var reg = regexp.MustCompile(`...`)
MustCompile
は panicするregexp.Compile
を使う
reg, err := regexp.Compile(`...`)
str[:4]
str[1:3]
str[3:]
ok := strings.HasPrefix("abcde", "abc")
fields := strings.Fields("a b c d e")
strings.Split("a,b,c", ",")
string.TrimSpace(" a b c ")
str := strings.Replace("Perl Perl", "Perl", "Golang", -1)
horenso
% horenso -r reporter.pl -- /path/to/job args...
2>&1 | logger ...
とかやっても、コマンドの成否自体は後続のコマンドからは知るすべはない% /path/to/wrapper /path/to/job ...
ラッパーコマンドが、実行コマンドを受け取り、そのコマンドを実行した後に、事後処理をおこなう
@kazuho さんによるPerlスクリプト
% cronlog -- ping -n 5 my-server 2>&1
ジョブが失敗した時だけ、出力を出す
→ cronに指定すると、失敗した時だけメールが飛ぶとかできる
拙作のPerl製ジョブ通知フレームワーク
% ./wrapper.(sh|pl) /path/to/job ...
https://github.com/Songmu/horenso/releases よりバイナリ取得可能
go get
する手もあります
% go get github.com/Songmu/horenso/cmd/horenso
$ /path/to/job args...
こういうジョブに対して
$ horenso --reporter reporter.pl -- /path/to/job args...
このようにラップする。(reporter.plは任意の通知用スクリプト・後述)
{
"command": "perl -E 'say 1;warn \"$$\\n\";sleep 2'",
"commandArgs": [
"perl",
"-E",
"say 1;warn \"$$\\n\";sleep 2"
],
"output": "57078\n1\n",
"stdout": "1\n",
"stderr": "57078\n",
"exitCode": 0,
"result": "command exited with code: 0",
"hostname": "MatsukiMasayuki-no-MacBook-Pro.local",
"pid": 57078,
"startAt": "2016-02-16T01:27:08.946009881+09:00",
"endAt": "2016-02-16T01:27:11.010627341+09:00",
"systemTime": 0.03176,
"userTime": 0.025954
}
use JSON::PP;
my $report = decode_json <>; // 結果を取り出す
$report->{startAt};
...
import (
"encoding/json"
"os"
"github.com/Songmu/horenso"
)
func main() {
var d = horenso.Report{}
json.NewDecoder(os.Stdin).Decode(&d)
d.StartAt
...
}
% horenso --noticer 'ruby noticer.rb' \
-r reporter.pl -- /path/to/job args...
時間のかかるジョブの実行前に通知させたい場合
% horenso -r reporter.pl -r reporter2.py -- /path/to/job args...
複数のreporterを指定した場合、平行に実行される(Goっぽい)
$ cat ./wrapper.sh
#!/bin/sh
exec /path/to/horenso \
-n /path/to/noticer.py \
-r /path/to/reporter.pl \
-r 'ruby /path/to/reporter.rb' \
-- "$@"
これを例えば以下のようにcrontabに指定する
3 4 * * * /path/to/wrapper.sh /path/to/job --args... 2>&1 | logger -t myjob
別にcronじゃなくても、バッチジョブ実行の仕組みに対して統一的に導入すると便利。
Goっぽさある。
// コマンドからパイプを取り出す
cmd := exec.Command(args[0], args[1:]...)
stdoutPipe, _ := cmd.StdoutPipe()
stderrPipe, _ := cmd.StderrPipe()
// 出力を格納する bytes.Buffer を宣言
var bufStdout bytes.Buffer
var bufStderr bytes.Buffer
var bufMerged bytes.Buffer
// pipeの書き出し先 io.TeeReader で追加
stdoutPipe2 := io.TeeReader(stdoutPipe, io.MultiWriter(&bufStdout, &bufMerged))
stderrPipe2 := io.TeeReader(stderrPipe, io.MultiWriter(&bufStderr, &bufMerged))
// コマンドの実行と io.Copy
cmd.Start()
go func() {
defer stdoutPipe.Close()
io.Copy(os.Stdout, stdoutPipe2)
}
go func() {
defer stderrPipe.Close()
io.Copy(os.Stderr, stderrPipe2)
}
cmd.Wait()
// 取り出す
out := bufStdout.String()
stderr := bufStderr.String()
merged := bufMerged.String()
cmd := exec.Command(args[0], args[1:]...)
stdoutPipe, _ := cmd.StdoutPipe()
stderrPipe, _ := cmd.StderrPipe()
var bufStdout bytes.Buffer
var bufStderr bytes.Buffer
var bufMerged bytes.Buffer
stdoutPipe2 := io.TeeReader(stdoutPipe, io.MultiWriter(&bufStdout, &bufMerged))
stderrPipe2 := io.TeeReader(stderrPipe, io.MultiWriter(&bufStderr, &bufMerged))
cmd.Start()
go func() {
defer stdoutPipe.Close()
io.Copy(os.Stdout, stdoutPipe2)
}
go func() {
defer stderrPipe.Close()
io.Copy(os.Stderr, stderrPipe2)
}
cmd.Wait()
out := bufStdout.String()
stderr := bufStderr.String()
merged := bufMerged.String()
err := cmd.Run()
exitCode := 0
if e, ok := err.(*exec.ExitError); ok {
if w, ok := e.Sys().(syscall.WaitStatus); ok {
if w.Signaled() {
// 無理やり感
exitCode = int(w) | 0x80
} else {
exitCode = s.ExitStatus()
}
}
exitCode = -1
}
https://github.com/Songmu/wrapcommander に切り出し
io.Writer
interfaceを実装Write()
が呼ばれるたびにメモリアロケートしてるので現状効率悪い