在Go中使用殼管

在命令行中成為好公民意味著使用管道。

使用豎線|我們可以將一個命令的輸出作為另一個命令的輸入來傳遞,並鏈接多個命令以提供唯一的輸出。

我製作了2個使用管道作為輸入的教程,建立了古柯賽戈洛爾卡特,但是我沒有詳細描述從管道獲取輸入的過程,因此這裡是有關此主題的文章。

這是一個例子:

package main

import ( “bufio” “fmt” “io” “os” )

func main() { info, err := os.Stdin.Stat() if err != nil { panic(err) }

<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">info</span>.<span style="color:#a6e22e">Mode</span>()<span style="color:#f92672">&amp;</span><span style="color:#a6e22e">os</span>.<span style="color:#a6e22e">ModeCharDevice</span> <span style="color:#f92672">!=</span> <span style="color:#ae81ff">0</span> <span style="color:#f92672">||</span> <span style="color:#a6e22e">info</span>.<span style="color:#a6e22e">Size</span>() <span style="color:#f92672">&lt;=</span> <span style="color:#ae81ff">0</span> {
	<span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#e6db74">"The command is intended to work with pipes."</span>)
	<span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#e6db74">"Usage: fortune | gocowsay"</span>)
	<span style="color:#66d9ef">return</span>
}

<span style="color:#a6e22e">reader</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">bufio</span>.<span style="color:#a6e22e">NewReader</span>(<span style="color:#a6e22e">os</span>.<span style="color:#a6e22e">Stdin</span>)
<span style="color:#66d9ef">var</span> <span style="color:#a6e22e">output</span> []<span style="color:#66d9ef">rune</span>

<span style="color:#66d9ef">for</span> {
	<span style="color:#a6e22e">input</span>, <span style="color:#a6e22e">_</span>, <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">reader</span>.<span style="color:#a6e22e">ReadRune</span>()
	<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> <span style="color:#f92672">&amp;&amp;</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">==</span> <span style="color:#a6e22e">io</span>.<span style="color:#a6e22e">EOF</span> {
		<span style="color:#66d9ef">break</span>
	}
	<span style="color:#a6e22e">output</span> = append(<span style="color:#a6e22e">output</span>, <span style="color:#a6e22e">input</span>)
}

<span style="color:#66d9ef">for</span> <span style="color:#a6e22e">j</span> <span style="color:#f92672">:=</span> <span style="color:#ae81ff">0</span>; <span style="color:#a6e22e">j</span> &lt; len(<span style="color:#a6e22e">output</span>); <span style="color:#a6e22e">j</span><span style="color:#f92672">++</span> {
	<span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Printf</span>(<span style="color:#e6db74">"%c"</span>, <span style="color:#a6e22e">output</span>[<span style="color:#a6e22e">j</span>])
}

}

讓我們研究一下它是如何工作的。第一條有趣的線:

info, err := os.Stdin.Stat()

os.Stdin像Stdout和Stderr一樣,是開放的文件。它指向標準輸入文件描述符。

Stat()是File的方法,該方法返回一個文件信息描述文件,提供包括模式和文件大小的信息。

下一個有趣的部分是

info.Mode()&os.ModeCharDevice != 0

FileInfo.Mode()返回uint32將文件模式和權限確定為const的掩碼(https://golang.org/pkg/os/#FileMode)。

Usign按位與並確定文件模式是否為os.ModeCharDevice。這是一種確保文件指向ModeCharDevice,Unix字符設備(終端)。由於文件指向os.Stdin,我們基本上排除了輸入是管道的情況。

如果模式是不是我們通過的常數。我們也可以檢查

info.Mode()&os.ModeCharDevice == os.ModeCharDevice

相同,但更具可讀性。

如果這是錯誤的,我們將另外檢查

info.Size() <= 0

與前面的檢查結合使用,請確保具有輸入管道,並且該管道實際上包含一些字節。

我們也可以檢查

if info.Mode()&os.ModeNamedPipe != 0 {
	// we have a pipe input
}

並確保輸入來自管道。

接下來,程序創建一個閱讀器

reader := bufio.NewReader(os.Stdin)

bufio是一個實現緩衝I / O的程序包,基本上是包裝io.Readerio.Writer

我們要求它使用默認的緩衝區大小(即4096字節)為os.Stdin提供一個緩衝的讀取器。

bufio.Reader提供了許多讀取數據的方法:ReadByteReadBytesReadLineReadRuneReadSliceReadString

該示例使用ReadRune因此可以將每個符文添加到var output []rune切片和處理統一碼字符不費吹灰之力。

閱讀更多


更多教程: