[golang][訳]os/execを使ってコマンドを実行します.
22349 ワード
[golang][訳]os/execを使ってコマンドを実行します.
https://colobu.com/2017/06/19/advanced-command-execution-in-Go-with-os-exec/
原文: A dvd command execution in Go with os/exec by Krzysztof Kwalczyk.完全コードは著者のgithubにあります. advance d-exec
Goは非常に便利に外部プログラムを実行できます.探索の旅を始めましょう.
コマンドを実行して出力結果を取得します.
最も簡単な例は、
上の例と似ていますが、stdoutとstderは別々に処理します.
もし一つの命令が実行されるまでに時間がかかりますか?
そのstdout/stderが得られるほか、コマンド実行の進捗をコンソールに表示したいです.
ちょっと複雑です
前の案は動作していますが、
実際にGo標準ライブラリは、より一般的な
プログラムで環境変数を取得する方法は分かりましたよね.
プログラムを実行する環境を変更したい場合があります.
あらかじめプログラムが存在するかどうかをチェックします.
想像してみてください.プログラムを書くには時間がかかります.最後に
もちろん、プログラムが存在するかどうかを確認しておけば完璧です.存在しないなら、エラー情報を印刷します.
チェックするために
次の章は翻訳者が追加した内容です.
パイプ
パイプラインを使って複数のコマンドを直列に接続できます.前のコマンドの出力は次のコマンドの入力です.
上のソリューションはGoスタイルのソリューションです.実際には「Trick」を使ってもいいです.
https://colobu.com/2017/06/19/advanced-command-execution-in-Go-with-os-exec/
原文: A dvd command execution in Go with os/exec by Krzysztof Kwalczyk.完全コードは著者のgithubにあります. advance d-exec
Goは非常に便利に外部プログラムを実行できます.探索の旅を始めましょう.
コマンドを実行して出力結果を取得します.
最も簡単な例は、
ls -lah
を実行し、組み合わせられたstdout/stder出力を得ることである.func main() {
cmd := exec.Command("ls", "-lah")
out, err := cmd.CombinedOutput()
if err != nil {
log.Fatalf("cmd.Run() failed with %s
", err)
}
fmt.Printf("combined out:
%s
", string(out))
}
stdoutとstderをそれぞれ処理します.上の例と似ていますが、stdoutとstderは別々に処理します.
func main() {
cmd := exec.Command("ls", "-lah")
var stdout, stderr bytes.Buffer
cmd.Stdout = &stdout
cmd.Stderr = &stderr
err := cmd.Run()
if err != nil {
log.Fatalf("cmd.Run() failed with %s
", err)
}
outStr, errStr := string(stdout.Bytes()), string(stderr.Bytes())
fmt.Printf("out:
%s
err:
%s
", outStr, errStr)
}
コマンド実行中に出力を取得します.もし一つの命令が実行されるまでに時間がかかりますか?
そのstdout/stderが得られるほか、コマンド実行の進捗をコンソールに表示したいです.
ちょっと複雑です
func copyAndCapture(w io.Writer, r io.Reader) ([]byte, error) {
var out []byte
buf := make([]byte, 1024, 1024)
for {
n, err := r.Read(buf[:])
if n > 0 {
d := buf[:n]
out = append(out, d...)
os.Stdout.Write(d)
}
if err != nil {
// Read returns io.EOF at the end of file, which is not an error for us
if err == io.EOF {
err = nil
}
return out, err
}
}
// never reached
panic(true)
return nil, nil
}
func main() {
cmd := exec.Command("ls", "-lah")
var stdout, stderr []byte
var errStdout, errStderr error
stdoutIn, _ := cmd.StdoutPipe()
stderrIn, _ := cmd.StderrPipe()
cmd.Start()
go func() {
stdout, errStdout = copyAndCapture(os.Stdout, stdoutIn)
}()
go func() {
stderr, errStderr = copyAndCapture(os.Stderr, stderrIn)
}()
err := cmd.Wait()
if err != nil {
log.Fatalf("cmd.Run() failed with %s
", err)
}
if errStdout != nil || errStderr != nil {
log.Fatalf("failed to capture stdout or stderr
")
}
outStr, errStr := string(stdout), string(stderr)
fmt.Printf("
out:
%s
err:
%s
", outStr, errStr)
}
コマンド実行中に出力2を取得します.前の案は動作していますが、
copyAndCapture
はio.Copy
を再実現したように見えます.Goのインターフェースの機能のために、io.Copy
を再利用することができます.CapturingPassThroughWriter
のstructを書いて、io.Writer
インターフェースを実現しました.すべてのデータを捕獲し、最終層のio.Writer
に書き込む.// CapturingPassThroughWriter is a writer that remembers
// data written to it and passes it to w
type CapturingPassThroughWriter struct {
buf bytes.Buffer
w io.Writer
}
// NewCapturingPassThroughWriter creates new CapturingPassThroughWriter
func NewCapturingPassThroughWriter(w io.Writer) *CapturingPassThroughWriter {
return &CapturingPassThroughWriter{
w: w,
}
}
func (w *CapturingPassThroughWriter) Write(d []byte) (int, error) {
w.buf.Write(d)
return w.w.Write(d)
}
// Bytes returns bytes written to the writer
func (w *CapturingPassThroughWriter) Bytes() []byte {
return w.buf.Bytes()
}
func main() {
var errStdout, errStderr error
cmd := exec.Command("ls", "-lah")
stdoutIn, _ := cmd.StdoutPipe()
stderrIn, _ := cmd.StderrPipe()
stdout := NewCapturingPassThroughWriter(os.Stdout)
stderr := NewCapturingPassThroughWriter(os.Stderr)
err := cmd.Start()
if err != nil {
log.Fatalf("cmd.Start() failed with '%s'
", err)
}
go func() {
_, errStdout = io.Copy(stdout, stdoutIn)
}()
go func() {
_, errStderr = io.Copy(stderr, stderrIn)
}()
err = cmd.Wait()
if err != nil {
log.Fatalf("cmd.Run() failed with %s
", err)
}
if errStdout != nil || errStderr != nil {
log.Fatalf("failed to capture stdout or stderr
")
}
outStr, errStr := string(stdout.Bytes()), string(stderr.Bytes())
fmt.Printf("
out:
%s
err:
%s
", outStr, errStr)
}
コマンド実行中に出力3を取得します.実際にGo標準ライブラリは、より一般的な
io.MultiWriter
を含んでいます.私たちは直接にそれを使うことができます.func main() {
var stdoutBuf, stderrBuf bytes.Buffer
cmd := exec.Command("ls", "-lah")
stdoutIn, _ := cmd.StdoutPipe()
stderrIn, _ := cmd.StderrPipe()
var errStdout, errStderr error
stdout := io.MultiWriter(os.Stdout, &stdoutBuf)
stderr := io.MultiWriter(os.Stderr, &stderrBuf)
err := cmd.Start()
if err != nil {
log.Fatalf("cmd.Start() failed with '%s'
", err)
}
go func() {
_, errStdout = io.Copy(stdout, stdoutIn)
}()
go func() {
_, errStderr = io.Copy(stderr, stderrIn)
}()
err = cmd.Wait()
if err != nil {
log.Fatalf("cmd.Run() failed with %s
", err)
}
if errStdout != nil || errStderr != nil {
log.Fatal("failed to capture stdout or stderr
")
}
outStr, errStr := string(stdoutBuf.Bytes()), string(stderrBuf.Bytes())
fmt.Printf("
out:
%s
err:
%s
", outStr, errStr)
}
プログラムを実行する環境を変更します.プログラムで環境変数を取得する方法は分かりましたよね.
[]string
は環境変数の名前であり、FOO=bar
は環境変数の値であり、FOO
の戻り値である.プログラムを実行する環境を変更したい場合があります.
bar
のos.Getenv("FOO")
の値を設定してもいいです.exec.Cmd
のフォーマットと同じです.通常は新しい環境を作るのではなく、自分に必要な環境変数を追加します. cmd := exec.Command("programToExecute")
additionalEnv := "FOO=bar"
newEnv := append(os.Environ(), additionalEnv))
cmd.Env = newEnv
out, err := cmd.CombinedOutput()
if err != nil {
log.Fatalf("cmd.Run() failed with %s
", err)
}
fmt.Printf("%s", out)
包みをつくる shurcooL/go/Outilは、環境変数の設定に便利な方法を提供します.あらかじめプログラムが存在するかどうかをチェックします.
想像してみてください.プログラムを書くには時間がかかります.最後に
Env
を呼び出して基本的な仕事をしてください.os.Environ()
プログラムが存在しない場合、プログラムは実行に失敗します.もちろん、プログラムが存在するかどうかを確認しておけば完璧です.存在しないなら、エラー情報を印刷します.
チェックするために
foo
方法を呼び出すことができます.func checkLsExists() {
path, err := exec.LookPath("ls")
if err != nil {
fmt.Printf("didn't find 'ls' executable
")
} else {
fmt.Printf("'ls' executable is in '%s'
", path)
}
}
もう一つの検査の方法は、プログラムに空の操作を実行させることです.例えば、パラメータを渡す「--help」にヘルプ情報を表示させることです.次の章は翻訳者が追加した内容です.
パイプ
パイプラインを使って複数のコマンドを直列に接続できます.前のコマンドの出力は次のコマンドの入力です.
foo
を使うのはちょっと面倒くさいです.次の方法を使ってもいいです.package main
import (
"bytes"
"io"
"os"
"os/exec"
)
func main() {
c1 := exec.Command("ls")
c2 := exec.Command("wc", "-l")
r, w := io.Pipe()
c1.Stdout = w
c2.Stdin = r
var b2 bytes.Buffer
c2.Stdout = &b2
c1.Start()
c2.Start()
c1.Wait()
w.Close()
c2.Wait()
io.Copy(os.Stdout, &b2)
}
または、自分でio.Pipeを作成するのではなく、exec.LookPath
のos.Exec
の方法を直接使用する`.package main
import (
"os"
"os/exec"
)
func main() {
c1 := exec.Command("ls")
c2 := exec.Command("wc", "-l")
c2.Stdin, _ = c1.StdoutPipe()
c2.Stdout = os.Stdout
_ = c2.Start()
_ = c1.Run()
_ = c2.Wait()
}
パイプ2上のソリューションはGoスタイルのソリューションです.実際には「Trick」を使ってもいいです.
package main
import (
"fmt"
"os/exec"
)
func main() {
cmd := "cat /proc/cpuinfo | egrep '^model name' | uniq | awk '{print substr($0, index($0,$4))}'"
out, err := exec.Command("bash", "-c", cmd).Output()
if err != nil {
fmt.Printf("Failed to execute command: %s", cmd)
}
fmt.Println(string(out))
}
https://www.cnblogs.com/landv/p/11584253.html