Golang中基础的命令行模块urfave/cli的用法说明 优客号 • 2022年11月4日 18:47:12 • 用户投稿

Golang中基础的命令行模块urfave/cli的用法说明前言相信只要部署过线上服务,都知道启动参数一定是必不可少的,当你在不同的网络、硬件、软件环境下去启动一个服务的时候,总会有一些启动参数是不确定的,这时候就需要通过命令行模块去解析这

前言

相信只要部署过线上服务,都知道启动参数一定是必不可少的,当你在不同的网络、硬件、软件环境下去启动一个服务的时候,总会有一些启动参数是不确定的,这时候就需要通过命令行模块去解析这些参数,urfave/cli是Golang中一个简单实用的命令行工具。

安装

通过 go get github.com/urfave/cli 命令即可完成安装。

正文

使用了urfave/cli之后,你的程序就会变成一个命令行程序,以下就是通过urfave/cli创建的一个最简单的命令行程序,它设定了一些基础的信息,这个程序的最终只是简单的打印了Test信息。

package main import ( “github.com/urfave/cli” “os” “log” “fmt”) func main() { //实例化一个命令行程序 oApp := cli.NewApp() //程序名称 oApp.Name = “GoTool” //程序的用途描述 oApp.Usage = “To save the world” //程序的版本号 oApp.Version = “1.0.0” //该程序执行的代码 oApp.Action = func(c *cli.Context) error { fmt.Println(“Test”) return nil } //启动 if err := oApp.Run(os.Args); err != nil { log.Fatal(err) } /* result: [root@localhost cli]# go run main.go help NAME: GoTool – To save the world USAGE: main [global options] command [command options] [arguments…] VERSION: 1.0.0 COMMANDS: help, h Shows a list of commands or help for one command GLOBAL OPTIONS: –help, -h show help –version, -v print the version [root@localhost cli]# go run main.go Test */ }

我们看到运行 go run main.go help 之后会输出一些帮助信息,说明你的程序已经成功成为一个命令行程序,接着使用命令 go run main.go 运行这个程序,结果是打印了Test信息,所以这个程序实际运行的函数由oApp.Action来控制,你后面的代码应该都在这个函数的内部去实现。

接下来我们设定一些常见的启动参数,非常的简单,代码如下

package main import ( “github.com/urfave/cli” “os” “log” “fmt”) func main() { //实例化一个命令行程序 oApp := cli.NewApp() //程序名称 oApp.Name = “GoTool” //程序的用途描述 oApp.Usage = “To save the world” //程序的版本号 oApp.Version = “1.0.0” //预置变量 var host string var debug bool //设置启动参数 oApp.Flags = []cli.Flag{ //参数类型string,int,bool cli.StringFlag{ Name: “host”, //参数名字 Value: “127.0.0.1”, //参数默认值 Usage: “Server Address”, //参数功能描述 Destination: &host, //接收值的变量 }, cli.IntFlag{ Name: “port,p”, Value: 8888, Usage: “Server port”, }, cli.BoolFlag{ Name: “debug”, Usage: “debug mode”, Destination: &debug, }, } //该程序执行的代码 oApp.Action = func(c *cli.Context) error { fmt.Printf(“host=%v n”,host) fmt.Printf(“host=%v n”,c.Int(“port”)) //不使用变量接收,直接解析 fmt.Printf(“host=%v n”,debug) /* result: [root@localhost cli]# go run main.go –port 7777 host=127.0.0.1 host=7777 host=false [root@localhost cli]# go run main.go help NAME: GoTool – To save the world USAGE: main [global options] command [command options] [arguments…] VERSION: 1.0.0 COMMANDS: help, h Shows a list of commands or help for one command GLOBAL OPTIONS: –host value Server Address (default: “127.0.0.1”) –port value, -p value Server port (default: 8888) –debug debug mode –help, -h show help –version, -v print the version */ return nil } //启动 if err := oApp.Run(os.Args); err != nil { log.Fatal(err) } }

执行 go run main.go –port 7777 之后,可以看到输出了设定的7777端口而非默认的8888端口,而服务器地址(host)和调试模式(debug)都输出了默认的数值。

如果第三方人员第一次使用你的程序也可以通过help命令看到可以设定的参数都有哪些,非常的人性化。

当然,urfave/cli还允许我们设置多个命令,不同的命令执行不同的操作,具体如下

package main import ( “github.com/urfave/cli” “os” “log” “fmt”) func main() { //实例化一个命令行程序 oApp := cli.NewApp() //程序名称 oApp.Name = “GoTool” //程序的用途描述 oApp.Usage = “To save the world” //程序的版本号 oApp.Version = “1.0.0” //设置多个命令处理函数 oApp.Commands = []cli.Command{ { //命令全称 Name:”lang”, //命令简写 Aliases:[]string{“l”}, //命令详细描述 Usage:”Setting language”, //命令处理函数 Action: func(c *cli.Context) { // 通过c.Args().First()获取命令行参数 fmt.Printf(“language=%v n”,c.Args().First()) }, }, { Name:”encode”, Aliases:[]string{“e”}, Usage:”Setting encoding”, Action: func(c *cli.Context) { fmt.Printf(“encoding=%v n”,c.Args().First()) }, }, } //启动 if err := oApp.Run(os.Args); err != nil { log.Fatal(err) } /* [root@localhost cli]# go run main.go l english language=english [root@localhost cli]# go run main.go e utf8 encoding=utf8 [root@localhost cli]# go run main.go help NAME: GoTool – To save the world USAGE: main [global options] command [command options] [arguments…] VERSION: 1.0.0 COMMANDS: lang, l Setting language encode, e Setting encoding help, h Shows a list of commands or help for one command GLOBAL OPTIONS: –help, -h show help –version, -v print the version */ }

上面代码只实现了两个简单命令,两个命令最后的处理函数不同,自然使用不同命令,最后的输出也不一样。

补充:Go语言命令行库-urfave/cli(gopkg.in/urfave/cli.v2)

Go语言命令行库-urfave/cli

官网:https://github.com/urfave/cli

很多用Go写的命令行程序都用了urfave/cli这个库。urfave/cli是一个命令行的框架。

用C写过命令行程序的人应该都不陌生,我们需要根据argc/argv一个个地解析命令行参数,调用不同的函数,最后还要写一个usage()函数用于打印帮助信息。urfave/cli把这个过程做了一下封装,抽象出flag/command/subcommand这些模块,用户只需要提供一些模块的配置,参数的解析和关联在库内部完成,帮助信息也可以自动生成。

总体来说,urfave/cli这个库还是很好用的,完成了很多routine的工作,程序员只需要专注于具体业务逻辑的实现。

怎么使用urfave/cli

go如何编写命令行(cli)程序

首先下载类库包

go get github.com/urfave/cli

main.go

package mainimport ( “os” “github.com/urfave/cli/v2” “fmt”)func main() { app := &cli.App{ Name: “greet”, Usage: “say a greeting”, Action: func(c *cli.Context) error { fmt.Println(“Greetings”) return nil }, } // 接受os.Args启动程序 app.Run(os.Args)}

Flags 用于设置参数。

Action 对应的函数就是你具体对各个参数具体的处理逻辑。

“gopkg.in/urfave/cli.v2” 和 “github.com/urfave/cli”

官网:https://github.com/urfave/cli

gopkg:一种方便的go pakcage管理方式

根据官网 readme描述,现在2个版本,主版本使用的是 v2 分支。

导入包为: “github.com/urfave/cli/v2”

有些 go 的代码库地址是gopkg.in开头的,比如gopkg.in/urfave/cli.v2。

v2 表明版本号为 v2,而代码则为 github 上面相应的 v2 branch。

这个也是 Go 的包管理解决方案之一,就是 gopkg.in 做了一个转发过程,实际上是使用了 github 里面的相应的 tag 的代码

子命令 Subcommands

如下 demo所示,我们再Action:同层添加 我们定义指针 &cli.Command 变量即可。

demo:

var daemonStopCmd = &cli.Command{ Name: “stop”, Usage: “Stop a running lotus daemon”, Flags: []cli.Flag{}, Action: func(cctx *cli.Context) error { panic(“wombat attack”) },}func main() { app := &cli.App{ Name: “greet”, Usage: “say a greeting”, Action: func(c *cli.Context) error { fmt.Println(“Greetings”) return nil }, Subcommands: []*cli.Command{ daemonStopCmd, }, } // 接受os.Args启动程序 app.Run(os.Args)}

以上为个人经验,希望能给大家一个参考,也希望大家多多支持。如有错误或未考虑完全的地方,望不吝赐教。

golang命令行库Cobra的使用

苹果调整iOS16界面UI适配机型(iOS16系统或将首发登场)

写了2次才写完,内容很长,翻译了很久,内容来源于Cobra github介绍。翻译完也更全面的了解了Cobra,功能相当强大完善,各种使用的场景都考虑到了。

以下是正文: Cobra提供简单的接口来创建强大的现代化CLI接口,比如git与go工具。Cobra同时也是一个程序, 用于创建CLI程序 Cobra是建立在结构的命令、参数和标志之上。 命令代表操作,参数和标志是这些行动的修饰符。 最好的应用程序就像读取句子。

用户会知道如何使用本机应用程序,因为他们将理解如何使用它。 比如下面的例子, server 是命令, port 是标志: 在下面的命令,我们告诉Git克隆url地址bare 使用Cobra很简单。首先,使用 go get 安装最新版本 然后在你项目里引用Cobra 通常基于Cobra的应用程序将遵循下面的组织结构,当然你也可以遵循自己的接口: 在Cobra应用程序中,通常main.go文件非常空洞。

它主要只干一件事:初始化Cobra。 Cobra提供自己的程序来创建你的程序并且添加你想要的命令。这是最简单的方式把Cobra添加到你的程序里。

这里 你能找到相关信息 使用Cobra,需要创建一个空的main.go文件和一个rootCmd文件。你可以选择在合适的地方添加额外的命令。 Cobra不需要特殊的构造函数。

简单的就可以创建你的命令。 理想情况下你把这个放在在 app/cmd/root.go 你会另外定义标志和处理配置init()函数。 比如 cmd/root.go 你需要在main函数里执行root命令。 通常main.go文件非常空洞。

它主要只干一件事:初始化Cobra。 其它的命令通常定义在cmd/目录下的自己文件内 如果你想创建一个version命令,你可以创建cmd/version.go文件,并在文件里这么写: 标志提供修饰符控制动作命令如何操作 当标志定义好了,我们需要定义一个变量来关联标志 ‘持久’表示每个在那个命令下的命令都将能分配到这个标志。对于全局标志,’持久’的标志绑定在root上。 Cobra默认只在目标命令上解析标志,父命令忽略任何局部标志。

通过打开 Command.TraverseChildren Cobra将会在执行任意目标命令前解析标志 你同样可以通过 viper 绑定标志: 在这个例子中,永久的标记 author 被 viper 绑定, 注意 , 当用户没有给 –author 提供值, author 不会被赋值。 标记默认是可选的,如果你希望当一个标记没有设置时,命令行报错,你可以标记它为必须的 验证位置参数可以通过 Command 的 Args 字段。 内置下列验证方法 一个设置自定义验证的例子 在下面的例子,我们定义了3个命令。2个在顶级,一个(cmdTimes)是其中一个顶级命令的子命令。

在这个例子里,由于没有给 rootCmd 提供 Run ,单独的root是不能运行的,必须要有子命令。 我们仅为一个命令定义了标记。 更多关于flags的文档可以在 https://github.com/spf13/pflag 找到 更完整大型程序的例子, 可以查看 Hugo . 当你的程序有子命令时,Cobra 会自动给你程序添加help命令。

当你运行‘app help’,会调用help命令。另外,help同样支持其它输入命令。例如,你有一个没有任何其它配置的命令叫‘create’,当你调用‘app help create’ Corbra 将会起作用。

下面的输入是 Cobra 自动生成的。除了命令和标志的定义,其它不再需要。 help 就跟其它命令一样,并没有特殊的逻辑或行为。事实上,你也可以提供你自己help如果你想的话。

你能为默认的命令,提供你自己的help命令或模板。使用下面的方法: 后2个也将适用于任何子命令 当用户提供无效的标记或命令,Cobra 将会返回 用法 。 你可能从上面的帮助意识到,默认的帮助将被嵌入到用法里然后作为输出。

你能提供你自己的用法函数或模板给 Cobra 使用。 比如帮助,方法和模板都可以重写。 如果Version字段设置到了根命令,Cobra 会提供了一个顶层 ‘–version’标记。运行带上‘–version’标记的程序,将会按照模板版本信息。

模板可以通过 cmd.SetVersionTemplate(s string) 方法修改 在命令运行前或运行后,再运行方法非常容易。 PersistentPreRun 和 PreRun 方法将会在 Run 之前执行。 PersistentPostRun 和 PostRun 方法将会在 Run 之后执行。

Persistent*Run 方法会被子命令继承,如果它们自己没有定义的话。这些方法将按照下面的属性执行: 下面的例子,2个命令都使。

Golang常用包有哪些

苹果调整iOS16界面UI适配机型(iOS16系统或将首发登场)

⑴ Go Kit它本身不是一个框架,而是一套微服务工具集,可以用于解决分布式系统开发中的大多数常见问题,所以使用者可以专注于你的业务逻辑中。⑵ Gingko是一个Go测试框架,目的是帮助我们使用行为驱动开发风格高效地编写富有表现力和全面的测试,它有着非常良好的帮助文档,任何人都可以轻松地在项目中集成使用它。

⑷ GooseGolang中最佳的数据库迁移包,通过创建增量SQL更改和Go函数来管理数据库结构,在Go1.16版本以上,还支持了嵌入式sql迁移。⑸ GORM是一个功能齐全的Golang对象关系映射库,是一种开发人员友好的工具,用于在不兼容的类型系统之间转换数据,专门设计用于在类型系统之间切换时最大限度地减少重写代码。⑹ Authboss一个模块化的身份验证包,使用它你可以快速地在项目中进行身份验证管理。它有几个常见的身份验证和授权模块供开发人员选择。

⑺ cli是一个简单快捷的命令行管理包,用于为Go语言构建命令行应用程序,允许开发人员开发自己的富有表现力的命令行应用程序,用于创建标志、bash完成例程并生成帮助文本。⑻ Vegeta是一个用于HTTP负载测试的工具包,这个多功能工具专为测试具有恒定请求率的HTTP服务而设计。它可以有效地分析程序中的潜在问题,是一个始终贯穿以提高整体性能为目的的包。

怎么学习golang

苹果调整iOS16界面UI适配机型(iOS16系统或将首发登场)

随着 PHP 有着越来越深入的了解,以及遇到越来越多的不同业务时,使用 PHP 总会让我有一种莫名的无力感。当然,并不是我一个人在使用 PHP 的时候遇到了问题。

各种配合 LAMP(或者LNMP?)架构的后端技术也因此被发明或被发现,进而整合到 PHP 的开发的技术体系中。从简单的 Memcached作为数据中转,cron 后端定时处理;到 Gearman、RabbitMQ 这些队列神器;最近 Laruence 甚至封装了利用 libcurl 的异步特性实现并发 RPC 调用的 yar 扩展。几乎整个社区都在寻找 PHP 的摩西之路。好吧,说了一大堆,回归主题。

之前我写了一篇英文练笔《Why you PHP guys should learn Golang》,获得不少国际友人的关注。排除拼写和语法被他们诟病外,主要是有许多朋友觉得我没把事情说清楚。所以这里我用母语重新聊聊这个事情,只是这些国际友人什么时候能学会阅读中文呢?;)Go 或者 Golang,是由 Google 支持的快速、一致、稳定的,有活跃的社区支持的开源编程语言。

越来越多的应用选择使用 Golang 进行构建。虽然 Rob Pike 说“… 我们希望 C++ 程序员来了解 Go 并作为一个可选的语言 …”,不过我真得认为:PHPer 应当学习 Golang! 接下来我们就来谈谈原因。容易学习PHP 相当容易学习。

Golang 也是!在这点上,一群大老外对我的观点进行了猛烈的抨击。他们认为我羞辱了 PHPer,说得好像只有简单的东西 PHPer 才能学会一样。但是,这难道不是事实吗?或者换个说法:像我一样的喜欢 PHP 的人,或多或少都会更喜欢简单的东西。

PHP 的语法接近 C 族编程语言(C/C++/Java等等)。如果有这些语言的经验,在第一次遇到 PHP 的时候立刻就能开始上手编写代码。在我看来,编写 PHP 代码或许更加考验程序员的记忆力,而不是智力(当你面对各种不同风格的函数定义、各种扩展的特殊约定时,你一定会相当认同我的观点)。Golang 同样是一个 C 族编程语言。

Golang 只有 3025 个关键字和 47 个操作符号、分隔符号或其他特殊标记。记住这些标记确实不需要什么特别的努力。精巧的类型系统相当容易使用。实用的,具有方法的结构体类型代替了笨重的对象系统。

接口的设计是 Golang 中我最喜欢的部分。当完成了《Go 指南》的学习之后,利用 PHP 积累的经验,立刻就可以开始使用 Golang 处理一些简单的任务。容易使用PHP 脚本是由 SAPI 组件进行解析执行的,如 Web 服务器模块、PHP-FPM 或者 CLI。

部署 PHP 所需要的全部东西就是一个 SAPI 环境。配置这个环境对于新手来说可能是学习 PHP 过程中最为困难的部分。所有的 Golang 代码会编译和链接为本地码。

所以除了编译环境,执行时无需再为其进行任何特别的部署。对比 PHP 环境的配置,这要简单很多。你真得认为配置 PHP 环境很复杂吗?我不觉得,真的!而配置 Golang 编译环境比那还要简单点。我确信已经有大量的 Golang 相关的书籍、文章介绍过如何进行编译环境的配置了。

为了更加清晰,我这里梳理一下思路。有三个步骤需要处理:下载Golang 的源代码;根据《[翻译]Go 环境设置》的提示设置环境变量;运行源代码 src 目录中的 all.bash。或者一步到位:使用二进制包进行安装。

然后就会得到一个叫做“go”的工具集合。使用“go”工具和使用 PHP 的 CLI 工具一样简单。《[翻译]go 工具》对此进行了详细的解释。PHP 的迷思如果一个编程语言容易学习和使用,我们是不是就应当学习它呢?有许多容易学习和使用的编程语言。

难道要把它们都学一遍?答案是显然的:NO!但是 呢?只是因为它很酷!是的,我在开玩笑,但是这是真的。无论如何先从 PHP 自身谈起吧。PHP “原本是为了开发动态的 Web 页面而设计的服务器端通用语言(Wikipedia)”。

PHP 一个重要的特性就是可以嵌入到 HMTL 中。代码编写在“”标签内;HTML。

golang 多人开发怎么保证源码安全

苹果调整iOS16界面UI适配机型(iOS16系统或将首发登场)

随着PHP有着越来越深入的了解,以及遇到越来越多的不同业务时,使用PHP总会让我有一种莫名的无力感。当然,并不是我一个人在使用PHP的时候遇到了问题。

各种配合LAMP(或者LNMP?)架构的后端技术也因此被发明或被发现,进而整合到PHP的开发的技术体系中。从简单的Memcached作为数据中转,cron后端定时处理;到Gearman、RabbitMQ这些队列神器;最近Laruence甚至封装了利用libcurl的异步特性实现并发RPC调用的yar扩展。几乎整个社区都在寻找PHP的摩西之路。好吧,说了一大堆,回归主题。

之前我写了一篇英文练笔《WhyyouPHPguysshouldlearnGolang》,获得不少国际友人的关注。排除拼写和语法被他们诟病外,主要是有许多朋友觉得我没把事情说清楚。所以这里我用母语重新聊聊这个事情,只是这些国际友人什么时候能学会阅读中文呢?;)Go或者Golang,是由Google支持的快速、一致、稳定的,有活跃的社区支持的开源编程语言。

越来越多的应用选择使用Golang进行构建。虽然RobPike说“…我们希望C++程序员来了解Go并作为一个可选的语言…”,不过我真得认为:PHPer应当学习Golang!接下来我们就来谈谈原因。容易学习PHP相当容易学习。

Golang也是!在这点上,一群大老外对我的观点进行了猛烈的抨击。他们认为我羞辱了PHPer,说得好像只有简单的东西PHPer才能学会一样。但是,这难道不是事实吗?或者换个说法:像我一样的喜欢PHP的人,或多或少都会更喜欢简单的东西。

PHP的语法接近C族编程语言(C/C++/Java等等)。如果有这些语言的经验,在第一次遇到PHP的时候立刻就能开始上手编写代码。在我看来,编写PHP代码或许更加考验程序员的记忆力,而不是智力(当你面对各种不同风格的函数定义、各种扩展的特殊约定时,你一定会相当认同我的观点)。Golang同样是一个C族编程语言。

呃,或者有一些不同吧。例如关键字“for”,功能上和PHP的接近,但是没有括号。条件语句“if”同样无需括号。可以阅读EffectiveGo了解内容。

Golang只有3025个关键字和47个操作符号、分隔符号或其他特殊标记。记住这些标记确实不需要什么特别的努力。精巧的类型系统相当容易使用。实用的,具有方法的结构体类型代替了笨重的对象系统。

接口的设计是Golang中我最喜欢的部分。当完成了《Go指南》的学习之后,利用PHP积累的经验,立刻就可以开始使用Golang处理一些简单的任务。容易使用PHP脚本是由SAPI组件进行解析执行的,如Web服务器模块、PHP-FPM或者CLI。

部署PHP所需要的全部东西就是一个SAPI环境。配置这个环境对于新手来说可能是学习PHP过程中最为困难的部分。所有的Golang代码会编译和链接为本地码。

所以除了编译环境,执行时无需再为其进行任何特别的部署。对比PHP环境的配置,这要简单很多。你真得认为配置PHP环境很复杂吗?我不觉得,真的!而配置Golang编译环境比那还要简单点。我确信已经有大量的Golang相关的书籍、文章介绍过如何进行编译环境的配置了。

为了更加清晰,我这里梳理一下思路。有三个步骤需要处理:下载Golang的源代码;根据《[翻译]Go环境设置》的提示设置环境变量;运行源代码src目录中的all.bash。或者一步到位:使用二进制包进行安装。

然后就会得到一个叫做“go”的工具集合。使用“go”工具和使用PHP的CLI工具一样简单。《[翻译]go工具》对此进行了详细的解释。PHP的迷思如果一个编程语言容易学习和使用,我们是不是就应当学习它呢?有许多容易学习和使用的编程语言。

难道要把它们都学一遍?答案是显然的:NO!但是呢?只是因为它很酷!是的,我在开玩笑,但是这是真的。无论如何先从PHP自身谈起吧。PHP“原本是为了开发动态的Web页面而设计的服务器端通用语言(Wikipedia)”。

PHP一个重要的特性就是可以嵌入到HMTL中。代码编写在“”标签内;HTML写在标签外。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表优客号立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:https://www.youkehao.org.cn/article/46504.html

如若内容造成侵权/违法违规/事实不符,请联系优客号进行投诉反馈,一经查实,立即删除! 赞 (0)

优客号

优客号 认证作者 0 0 生成海报 扫码分享到微信 Golang使用第三方包viper读取yaml配置信息操作 « 上一篇 2022年11月4日 18:46:41 Golang中优秀的消息队列NSQ基础安装及使用详解 下一篇 » 2022年11月4日 18:47:56 相关推荐

sql server连接服务器的操作方法

用户投稿 sql server连接服务器的操作方法 2022年11月4日

Docker安装Nginx教程实现图例讲解

用户投稿 Docker安装Nginx教程实现图例讲解 2022年11月5日

python 安装教程之Pycharm安装及配置字体主题,换行,自动更新

用户投稿 python 安装教程之Pycharm安装及配置字体主题,换行,自动更新 2022年11月2日

Python + selenium + crontab实现每日定时自动打卡功能

用户投稿 Python + selenium + crontab实现每日定时自动打卡功能 2022年11月2日

keil5将keil工程生成hex文件的方法

用户投稿 keil5将keil工程生成hex文件的方法 2022年11月4日

微信小程序新手教程之加载本地图片方法

用户投稿 微信小程序新手教程之加载本地图片方法 2022年11月4日

hypermesh删除网格的方法步骤

用户投稿 hypermesh删除网格的方法步骤 2022年11月4日

Mybatis plus逻辑删除失败的BUG操作

用户投稿 Mybatis plus逻辑删除失败的BUG操作 2022年11月5日

golang 监听服务的信号,实现平滑启动,linux信号说明详解

用户投稿 golang 监听服务的信号,实现平滑启动,linux信号说明详解 2022年11月4日

浅谈Vue的组件间传值(包括Vuex)

用户投稿 浅谈Vue的组件间传值(包括Vuex) 2022年11月1日 发表评论 取消回复请登录后评论...登录后才能评论