简体   繁体   English

如何将 cobra 子命令源放入单独的文件夹中

[英]How to put cobra sub commands sources into separate folders

I'm a Go beginner, and I'm trying to create a CLI with Cobra .我是 Go 初学者,我正在尝试使用Cobra创建 CLI。 To bootstrap the project, I used the Cobra Generator , generated a command, a subcommand, and everything works fine.为了引导项目,我使用了Cobra Generator ,生成了一个命令,一个子命令,一切正常。

I now have this type of layout:我现在有这种类型的布局:

cli
├── cmd
│   ├── command.go
│   ├── root.go
│   ├── subcommand.go
├── go.mod
├── go.sum
└── main.go

This doesn't suit me, let's say my project is intendend to have lots of commands, or lots of command namespaces, each owned by a different team, it would become very messy and hard to maintain.这不适合我,假设我的项目打算有很多命令,或者很多命令命名空间,每个都由不同的团队拥有,它会变得非常混乱且难以维护。 I would prefer a layout like this:我更喜欢这样的布局:

cli
├── cmd
│   ├── command
│   │   ├── command.go
│   │   └── subcommand.go
│   └── root.go
├── go.mod
├── go.sum
└── main.go

Now, I lack some knowledge about the way packages and imports are done in Go (even after reading the doc here and there ), but as far as I understand, a resource can be accessed accross multiple go source files as long as they belong to the same package. But as said in the documentation, "An implementation may require that all source files for a package inhabit the same directory.", so to achieve a layout such as I would like, I would need to have multiple packages, ie one for each command namespace, and I think this is fine (or at least, I don't understand what would be wrong with that).现在,我对包和导入在 Go 中完成的方式缺乏一些了解(即使在这里那里阅读了文档),但据我所知,资源可以跨多个 go 源文件访问,只要它们属于相同的 package。但正如文档中所述,“实现可能要求 package 的所有源文件位于同一目录中。”,因此要实现我想要的布局,我需要有多个包,即每个命令命名空间一个,我认为这很好(或者至少,我不明白这样做有什么问题)。 So this is what I've tried:所以这就是我尝试过的:

  • Create a command directory inside the cmd onecmd里面创建一个command目录
  • Move the command.go file inside the command directorycommand.go文件移动到command目录中
  • Change the package clause from the command.go file to commandcommand.go文件中的package子句改为command
  • Do the same for subcommand.go对子命令执行相同subcommand.go

And this builds fine, but the command is not found( Error: unknown command "command" for "cli" ).这构建得很好,但是找不到命令( Error: unknown command "command" for "cli" )。 I thought it was because I never imported that new command package, so I imported it in the main.go file, where the cmd package is imported.我以为是因为我从来没有导入过那个新command package,所以我在main.go文件中导入了,这里导入了cmd package。

The build fails, telling me undefined: rootCmd in the command.go file.构建失败,告诉我undefined: rootCmd command.go文件中的 rootCmd。 I guess the command package is unable to see the resources from the cmd one, so I imported the cmd package in the command.go file, and replaced rootCmd with cmd.rootCmd ( rootCmd being a variable created in the cli/cmd/root.go file, part of the Cobra Generator provided files).我猜command package 无法从cmd中看到资源,所以我在command.go文件中导入了cmd package,并用rootCmd中创建的变量 rootCmd 替换了cmd.rootCmd cli/cmd/root.go rootCmd cli/cmd/root.go文件,Cobra Generator 提供文件的一部分)。 I really had hope this time, but the result is still the same ( undefined: cmd.rootCmd ), and now I'm lost.这次我真的抱有希望,但结果还是一样( undefined: cmd.rootCmd ),现在我迷路了。

Am I trying to do something that is not possible with Cobra?我是否正在尝试做 Cobra 做不到的事情?

Am I trying to do something that is possible with Cobra, but not using the Cobra Generator?我是否正在尝试使用 Cobra 做一些可能的事情,但不使用 Cobra 生成器?

Am I trying to do something that should not be done at all (like a bad design from which Go is trying to protect me)?我是否正在尝试做一些根本不应该做的事情(比如 Go 试图保护我的糟糕设计)?

If not, how should I proceed?如果没有,我应该如何进行?

You can't get the layout you want by using the cobra-cli command, but you can certainly set things up manually.您无法使用cobra-cli命令获得所需的布局,但您当然可以手动设置。 Let's start with the cobra-cli layou:让我们从cobra-cli layou 开始:

$ go mod init example
$ cobra-cli init

And add a couple of commands:并添加几个命令:

$ cobra-cli add foo
$ cobra-cli add bar

This gets us:这让我们:

.
├── cmd
│   ├── bar.go
│   ├── foo.go
│   └── root.go
├── go.mod
├── go.sum
├── LICENSE
└── main.go

Let's first move the commands into subdirectories so we have the desired layout.让我们首先将命令移动到子目录中,以便我们拥有所需的布局。 That gets us:这让我们:

.
├── cmd
│   ├── bar
│   │   └── bar.go
│   ├── foo
│   │   └── foo.go
│   └── root.go
├── go.mod
├── go.sum
├── LICENSE
└── main.go

Now we need to make some changes to our code so that this works.现在我们需要对我们的代码进行一些更改以使其正常工作。

  1. Because our subcommands are now in separate packages, we'll need to rename rootCmd so that it is exportable.因为我们的子命令现在位于单独的包中,所以我们需要重命名rootCmd以便它可以导出。

     find cmd -name '*.go' -print | xargs sed -i 's/rootCmd/RootCmd/g'
  2. We need each subcommand to be in its own package, so we'll replace package cmd at the top of cmd/foo/foo.go with package foo , and with package bar in cmd/bar/bar.go . We need each subcommand to be in its own package, so we'll replace package cmd at the top of cmd/foo/foo.go with package foo , and with package bar in cmd/bar/bar.go .

  3. We need the subcommands to import the root command so that they can call RootCmd.AddCommand .我们需要子命令来导入根命令,以便它们可以调用RootCmd.AddCommand That means in cmd/foo/foo.go and cmd/bar/bar.go we need to add example/cmd to our import section (recall that we named our top-level package example via the go mod init command).这意味着在cmd/foo/foo.gocmd/bar/bar.go ,我们需要将example/cmd添加到import部分(回想一下,我们通过go mod init命令将顶级example命名为 package)。 That means each file will start with:这意味着每个文件将以:

     package foo import ( "fmt" "example/cmd" "github.com/spf13/cobra" )

    As part of this change, we will also need to update the reference to AddCommand to use an explicit package name:作为此更改的一部分,我们还需要更新对 AddCommand 的引用以使用明确的AddCommand名称:

     func init() { cmd.RootCmd.AddCommand(fooCmd) }
  4. Lastly, we need to import the subcommands somewhere.最后,我们需要在某处导入子命令。 Right now we never import the foo or bar packages, so the code is effectively invisible (try introducing an egregious syntax error in one of those files -- you'll see that go build will succeed because those files aren't referenced anywhere).现在我们从不导入foobar包,因此代码实际上是不可见的(尝试在其中一个文件中引入严重的语法错误——您会看到go build将成功,因为这些文件未在任何地方引用)。

    We can do this in our top level main.go file:我们可以在我们的顶级main.go文件中执行此操作:

     package main import ( "example/cmd" _ "example/cmd/bar" _ "example/cmd/foo" ) func main() { cmd.Execute() }

Now if we build and run the code, we see that our foo and bar commands are available:现在,如果我们构建并运行代码,我们会看到我们的foobar命令可用:

$ go build
$ ./example
A longer description that spans multiple lines and likely contains
examples and usage of using your application. For example:

Cobra is a CLI library for Go that empowers applications.
This application is a tool to generate the needed files
to quickly create a Cobra application.

Usage:
  example [command]

Available Commands:
  bar         A brief description of your command
  completion  Generate the autocompletion script for the specified shell
  foo         A brief description of your command
  help        Help about any command

Flags:
  -h, --help     help for example
  -t, --toggle   Help message for toggle

Use "example [command] --help" for more information about a command.

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM