色付き文字列をio.MultiWriterで出力するには
以下のGo言語コードをyellow.exe
という名前で保存していたとします
package main
import (
"fmt"
"github.com/fatih/color"
)
func main() {
yellow := color.New(color.FgYellow).SprintFunc()
fmt.Fprintf(color.Output, "< %s >\n", yellow("yellow"))
}
それを以下のようなコードで外部プログラム実行した場合、io.MultiWriter だと色が出力されません。
package main
import (
"io"
"os"
"os/exec"
)
func main() {
var cmd *exec.Cmd
cmd = exec.Command(`yellow.exe`)
cmd.Stdout = os.Stdout
cmd.Run()
cmd = exec.Command(`yellow.exe`)
cmd.Stdout = io.MultiWriter(os.Stdout)
cmd.Run()
}
この例ではos.Stdout
のみをio.MultiWriter
に入れていますが、本来はファイルへの出力と同時に標準出力にも出力したいと考えていて、ファイルには色なしで出力し、標準出力には色付きで出力したいと考えています。また、外部プログラム実行をするプログラムはgo言語とは限らず、C#で作成したプログラムの場合もあり、外部プログラム側のコードはいじれない場合もあります。その場合にどのようにすれば、標準出力に色付きの出力をしつつ、ファイルにも同じ文字列を落とせるでしょうか?
========
追記(2018/07/10)
@metropolis さんのコメントを受けて、さらなる調査を行ったところ、一定条件下での解決策は出たと思います。以下にそれを記しておきます。
io.MultiWriter
を標準出力に設定すると、ターミナルではないという判定になり、mattn/colorable
が色設定をスキップするようなので、fatih/color
側の NoColor
をチェックしtrue
ならエスケープ情報をそのまま標準出力に流し、親側に色情報を提供するようにしました。もしfalse
なら生のターミナルにそのまま色情報を出力します。
package main
import (
"fmt"
"io"
"os"
"github.com/fatih/color"
)
func main() {
c := color.New(color.FgYellow)
var output io.Writer
if color.NoColor {
c.EnableColor()
output = os.Stdout
} else {
output = color.Output
}
yellow := c.SprintFunc()
fmt.Fprintf(output, "< %s >\n", yellow("yellow"))
}
親側はそれを受けて、標準出力はcolorable.NewColorable()
で受けて、ファイルへの出力はcolorable.NewNonColorable()
で受けてやることで、io.MultiWriterでも色設定をできるようにしました。
package main
import (
"flag"
"io"
"os"
"os/exec"
colorable "github.com/mattn/go-colorable"
)
var c = flag.String("c", "yellow.exe", "")
func main() {
flag.Parse()
var cmd *exec.Cmd
cmd = exec.Command(*c)
cmd.Stdout = colorable.NewColorable(os.Stdout)
cmd.Run()
f, _ := os.Create("yellow.txt")
defer f.Close()
cmd = exec.Command(*c)
cmd.Stdout = io.MultiWriter(colorable.NewNonColorable(f), colorable.NewColorable(os.Stdout))
cmd.Run()
}
親子がGo言語で書ける
且つ 子のコードを編集可能
である場合に限り、この対応で問題ないと思いますが、これ以外の場合においては、いまだ解決策がない状態です。