Golang實(shí)現(xiàn)簡(jiǎ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)簡(jiǎ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)簡(jiǎn)易的命令行功能的詳細(xì)內(nèi)容,更多關(guān)于Golang命令行功能的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Go類型斷言提取測(cè)試接口值動(dòng)態(tài)類型及靜態(tài)轉(zhuǎn)換確保類型接口編譯安全
這篇文章主要為大家介紹了Go類型斷言提取測(cè)試接口值動(dòng)態(tài)類型及靜態(tài)轉(zhuǎn)換確保類型實(shí)現(xiàn)特定接口的編譯時(shí)安全性詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-10-10
一文搞懂Go語(yǔ)言中defer關(guān)鍵字的使用
defer是golang中用的比較多的一個(gè)關(guān)鍵字,也是go面試題里經(jīng)常出現(xiàn)的問題。今天就來整理一下關(guān)于defer的學(xué)習(xí)使用,希望對(duì)需要的朋友有所幫助2022-09-09
golang 后臺(tái)進(jìn)程的啟動(dòng)和停止操作
這篇文章主要介紹了golang 后臺(tái)進(jìn)程的啟動(dòng)和停止操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2021-04-04
Go語(yǔ)言基礎(chǔ)語(yǔ)法之結(jié)構(gòu)體及方法詳解
結(jié)構(gòu)體類型可以用來保存不同類型的數(shù)據(jù),也可以通過方法的形式來聲明它的行為。本文將介紹go語(yǔ)言中的結(jié)構(gòu)體和方法,以及“繼承”的實(shí)現(xiàn)方法2021-09-09
go mod 安裝依賴 unkown revision問題的解決方案
這篇文章主要介紹了go mod 安裝依賴 unkown revision問題的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2021-05-05
Golang語(yǔ)言學(xué)習(xí)拿捏Go反射示例教程
這篇文章主要為大家介紹了Golang語(yǔ)言中Go反射示例的教程,教你拿捏Go反射,再也不用被Go反射折磨,有需要的朋友可以共同學(xué)習(xí)參考下2021-11-11
Go語(yǔ)言net包RPC遠(yuǎn)程調(diào)用三種方式http與json-rpc及tcp
這篇文章主要為大家介紹了Go語(yǔ)言net包RPC遠(yuǎn)程調(diào)用三種方式分別使用http與json-rpc及tcp的示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助2021-11-11
Golang?統(tǒng)計(jì)字符串中數(shù)字字母數(shù)量的實(shí)現(xiàn)方法
這篇文章主要介紹了Golang?統(tǒng)計(jì)字符串中數(shù)字字母數(shù)量,本文給出了兩種從字符串獲取數(shù)字與字母數(shù)量的方法,分別是ASCII 碼值和正則表達(dá)式,需要的朋友可以參考下2022-06-06

