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

aTo*_*Tom 9 go go-cobra

我是 Go 初学者,我正在尝试使用Cobra创建 CLI 。为了引导该项目,我使用了Cobra Generator,生成了一个命令,一个子命令,一切正常。

\n

我现在有这种类型的布局:

\n
cli\n\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 cmd\n\xe2\x94\x82\xc2\xa0\xc2\xa0 \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 command.go\n\xe2\x94\x82\xc2\xa0\xc2\xa0 \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 root.go\n\xe2\x94\x82\xc2\xa0\xc2\xa0 \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 subcommand.go\n\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 go.mod\n\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 go.sum\n\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 main.go\n
Run Code Online (Sandbox Code Playgroud)\n

这不适合我,假设我的项目打算有很多命令,或者很多命令命名空间,每个命令都由不同的团队拥有,它会变得非常混乱并且难以维护。我更喜欢这样的布局:

\n
cli\n\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 cmd\n\xe2\x94\x82\xc2\xa0\xc2\xa0 \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 command\n\xe2\x94\x82\xc2\xa0\xc2\xa0 \xe2\x94\x82\xc2\xa0\xc2\xa0 \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 command.go\n\xe2\x94\x82\xc2\xa0\xc2\xa0 \xe2\x94\x82\xc2\xa0\xc2\xa0 \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 subcommand.go\n\xe2\x94\x82\xc2\xa0\xc2\xa0 \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 root.go\n\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 go.mod\n\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 go.sum\n\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 main.go\n
Run Code Online (Sandbox Code Playgroud)\n

现在,我对 Go 中的包和导入的完成方式缺乏一些了解(即使在阅读了这里那里的文档之后文档之后),但据我所知,只要它们属于多个 go 源文件,就可以跨多个源文件访问资源同一个包。但正如文档中所述,“实现可能要求包的所有源文件都位于同一目录中。”,因此要实现我想要的布局,我需要有多个包,例如每个命令一个包命名空间,我认为这很好(或者至少,我不明白这样做有什么问题)。这就是我尝试过的:

\n
    \n
  • 在里面创建一个command目录cmd创建
  • \n
  • command.go文件移动到command到目录
  • \n
  • package条款从command.gocommand
  • \n
  • 做同样的事subcommand.go
  • \n
\n

这构建得很好,但是找不到命令(Error: unknown command "command" for "cli")。我认为这是因为我从未导入过那个新command包,所以我将其导入到main.go文件中,其中cmd导入包的

\n

构建失败,undefined: rootCmdcommand.go文件中告诉我。我猜想该command包无法从该包中看到资源cmd,因此我将cmd包导入到文件中command.go,并替换rootCmdcmd.rootCmdrootCmd作为文件中创建的变量cli/cmd/root.go,Cobra Generator 提供的文件的一部分)。这次我确实抱有希望,但结果还是一样(undefined: cmd.rootCmd),现在我迷失了。

\n

我是否正在尝试做一些 Cobra 做不到的事情?

\n

我是否正在尝试做一些可以用 Cobra 实现但不使用 Cobra 生成器的事情?

\n

我是否试图做一些根本不应该做的事情(比如 Go 试图保护我免受不良设计的影响)?

\n

如果没有,我应该如何进行?

\n

lar*_*sks 24

您无法使用cobra-cli命令获得所需的布局,但您当然可以手动进行设置。让我们从cobra-cli布局开始:

\n
$ go mod init example\n$ cobra-cli init\n
Run Code Online (Sandbox Code Playgroud)\n

并添加几个命令:

\n
$ cobra-cli add foo\n$ cobra-cli add bar\n
Run Code Online (Sandbox Code Playgroud)\n

这让我们:

\n
.\n\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 cmd\n\xe2\x94\x82\xc2\xa0\xc2\xa0 \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 bar.go\n\xe2\x94\x82\xc2\xa0\xc2\xa0 \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 foo.go\n\xe2\x94\x82\xc2\xa0\xc2\xa0 \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 root.go\n\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 go.mod\n\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 go.sum\n\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 LICENSE\n\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 main.go\n
Run Code Online (Sandbox Code Playgroud)\n

让我们首先将命令移动到子目录中,这样我们就有了所需的布局。这让我们:

\n
.\n\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 cmd\n\xe2\x94\x82\xc2\xa0\xc2\xa0 \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 bar\n\xe2\x94\x82\xc2\xa0\xc2\xa0 \xe2\x94\x82\xc2\xa0\xc2\xa0 \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 bar.go\n\xe2\x94\x82\xc2\xa0\xc2\xa0 \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 foo\n\xe2\x94\x82\xc2\xa0\xc2\xa0 \xe2\x94\x82\xc2\xa0\xc2\xa0 \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 foo.go\n\xe2\x94\x82\xc2\xa0\xc2\xa0 \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 root.go\n\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 go.mod\n\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 go.sum\n\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 LICENSE\n\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 main.go\n
Run Code Online (Sandbox Code Playgroud)\n

现在我们需要对代码进行一些更改才能使其正常工作。

\n
    \n
  1. 因为我们的子命令现在位于单独的包中,所以我们需要重命名rootCmd以便可以导出。

    \n
    find cmd -name '*.go' -print | xargs sed -i 's/rootCmd/RootCmd/g'\n
    Run Code Online (Sandbox Code Playgroud)\n
  2. \n
  3. 我们需要每个子命令位于其自己的包中,因此我们将package cmd顶部的 替换为cmd/foo/foo.go,package foo并替换为package barin cmd/bar/bar.go

    \n
  4. \n
  5. 我们需要子命令来导入 root 命令,以便它们可以调用RootCmd.AddCommand. 这意味着我们cmd/foo/foo.go需要cmd/bar/bar.go添加example/cmd到我们的部分(回想一下,我们通过命令import命名了顶级包)。这意味着每个文件将以:examplego mod init

    \n
    package foo\n\nimport (\n        "fmt"\n\n        "example/cmd"\n        "github.com/spf13/cobra"\n)\n
    Run Code Online (Sandbox Code Playgroud)\n

    作为此更改的一部分,我们还需要更新引用以AddCommand使用显式包名称:

    \n
    func init() {\n  cmd.RootCmd.AddCommand(fooCmd)\n}\n
    Run Code Online (Sandbox Code Playgroud)\n
  6. \n
  7. 最后,我们需要将子命令导入到某个地方。现在我们从不导入foobar包,因此代码实际上是不可见的(尝试在这些文件之一中引入严重的语法错误 - 您会发现这go build会成功,因为这些文件在任何地方都没有引用)。

    \n

    我们可以在顶级main.go文件中执行此操作:

    \n
    package main\n\nimport (\n  "example/cmd"\n  _ "example/cmd/bar"\n  _ "example/cmd/foo"\n)\n\nfunc main() {\n  cmd.Execute()\n}\n
    Run Code Online (Sandbox Code Playgroud)\n
  8. \n
\n

现在,如果我们构建并运行代码,我们会看到我们的foobar命令可用:

\n
$ go build\n$ ./example\nA longer description that spans multiple lines and likely contains\nexamples and usage of using your application. For example:\n\nCobra is a CLI library for Go that empowers applications.\nThis application is a tool to generate the needed files\nto quickly create a Cobra application.\n\nUsage:\n  example [command]\n\nAvailable Commands:\n  bar         A brief description of your command\n  completion  Generate the autocompletion script for the specified shell\n  foo         A brief description of your command\n  help        Help about any command\n\nFlags:\n  -h, --help     help for example\n  -t, --toggle   Help message for toggle\n\nUse "example [command] --help" for more information about a command.\n
Run Code Online (Sandbox Code Playgroud)\n

  • 多谢。我缺少导出 `rootCmd` 的方法,以及使用 `_ "example/cmd/foo` 而不仅仅是 `"example/cmd/foo` 正确导入命令包的方法,这会导致未使用的编译错误导入包 (3认同)
  • 您可以在[此处](https://github.com/larsks/so-example-73986477)找到此答案中引用的所有文件。 (2认同)