脚本之家,脚本语言编程技术及教程分享平台!
分类导航

Python|VBS|Ruby|Lua|perl|VBA|Golang|PowerShell|Erlang|autoit|Dos|bat|shell|

服务器之家 - 脚本之家 - Golang - Golang实现简易的命令行功能

Golang实现简易的命令行功能

2023-02-14 11:43陪我去看海 Golang

这篇文章主要为大家详细介绍了如何通过Golang实现一个简易的命令行功能,文中的示例代码讲解详细,具有一定的学习价值,感兴趣的可以了解一下

前言

一次偶然的想法,想知道为什么在终端输入那些命令行后,就执行了对应的操作,这转化为代码,应该怎么实现呢?

既然有了问题,那我们就来解决问题吧!

首先我认为想做命令行操作,那就得先”认识“命令行(当然这里指你的代码认识),所以我认位有两个步骤:

  • 解析命令行
  • 实现对应命令行的功能

话不多说开干!

开始

正好在学习 Golang ,那就用它的试试吧!

首先,我们先来学习以下几个 API

flag.String

?
1
2
3
4
5
6
// 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 的命令参数,usage是对这个参数的说明,返回值是这个 value 的指针,也就是用户输入在 -name 后的 value

flag.Int

?
1
2
3
4
5
6
// 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() 一样,只是类型的区别。

flag.StringVar

?
1
2
3
4
5
6
// 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 定义了一个带有指定名称、默认值和用法字符串的字符串标志。参数 p 指向一个字符串变量,用于存储标志的值。
func StringVar(p *string, name string, value string, usage string) {
    CommandLine.Var(newStringValue(value, p), name, usage)
}

这里可以看到区别就是,将返回值指针,变成了函数的第一个参数。

flag.IntVar

?
1
2
3
4
5
6
// 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 标志。参数 p 指向一个存储标志值的 int 变量。
func IntVar(p *int, name string, value int, usage string) {
    CommandLine.Var(newIntValue(value, p), name, usage)
}

了解这些后,我们就开始吧!

定义命令行参数

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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:] 的命令行标志。必须在定义所有标志之后和程序访问标志之前调用。

这里的一个重要的点就是要使用 flag.Parse(),也就是解析go run .` 之后的标志。使用变量将标志的值接收,然后打印。

这两种方式结果都是一样,只有写法上的差距,这时候虽然我们体验了一点简单的命令行的影子了,但似乎还是感觉好像啥效果也没有呀。接下来我们就来实现一个 copy 文件内容的功能

实现 -f -v 是否强制拷贝

首先我们使用上面说过的类似方式,注册标志 f,v,然后解析标志

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
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()
 
    // 获取参数个数,必须要输入两个参数,因为copy是从这个文件到另一个文件
    if flag.NArg() < 2 {
        flag.Usage() // 打印用途
        return
    }
    copyFileAction(flag.Arg(0), flag.Arg(1), showProgress, force)
}

注册标志完成后,我们就可以开始实现我们的 copy 功能了

首先我们必须后面要输入两个文件名,让最后一个文件copy到前一个文件(制定规则)

我们模拟命令行输入:go run . -f -v a.txt b.txt ,这就是我们最后需要实现的东西,f, v是可以省略的,默认把 a.txt -> b.txt

  • -f表示当文件存在时,强制copy覆盖里面的内容
  • -v表示解释正在做什么

接下来我们需要实现一个 copyFileAction 函数,来实现copy功能,以及命令行参数的效果

copyFileAction 实现

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
func fileExist(fileName string)bool {
    _, err := os.Stat(fileName) // 返回这个文件信息
    // IsExist 只是错误或报告是否存在
    // err == nil,表示有文件信息,os.IsExist(err),表示有文件存在
    return err == nil || os.IsExist(err)
}
 
// 转化操作,命令行,与功能实现的逻辑判断
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) // 读取输入内容
            data, _, _ := reader.ReadLine() // 取一行的内容
            // 判断输入的内容
            if strings.TrimSpace(string(data)) != "y" {
                return
            }
        }
    }
    // copy 文件
    copyFile(src, dst)
}

这里我们可以看到充分利用到了 showProgress 和 force 两个命令行取的值,当文件``存在且不强制`时,会有一个询问,是否覆盖,同意就实行 copy 操作,不同意不做处理(相当于一次无效命令)。

接下来我们实现功能核心 copyFile

copyFile

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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) // 创建文件
    if err != nil {
        // Error() 返回错误信息
        log.Fatal(err)
        return
    }
    defer dstFile.Close()
    return io.Copy(dstFile, srcFile) // 拷贝文件
}

这里我们是采取,将需要被拷贝的文件打开,拷贝到的文件名进行创建,然后将内容填充进去,这里使用了io.Copy() 的内置功能。

效果图

Golang实现简易的命令行功能

完整代码

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
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()
 
    // 获取参数个数
    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) // 创建文件
    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) // 读取输入内容
            data, _, _ := reader.ReadLine() // 取一行的内容
            // 判断输入的内容
            if strings.TrimSpace(string(data)) != "y" {
                return
            }
        }
    }
    // copy 文件
    copyFile(src, dst)
}

以上就是Golang实现简易的命令行功能的详细内容,更多关于Golang命令行功能的资料请关注服务器之家其它相关文章!

原文链接:https://juejin.cn/post/7198707460823253047

延伸 · 阅读

精彩推荐
  • GolangGolang Gin解析JSON请求数据避免出现EOF错误

    Golang Gin解析JSON请求数据避免出现EOF错误

    这篇文章主要为大家介绍了Golang Gin 优雅地解析JSON请求数据,避免ShouldBindBodyWith出现EOF错误的源码分析,有需要的朋友可以借鉴参考下,希望能够有所帮助...

    EvaCcino9452022-09-29
  • Golang一个 Demo 学会使用 Go Delve 调试

    一个 Demo 学会使用 Go Delve 调试

    在 Go 语言中,Delve 调试工具是与 Go 语言亲和度最高的,因为 Delve 是 Go 语言实现的。其在我们日常工作中,非常常用。...

    脑子进煎鱼了7342021-07-26
  • GolangGO语言并发编程之互斥锁、读写锁详解

    GO语言并发编程之互斥锁、读写锁详解

    这篇文章主要介绍了GO语言并发编程之互斥锁、读写锁详解,本文是GO并发编程实战一书的样章,详细讲解了互斥锁、读写锁,然后给出了一个完整示例,需要的...

    junjie4102020-04-10
  • GolangGolang中slice删除元素的性能对比

    Golang中slice删除元素的性能对比

    go没有对删除切片元素提供专用的语法或者接口,需要使用切片本身的特性来删除元素,下面这篇文章主要给大家介绍了关于Golang中slice删除元素的性能对比...

    qishuai8252022-10-27
  • GolangGo语言并发编程 互斥锁详情

    Go语言并发编程 互斥锁详情

    在并发编程中,多个Goroutine访问同一块内存资源时可能会出现竞态条件,我们需要在临界区中使用适当的同步操作来以避免竞态条件。Go 语言中提供了很多...

    测试开发小记5082021-11-23
  • Golanggolang实现单点登录系统(go-sso)

    golang实现单点登录系统(go-sso)

    这篇文章主要介绍了golang实现单点登录系统(go-sso),本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...

    guyan03197602020-07-20
  • GolangGo中Writer和Reader接口的使用入门

    Go中Writer和Reader接口的使用入门

    本文主要介绍了Go中Writer和Reader接口的使用入门,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...

    宇宙之一粟11812022-09-25
  • GolangGo语言net包RPC远程调用三种方式http与json-rpc及tcp

    Go语言net包RPC远程调用三种方式http与json-rpc及tcp

    这篇文章主要为大家介绍了Go语言net包RPC远程调用三种方式分别使用http与json-rpc及tcp的示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助...

    秋天的春9672021-12-05