溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

Golang怎么應(yīng)用執(zhí)行Shell命令

發(fā)布時(shí)間:2023-03-16 10:26:46 來源:億速云 閱讀:143 作者:iii 欄目:開發(fā)技術(shù)

今天小編給大家分享一下Golang怎么應(yīng)用執(zhí)行Shell命令的相關(guān)知識(shí)點(diǎn),內(nèi)容詳細(xì),邏輯清晰,相信大部分人都還太了解這方面的知識(shí),所以分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后有所收獲,下面我們一起來了解一下吧。

exec包

使用官方os/exec包可以執(zhí)行外部命令,當(dāng)你執(zhí)行shell命令,是需要在Go應(yīng)用的外部運(yùn)行代碼,因此需要這些命令在子進(jìn)程中運(yùn)行。如下圖所示:

Golang怎么應(yīng)用執(zhí)行Shell命令

每個(gè)命令在Go應(yīng)用中作為子進(jìn)程運(yùn)行,并暴露stdin和stdout屬性,我們可以使用它們讀寫進(jìn)程數(shù)據(jù)。

運(yùn)行基本Shell命令

運(yùn)行簡(jiǎn)單命令并從它的輸出中讀取數(shù)據(jù),通過創(chuàng)建*exec.Cmd實(shí)例實(shí)現(xiàn)。在下面示例中,使用ls列出當(dāng)前目錄下的文件,并從代碼中打印其輸出:

// create a new *Cmd instance
// here we pass the command as the first argument and the arguments to pass to the command as the
// remaining arguments in the function
cmd := exec.Command("ls", "./")

// The `Output` method executes the command and
// collects the output, returning its value
out, err := cmd.Output()
if err != nil {
  // if there was any error, print it here
  fmt.Println("could not run command: ", err)
}
// otherwise, print the output from running the command
fmt.Println("Output: ", string(out))

因?yàn)樵诋?dāng)前目錄下運(yùn)行程序,因此輸出項(xiàng)目根目錄下文件:

> go run shellcommands/main.go

Output:  LICENSE
README.md
command.go

Golang怎么應(yīng)用執(zhí)行Shell命令

當(dāng)運(yùn)行exec,程序沒有產(chǎn)生shell,而是直接運(yùn)行給定命令,這意味著不會(huì)進(jìn)行任何基于shell的處理,比如glob模式或擴(kuò)展。舉例,當(dāng)運(yùn)行ls ./*.md命令,并不會(huì)如我們?cè)谀莻€(gè)shell中運(yùn)行命令一樣輸出readme.md。

執(zhí)行長(zhǎng)時(shí)間運(yùn)行命令

前面示例執(zhí)行l(wèi)s命令立刻返回結(jié)果,但當(dāng)命令輸出是連續(xù)的、或需要很長(zhǎng)時(shí)間執(zhí)行時(shí)會(huì)怎樣呢?舉例,運(yùn)行ping命令,會(huì)周期性獲得連續(xù)結(jié)果:

ping www.baidu.com 
PING www.a.shifen.com (36.152.44.95) 56(84) bytes of data.
64 bytes from 36.152.44.95 (36.152.44.95): icmp_seq=1 ttl=128 time=11.1 ms
64 bytes from 36.152.44.95 (36.152.44.95): icmp_seq=2 ttl=128 time=58.8 ms
64 bytes from 36.152.44.95 (36.152.44.95): icmp_seq=3 ttl=128 time=28.2 ms
64 bytes from 36.152.44.95 (36.152.44.95): icmp_seq=4 ttl=128 time=11.1 ms
64 bytes from 36.152.44.95 (36.152.44.95): icmp_seq=5 ttl=128 time=11.5 ms
64 bytes from 36.152.44.95 (36.152.44.95): icmp_seq=6 ttl=128 time=53.6 ms
64 bytes from 36.152.44.95 (36.152.44.95): icmp_seq=7 ttl=128 time=10.2 ms
64 bytes from 36.152.44.95 (36.152.44.95): icmp_seq=8 ttl=128 time=10.4 ms
64 bytes from 36.152.44.95 (36.152.44.95): icmp_seq=9 ttl=128 time=15.8 ms
64 bytes from 36.152.44.95 (36.152.44.95): icmp_seq=10 ttl=128 time=16.5 ms
64 bytes from 36.152.44.95 (36.152.44.95): icmp_seq=11 ttl=128 time=10.9 ms
^C64 bytes from 36.152.44.95: icmp_seq=12 ttl=128 time=9.92 ms

如果嘗試使用cmd.Output執(zhí)行這類命令,則不會(huì)獲得任何結(jié)果,因?yàn)镺utput方法等待命令執(zhí)行結(jié)束,而ping無限期執(zhí)行。因此需要自定義Stdout屬性去讀取連續(xù)輸出:

cmd := exec.Command("ping", "google.com")

// pipe the commands output to the applications
// standard output
cmd.Stdout = os.Stdout

// Run still runs the command and waits for completion
// but the output is instantly piped to Stdout
if err := cmd.Run(); err != nil {
  fmt.Println("could not run command: ", err)
}

再次運(yùn)行程序,輸出結(jié)果于Shell中執(zhí)行類似。

通過直接分配Stdout屬性,我們可以在整個(gè)命令生命周期中捕獲輸出,并在接收到輸出后立即對(duì)其進(jìn)行處理。進(jìn)程間io交互如下圖所示:

Golang怎么應(yīng)用執(zhí)行Shell命令

自定義寫輸出

代替使用os.Stdout,還能通過實(shí)現(xiàn)io.Writer接口創(chuàng)建自定義寫輸出。

下面自定義代碼在每個(gè)輸出塊前增加"received output: "前綴:

type customOutput struct{}

func (c customOutput) Write(p []byte) (int, error) {
	fmt.Println("received output: ", string(p))
	return len(p), nil
}

現(xiàn)在給命令輸出賦值自定義寫輸出實(shí)例:

cmd.Stdout = customOutput{}

再次運(yùn)行程序,會(huì)獲得下面的輸出。

使用Stdin給命令傳遞輸入

前面示例沒有給命令任何輸入(或提供有限輸入作為參數(shù)),大多數(shù)場(chǎng)景中通過Stdin流傳遞輸入信息。典型的示例為grep命令,可以通過管道從一個(gè)命令串給另一個(gè)命令:

?  ~ echo "1. pear\n2. grapes\n3. apple\n4. banana\n" | grep apple
3. apple

這里echo的輸出作為stdin傳給grep,輸入一組水果,通過grep過濾僅輸出apple.

*Cmd實(shí)例提供了輸入流用于寫入,下面實(shí)例使用它傳遞輸入給grep子進(jìn)程:

cmd := exec.Command("grep", "apple")

// Create a new pipe, which gives us a reader/writer pair
reader, writer := io.Pipe()
// assign the reader to Stdin for the command
cmd.Stdin = reader
// the output is printed to the console
cmd.Stdout = os.Stdout

go func() {
  defer writer.Close()
  // the writer is connected to the reader via the pipe
  // so all data written here is passed on to the commands
  // standard input
  writer.Write([]byte("1. pear\n"))
  writer.Write([]byte("2. grapes\n"))
  writer.Write([]byte("3. apple\n"))
  writer.Write([]byte("4. banana\n"))
}()

if err := cmd.Run(); err != nil {
  fmt.Println("could not run command: ", err)
}

輸出結(jié)果:

3. apple

Golang怎么應(yīng)用執(zhí)行Shell命令

結(jié)束子進(jìn)程

有一些命令無限期運(yùn)行,需要能夠顯示信號(hào)去結(jié)束。舉例,如果使用python3 -m http.server運(yùn)行web服務(wù)或sleep 10000,則子進(jìn)程會(huì)運(yùn)行很長(zhǎng)時(shí)間或無限期運(yùn)行。

要停止進(jìn)程,需要從應(yīng)用中發(fā)送kill信號(hào),可以通過給命令增加上下文實(shí)例實(shí)現(xiàn)。如果上下文取消,則命令也會(huì)終止執(zhí)行:

ctx := context.Background()
// The context now times out after 1 second
// alternately, we can call `cancel()` to terminate immediately
ctx, _ = context.WithTimeout(ctx, 1*time.Second)

// sleep 10 second 
cmd := exec.CommandContext(ctx, "sleep", "10")

out, err := cmd.Output()
if err != nil {
  fmt.Println("could not run command: ", err)
}
fmt.Println("Output: ", string(out))

運(yùn)行程序,1秒后輸出結(jié)果:

could not run command:  signal: killed
Output:  

當(dāng)需要在有限時(shí)間內(nèi)運(yùn)行命令或在一定時(shí)間內(nèi)命令沒有返回結(jié)果則執(zhí)行備用邏輯。

以上就是“Golang怎么應(yīng)用執(zhí)行Shell命令”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家閱讀完這篇文章都有很大的收獲,小編每天都會(huì)為大家更新不同的知識(shí),如果還想學(xué)習(xí)更多的知識(shí),請(qǐng)關(guān)注億速云行業(yè)資訊頻道。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI