Golang實(shí)現(xiàn)簡易的命令行功能
前言
一次偶然的想法,想知道為什么在終端輸入那些命令行后,就執(zhí)行了對(duì)應(yīng)的操作,這轉(zhuǎn)化為代碼,應(yīng)該怎么實(shí)現(xiàn)呢?
既然有了問題,那我們就來解決問題吧!
首先我認(rèn)為想做命令行操作,那就得先”認(rèn)識(shí)“命令行(當(dāng)然這里指你的代碼認(rèn)識(shí)),所以我認(rèn)位有兩個(gè)步驟:
- 解析命令行
- 實(shí)現(xiàn)對(duì)應(yīng)命令行的功能
話不多說開干!
開始
正好在學(xué)習(xí) Golang ,那就用它的試試吧!
首先,我們先來學(xué)習(xí)以下幾個(gè) API
flag.String
// String defines a string flag with specified name, default value, and usage string.
// The return value is the address of an int variable that stores the value of the flag.
// String 定義了一個(gè)字符串標(biāo)志,具有指定的名稱、默認(rèn)值和用法字符串。返回值是存儲(chǔ)標(biāo)志值的字符串變量的地址。
func String(name string, value string, usage string) *string {
return CommandLine.String(name, value, usage)
}也就是說,使用-name value 的命令參數(shù),usage是對(duì)這個(gè)參數(shù)的說明,返回值是這個(gè) value 的指針,也就是用戶輸入在 -name 后的 value。
flag.Int
// Int defines an int flag with specified name, default value, and usage string.
// The return value is the address of an int variable that stores the value of the flag.
// Int 定義了一個(gè)具有指定名稱、默認(rèn)值和用法字符串的 int 標(biāo)志。返回值是存儲(chǔ)標(biāo)志值的 int 變量的地址。
func Int(name string, value int, usage string) *int {
return CommandLine.Int(name, value, usage)
}使用方式和 String() 一樣,只是類型的區(qū)別。
flag.StringVar
// StringVar defines a string flag with specified name, default value, and usage string.
// The argument p points to a string variable in which to store the value of the flag.
// StringVar 定義了一個(gè)帶有指定名稱、默認(rèn)值和用法字符串的字符串標(biāo)志。參數(shù) p 指向一個(gè)字符串變量,用于存儲(chǔ)標(biāo)志的值。
func StringVar(p *string, name string, value string, usage string) {
CommandLine.Var(newStringValue(value, p), name, usage)
}這里可以看到區(qū)別就是,將返回值指針,變成了函數(shù)的第一個(gè)參數(shù)。
flag.IntVar
// IntVar defines an int flag with specified name, default value, and usage string.
// The argument p points to an int variable in which to store the value of the flag.
// IntVar 定義了一個(gè)具有指定名稱、默認(rèn)值和用法字符串的 int 標(biāo)志。參數(shù) p 指向一個(gè)存儲(chǔ)標(biāo)志值的 int 變量。
func IntVar(p *int, name string, value int, usage string) {
CommandLine.Var(newIntValue(value, p), name, usage)
}了解這些后,我們就開始吧!
定義命令行參數(shù)
package main
// ...import
func commandStyle() {
methodPtr := flag.String("method", "default", "method of sample")
valuePtr := flag.Int("value", -1, "value of sample")
// 解析
flag.Parse()
fmt.Println(*methodPtr, *valuePtr)
}
func commandStyle2() {
var method string
var value int
flag.StringVar(&method, "method", "default", "method of sample")
flag.IntVar(&value, "value", -1, "value of sample")
flag.Parse()
fmt.Println(method, value)
}
func main() {
commandStyle()
}在終端使用 go run . -method get -value 1 這串命令后,打印出了 get 1。
Parse 解析來自 os.Args[1:] 的命令行標(biāo)志。必須在定義所有標(biāo)志之后和程序訪問標(biāo)志之前調(diào)用。
這里的一個(gè)重要的點(diǎn)就是要使用 flag.Parse(),也就是解析go run .` 之后的標(biāo)志。使用變量將標(biāo)志的值接收,然后打印。
這兩種方式結(jié)果都是一樣,只有寫法上的差距,這時(shí)候雖然我們體驗(yàn)了一點(diǎn)簡單的命令行的影子了,但似乎還是感覺好像啥效果也沒有呀。接下來我們就來實(shí)現(xiàn)一個(gè) copy 文件內(nèi)容的功能
實(shí)現(xiàn) -f -v 是否強(qiáng)制拷貝
首先我們使用上面說過的類似方式,注冊(cè)標(biāo)志 f,v,然后解析標(biāo)志
func main() {
var showProgress, force bool
// -f 當(dāng)存在時(shí)拷貝,是否強(qiáng)制拷貝
flag.BoolVar(&force, "f", false, "force copy when existing")
flag.BoolVar(&showProgress, "v", false, "explain what is being done")
flag.Parse()
// 獲取參數(shù)個(gè)數(shù),必須要輸入兩個(gè)參數(shù),因?yàn)閏opy是從這個(gè)文件到另一個(gè)文件
if flag.NArg() < 2 {
flag.Usage() // 打印用途
return
}
copyFileAction(flag.Arg(0), flag.Arg(1), showProgress, force)
}注冊(cè)標(biāo)志完成后,我們就可以開始實(shí)現(xiàn)我們的 copy 功能了
首先我們必須后面要輸入兩個(gè)文件名,讓最后一個(gè)文件copy到前一個(gè)文件(制定規(guī)則)
我們模擬命令行輸入:go run . -f -v a.txt b.txt ,這就是我們最后需要實(shí)現(xiàn)的東西,f, v是可以省略的,默認(rèn)把 a.txt -> b.txt。
-f表示當(dāng)文件存在時(shí),強(qiáng)制copy覆蓋里面的內(nèi)容-v表示解釋正在做什么
接下來我們需要實(shí)現(xiàn)一個(gè) copyFileAction 函數(shù),來實(shí)現(xiàn)copy功能,以及命令行參數(shù)的效果
copyFileAction 實(shí)現(xiàn)
func fileExist(fileName string)bool {
_, err := os.Stat(fileName) // 返回這個(gè)文件信息
// IsExist 只是錯(cuò)誤或報(bào)告是否存在
// err == nil,表示有文件信息,os.IsExist(err),表示有文件存在
return err == nil || os.IsExist(err)
}
// 轉(zhuǎn)化操作,命令行,與功能實(shí)現(xiàn)的邏輯判斷
func copyFileAction(src, dst string, showProgress, force bool) {
if !force {
// 判斷是否存在文件,若存在,是否需要覆蓋它
if fileExist(dst) {
fmt.Printf("%s exists override? y/n \n", dst)
reader := bufio.NewReader(os.Stdin) // 讀取輸入內(nèi)容
data, _, _ := reader.ReadLine() // 取一行的內(nèi)容
// 判斷輸入的內(nèi)容
if strings.TrimSpace(string(data)) != "y" {
return
}
}
}
// copy 文件
copyFile(src, dst)
}這里我們可以看到充分利用到了 showProgress 和 force 兩個(gè)命令行取的值,當(dāng)文件``存在且不強(qiáng)制`時(shí),會(huì)有一個(gè)詢問,是否覆蓋,同意就實(shí)行 copy 操作,不同意不做處理(相當(dāng)于一次無效命令)。
接下來我們實(shí)現(xiàn)功能核心 copyFile
copyFile
func copyFile(originFile, targetFile string)(written int64, err error){
srcFile, err := os.Open(originFile) // 打開文件
if err != nil {
// Error() 返回錯(cuò)誤信息
log.Fatal(err)
return
}
defer srcFile.Close()
dstFile, err := os.Create(targetFile) // 創(chuàng)建文件
if err != nil {
// Error() 返回錯(cuò)誤信息
log.Fatal(err)
return
}
defer dstFile.Close()
return io.Copy(dstFile, srcFile) // 拷貝文件
}這里我們是采取,將需要被拷貝的文件打開,拷貝到的文件名進(jìn)行創(chuàng)建,然后將內(nèi)容填充進(jìn)去,這里使用了io.Copy() 的內(nèi)置功能。
效果圖

完整代碼
package main
import (
"bufio"
"flag"
"fmt"
"io"
"log"
"os"
"strings"
)
// func commandStyle() {
// methodPtr := flag.String("method", "default", "method of sample")
// valuePtr := flag.Int("value", -1, "value of sample")
// // 解析
// flag.Parse()
// fmt.Println(*methodPtr, *valuePtr)
// }
// func commandStyle2() {
// var method string
// var value int
// flag.StringVar(&method, "method", "default", "method of sample")
// flag.IntVar(&value, "value", -1, "value of sample")
// flag.Parse()
// fmt.Println(method, value)
// }
func main() {
// commandStyle()
// commandStyle2()
var showProgress, force bool
// -f 當(dāng)存在時(shí)拷貝,是否強(qiáng)制拷貝
flag.BoolVar(&force, "f", false, "force copy when existing")
flag.BoolVar(&showProgress, "v", false, "explain what is being done")
flag.Parse()
// 獲取參數(shù)個(gè)數(shù)
if flag.NArg() < 2 {
flag.Usage() // 打印用途
return
}
copyFileAction(flag.Arg(0), flag.Arg(1), showProgress, force)
}
func fileExist(fileName string)bool {
_, err := os.Stat(fileName) // 返回這個(gè)文件信息
// IsExist 只是錯(cuò)誤或報(bào)告是否存在
// err == nil,表示有文件信息,os.IsExist(err),表示有文件存在
return err == nil || os.IsExist(err)
}
func copyFile(originFile, targetFile string)(written int64, err error){
srcFile, err := os.Open(originFile) // 打開文件
if err != nil {
// Error() 返回錯(cuò)誤信息
log.Fatal(err)
return
}
defer srcFile.Close()
dstFile, err := os.Create(targetFile) // 創(chuàng)建文件
if err != nil {
// Error() 返回錯(cuò)誤信息
log.Fatal(err)
return
}
defer dstFile.Close()
return io.Copy(dstFile, srcFile) // 拷貝文件
}
// 拷貝文件
func copyFileAction(src, dst string, showProgress, force bool) {
if !force {
// 判斷是否存在文件,若存在,是否需要覆蓋它
if fileExist(dst) {
fmt.Printf("%s exists override? y/n \n", dst)
reader := bufio.NewReader(os.Stdin) // 讀取輸入內(nèi)容
data, _, _ := reader.ReadLine() // 取一行的內(nèi)容
// 判斷輸入的內(nèi)容
if strings.TrimSpace(string(data)) != "y" {
return
}
}
}
// copy 文件
copyFile(src, dst)
}以上就是Golang實(shí)現(xiàn)簡易的命令行功能的詳細(xì)內(nèi)容,更多關(guān)于Golang命令行功能的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Go語言性能監(jiān)控和調(diào)優(yōu)的工具和方法
本文介紹了Go語言性能監(jiān)控和調(diào)優(yōu)的工具和方法,包括?pprof、expvar?和?trace?等工具的使用方法和注意事項(xiàng),以及性能調(diào)優(yōu)的一些常見方法,如減少內(nèi)存分配、避免頻繁的垃圾回收、避免過度查詢數(shù)據(jù)庫等,針對(duì)不同的程序,應(yīng)該根據(jù)實(shí)際情況采用不同的優(yōu)化方法2024-01-01
Golang實(shí)現(xiàn)Trie(前綴樹)的示例
本文主要介紹了Golang實(shí)現(xiàn)Trie(前綴樹)的示例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-01-01
Golang Cron 定時(shí)任務(wù)的實(shí)現(xiàn)示例
這篇文章主要介紹了Golang Cron 定時(shí)任務(wù)的實(shí)現(xiàn)示例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-05-05
深入探討Golang中如何進(jìn)行并發(fā)發(fā)送HTTP請(qǐng)求
在?Golang?領(lǐng)域,并發(fā)發(fā)送?HTTP?請(qǐng)求是優(yōu)化?Web?應(yīng)用程序的一項(xiàng)重要技能,本文探討了實(shí)現(xiàn)此目的的各種方法,文中的示例代碼講解詳細(xì),希望對(duì)大家有所幫助2024-01-01
Go中string與[]byte高效互轉(zhuǎn)的方法實(shí)例
string與[]byte經(jīng)常需要互相轉(zhuǎn)化,普通轉(zhuǎn)化會(huì)發(fā)生底層數(shù)據(jù)的復(fù)制,下面這篇文章主要給大家介紹了關(guān)于Go中string與[]byte高效互轉(zhuǎn)的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考下2021-09-09

