Golang實現(xiàn)簡易的命令行功能
前言
一次偶然的想法,想知道為什么在終端輸入那些命令行后,就執(zhí)行了對應(yīng)的操作,這轉(zhuǎn)化為代碼,應(yīng)該怎么實現(xiàn)呢?
既然有了問題,那我們就來解決問題吧!
首先我認為想做命令行操作,那就得先”認識“命令行(當然這里指你的代碼認識),所以我認位有兩個步驟:
- 解析命令行
- 實現(xiàn)對應(yīng)命令行的功能
話不多說開干!
開始
正好在學(xué)習 Golang ,那就用它的試試吧!
首先,我們先來學(xué)習以下幾個 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 定義了一個字符串標志,具有指定的名稱、默認值和用法字符串。返回值是存儲標志值的字符串變量的地址。
func String(name string, value string, usage string) *string {
return CommandLine.String(name, value, usage)
}也就是說,使用-name value 的命令參數(shù),usage是對這個參數(shù)的說明,返回值是這個 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 定義了一個具有指定名稱、默認值和用法字符串的 int 標志。返回值是存儲標志值的 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 定義了一個帶有指定名稱、默認值和用法字符串的字符串標志。參數(shù) p 指向一個字符串變量,用于存儲標志的值。
func StringVar(p *string, name string, value string, usage string) {
CommandLine.Var(newStringValue(value, p), name, usage)
}這里可以看到區(qū)別就是,將返回值指針,變成了函數(shù)的第一個參數(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 定義了一個具有指定名稱、默認值和用法字符串的 int 標志。參數(shù) p 指向一個存儲標志值的 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:] 的命令行標志。必須在定義所有標志之后和程序訪問標志之前調(diào)用。
這里的一個重要的點就是要使用 flag.Parse(),也就是解析go run .` 之后的標志。使用變量將標志的值接收,然后打印。
這兩種方式結(jié)果都是一樣,只有寫法上的差距,這時候雖然我們體驗了一點簡單的命令行的影子了,但似乎還是感覺好像啥效果也沒有呀。接下來我們就來實現(xiàn)一個 copy 文件內(nèi)容的功能
實現(xiàn) -f -v 是否強制拷貝
首先我們使用上面說過的類似方式,注冊標志 f,v,然后解析標志
func main() {
var showProgress, force bool
// -f 當存在時拷貝,是否強制拷貝
flag.BoolVar(&force, "f", false, "force copy when existing")
flag.BoolVar(&showProgress, "v", false, "explain what is being done")
flag.Parse()
// 獲取參數(shù)個數(shù),必須要輸入兩個參數(shù),因為copy是從這個文件到另一個文件
if flag.NArg() < 2 {
flag.Usage() // 打印用途
return
}
copyFileAction(flag.Arg(0), flag.Arg(1), showProgress, force)
}注冊標志完成后,我們就可以開始實現(xiàn)我們的 copy 功能了
首先我們必須后面要輸入兩個文件名,讓最后一個文件copy到前一個文件(制定規(guī)則)
我們模擬命令行輸入:go run . -f -v a.txt b.txt ,這就是我們最后需要實現(xiàn)的東西,f, v是可以省略的,默認把 a.txt -> b.txt。
-f表示當文件存在時,強制copy覆蓋里面的內(nèi)容-v表示解釋正在做什么
接下來我們需要實現(xiàn)一個 copyFileAction 函數(shù),來實現(xiàn)copy功能,以及命令行參數(shù)的效果
copyFileAction 實現(xiàn)
func fileExist(fileName string)bool {
_, err := os.Stat(fileName) // 返回這個文件信息
// IsExist 只是錯誤或報告是否存在
// err == nil,表示有文件信息,os.IsExist(err),表示有文件存在
return err == nil || os.IsExist(err)
}
// 轉(zhuǎn)化操作,命令行,與功能實現(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 兩個命令行取的值,當文件``存在且不強制`時,會有一個詢問,是否覆蓋,同意就實行 copy 操作,不同意不做處理(相當于一次無效命令)。
接下來我們實現(xiàn)功能核心 copyFile
copyFile
func copyFile(originFile, targetFile string)(written int64, err error){
srcFile, err := os.Open(originFile) // 打開文件
if err != nil {
// Error() 返回錯誤信息
log.Fatal(err)
return
}
defer srcFile.Close()
dstFile, err := os.Create(targetFile) // 創(chuàng)建文件
if err != nil {
// Error() 返回錯誤信息
log.Fatal(err)
return
}
defer dstFile.Close()
return io.Copy(dstFile, srcFile) // 拷貝文件
}這里我們是采取,將需要被拷貝的文件打開,拷貝到的文件名進行創(chuàng)建,然后將內(nèi)容填充進去,這里使用了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 當存在時拷貝,是否強制拷貝
flag.BoolVar(&force, "f", false, "force copy when existing")
flag.BoolVar(&showProgress, "v", false, "explain what is being done")
flag.Parse()
// 獲取參數(shù)個數(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) // 返回這個文件信息
// IsExist 只是錯誤或報告是否存在
// 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() 返回錯誤信息
log.Fatal(err)
return
}
defer srcFile.Close()
dstFile, err := os.Create(targetFile) // 創(chuàng)建文件
if err != nil {
// Error() 返回錯誤信息
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實現(xiàn)簡易的命令行功能的詳細內(nèi)容,更多關(guān)于Golang命令行功能的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Go語言性能監(jiān)控和調(diào)優(yōu)的工具和方法
本文介紹了Go語言性能監(jiān)控和調(diào)優(yōu)的工具和方法,包括?pprof、expvar?和?trace?等工具的使用方法和注意事項,以及性能調(diào)優(yōu)的一些常見方法,如減少內(nèi)存分配、避免頻繁的垃圾回收、避免過度查詢數(shù)據(jù)庫等,針對不同的程序,應(yīng)該根據(jù)實際情況采用不同的優(yōu)化方法2024-01-01
Golang Cron 定時任務(wù)的實現(xiàn)示例
這篇文章主要介紹了Golang Cron 定時任務(wù)的實現(xiàn)示例,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習或者工作具有一定的參考學(xué)習價值,需要的朋友們下面隨著小編來一起學(xué)習學(xué)習吧2020-05-05
深入探討Golang中如何進行并發(fā)發(fā)送HTTP請求
在?Golang?領(lǐng)域,并發(fā)發(fā)送?HTTP?請求是優(yōu)化?Web?應(yīng)用程序的一項重要技能,本文探討了實現(xiàn)此目的的各種方法,文中的示例代碼講解詳細,希望對大家有所幫助2024-01-01
Go中string與[]byte高效互轉(zhuǎn)的方法實例
string與[]byte經(jīng)常需要互相轉(zhuǎn)化,普通轉(zhuǎn)化會發(fā)生底層數(shù)據(jù)的復(fù)制,下面這篇文章主要給大家介紹了關(guān)于Go中string與[]byte高效互轉(zhuǎn)的相關(guān)資料,文中通過示例代碼介紹的非常詳細,需要的朋友可以參考下2021-09-09

