CLI-Anwendungen in Go mit Cobra & Viper erstellen
CLI-Entwicklung in Go mit den Frameworks Cobra und Viper
Kommandzeilen-Interface (CLI)-Anwendungen sind essentielle Werkzeuge für Entwickler, Systemadministratoren und DevOps-Professionals. Zwei Go-Bibliotheken haben sich als de facto Standard für CLI-Entwicklung in Go: Cobra für die Befehlsstruktur und Viper für das Konfigurationsmanagement.
Go hat sich als hervorragende Sprache für den Bau von CLI-Tools erwiesen, dank seiner Leistung, einfachen Bereitstellung und plattformübergreifenden Unterstützung.

Warum Go für CLI-Anwendungen wählen
Go bietet überzeugende Vorteile für die CLI-Entwicklung:
- Einzelne Binärverteilung: Keine Laufzeitabhängigkeiten oder Paketmanager erforderlich
- Schnelle Ausführung: Native Kompilierung bietet hervorragende Leistung
- Plattformübergreifende Unterstützung: Einfache Kompilierung für Linux, macOS, Windows und mehr
- Starke Standardbibliothek: Reichhaltige Werkzeuge für Datei-I/O, Netzwerk und Textverarbeitung
- Konkurrrenzfähigkeit: Eingebaute Goroutines für parallele Operationen
- Statische Typisierung: Fehler werden zur Kompilierzeit erkannt
Beliebte CLI-Tools, die mit Go erstellt wurden, umfassen Docker, Kubernetes (kubectl), Hugo, Terraform und GitHub CLI. Wenn Sie neu in Go sind oder eine schnelle Referenz benötigen, werfen Sie einen Blick auf unseren Go Cheat Sheet für wesentliche Go-Syntax und Muster.
Einführung in Cobra
Cobra ist eine Bibliothek, die eine einfache Schnittstelle für die Erstellung leistungsfähiger moderner CLI-Anwendungen bietet. Erstellt von Steve Francia (spf13), dem gleichen Autor hinter Hugo und Viper, wird Cobra in vielen der beliebtesten Go-Projekten verwendet.
Wichtige Cobra-Funktionen
Befehlsstruktur: Cobra implementiert das Befehlsmuster, das es Ihnen ermöglicht, Anwendungen mit Befehlen und Unterbefehlen (wie git commit oder docker run) zu erstellen.
Flag-Verarbeitung: Sowohl lokale als auch dauerhafte Flags mit automatischer Analyse und Typumwandlung.
Automatische Hilfe: Generiert Hilfetexte und Nutzungsinformationen automatisch.
Intelligente Vorschläge: Bietet Vorschläge, wenn Benutzer Tippfehler machen (“Meinten Sie ‘status’?”).
Shell-Vervollständigungen: Generiert Vervollständigungsskripts für bash, zsh, fish und PowerShell.
Flexible Ausgabe: Funktioniert nahtlos mit benutzerdefinierten Formatierern und Ausgabestilen.
Einführung in Viper
Viper ist eine vollständige Konfigurationslösung für Go-Anwendungen, die speziell für die nahtlose Zusammenarbeit mit Cobra entwickelt wurde. Es verwaltet Konfigurationen aus mehreren Quellen mit einer klaren Prioritätsreihenfolge.
Wichtige Viper-Funktionen
Mehrere Konfigurationsquellen:
- Konfigurationsdateien (JSON, YAML, TOML, HCL, INI, envfile, Java-Eigenschaften)
- Umgebungsvariablen
- Befehlszeilenflags
- Remote-Konfigurationssysteme (etcd, Consul)
- Standardwerte
Konfigurationsprioritätsreihenfolge:
- Explizite Aufrufe von Set
- Befehlszeilenflags
- Umgebungsvariablen
- Konfigurationsdatei
- Schlüssel-Wert-Speicher
- Standardwerte
Live-Überwachung: Überwacht Konfigurationsdateien und lädt automatisch bei Änderungen neu.
Typumwandlung: Automatische Umwandlung in verschiedene Go-Typen (String, int, bool, Dauer, usw.).
Erste Schritte: Installation
Zuerst initialisieren Sie ein neues Go-Modul und installieren Sie beide Bibliotheken:
go mod init myapp
go get -u github.com/spf13/cobra@latest
go get -u github.com/spf13/viper@latest
Optional installieren Sie den Cobra-CLI-Generator für das Gerüst:
go install github.com/spf13/cobra-cli@latest
Aufbau Ihrer ersten CLI-Anwendung
Lassen Sie uns ein praktisches Beispiel erstellen: ein Aufgabenverwaltungs-CLI-Tool mit Konfigurationsunterstützung.
Projektstruktur
mytasks/
├── cmd/
│ ├── root.go
│ ├── add.go
│ ├── list.go
│ └── complete.go
├── config/
│ └── config.go
├── main.go
└── config.yaml
Haupteinstiegspunkt
// main.go
package main
import "mytasks/cmd"
func main() {
cmd.Execute()
}
Root-Befehl mit Viper-Integration
// cmd/root.go
package cmd
import (
"fmt"
"os"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
var cfgFile string
var rootCmd = &cobra.Command{
Use: "mytasks",
Short: "Ein einfaches Aufgabenverwaltungs-CLI",
Long: `MyTasks ist ein CLI-Aufgabenmanager, der Ihnen hilft,
Ihre täglichen Aufgaben einfach zu organisieren. Erstellt mit Cobra und Viper.`,
}
func Execute() {
if err := rootCmd.Execute(); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}
func init() {
cobra.OnInitialize(initConfig)
rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "",
"Konfigurationsdatei (Standard ist $HOME/.mytasks.yaml)")
rootCmd.PersistentFlags().String("db",
"Datenbankdatei-Speicherort")
viper.BindPFlag("database", rootCmd.PersistentFlags().Lookup("db"))
}
func initConfig() {
if cfgFile != "" {
viper.SetConfigFile(cfgFile)
} else {
home, err := os.UserHomeDir()
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
viper.AddConfigPath(home)
viper.AddConfigPath(".")
viper.SetConfigType("yaml")
viper.SetConfigName(".mytasks")
}
viper.SetEnvPrefix("MYTASKS")
viper.AutomaticEnv()
if err := viper.ReadInConfig(); err == nil {
fmt.Println("Verwendung der Konfigurationsdatei:", viper.ConfigFileUsed())
}
}
Hinzufügen von Unterbefehlen
// cmd/add.go
package cmd
import (
"fmt"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
var priority string
var addCmd = &cobra.Command{
Use: "add [Aufgabenbeschreibung]",
Short: "Fügt eine neue Aufgabe hinzu",
Args: cobra.MinimumNArgs(1),
Run: func(cmd *cobra.Command, args []string) {
task := args[0]
db := viper.GetString("database")
fmt.Printf("Aufgabe hinzufügen: %s\n", task)
fmt.Printf("Priorität: %s\n", priority)
fmt.Printf("Datenbank: %s\n", db)
// Hier würden Sie die tatsächliche Aufgabenverwaltung implementieren
},
}
func init() {
rootCmd.AddCommand(addCmd)
addCmd.Flags().StringVarP(&priority, "priority", "p", "medium",
"Aufgabenpriorität (niedrig, mittel, hoch)")
}
List-Befehl
// cmd/list.go
package cmd
import (
"fmt"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
var showCompleted bool
var listCmd = &cobra.Command{
Use: "list",
Short: "Listet alle Aufgaben auf",
Run: func(cmd *cobra.Command, args []string) {
db := viper.GetString("database")
fmt.Printf("Aufgaben auflisten von: %s\n", db)
fmt.Printf("Abgeschlossene Aufgaben anzeigen: %v\n", showCompleted)
// Hier würden Sie die tatsächliche Aufgabenliste implementieren
},
}
func init() {
rootCmd.AddCommand(listCmd)
listCmd.Flags().BoolVarP(&showCompleted, "completed", "c", false,
"Abgeschlossene Aufgaben anzeigen")
}
Implementierung der Datenpersistenz
Für ein Produktionsaufgabenverwaltungs-CLI müssen Sie die tatsächliche Datenspeicherung implementieren. Während wir hier einen einfachen Datenbankpfad verwenden, haben Sie mehrere Optionen zur Persistenz von Daten:
- SQLite: Leichtgewichtige, serverlose Datenbank, perfekt für CLI-Tools
- PostgreSQL/MySQL: Vollständige Datenbanken für komplexere Anwendungen
- JSON/YAML-Dateien: Einfache dateibasierte Speicherung für leichte Anforderungen
- Eingebettete Datenbanken: BoltDB, BadgerDB für Schlüssel-Wert-Speicherung
Wenn Sie mit relationalen Datenbanken wie PostgreSQL arbeiten, möchten Sie wahrscheinlich ein ORM oder einen Abfragegenerator verwenden. Für einen umfassenden Vergleich von Go-Datenbankbibliotheken sehen Sie sich unsere Anleitung an Vergleich von Go-ORMs für PostgreSQL: GORM vs Ent vs Bun vs sqlc.
Fortgeschrittene Konfiguration mit Viper
Beispiel für Konfigurationsdatei
# .mytasks.yaml
database: ~/.mytasks.db
format: table
colors: true
notifications:
enabled: true
sound: true
priorities:
high: red
medium: yellow
low: green
Lesen verschachtelter Konfiguration
func getNotificationSettings() {
enabled := viper.GetBool("notifications.enabled")
sound := viper.GetBool("notifications.sound")
fmt.Printf("Benachrichtigungen aktiviert: %v\n", enabled)
fmt.Printf("Ton aktiviert: %v\n", sound)
}
func getPriorityColors() map[string]string {
return viper.GetStringMapString("priorities")
}
Umgebungsvariablen
Viper liest automatisch Umgebungsvariablen mit dem konfigurierten Präfix:
export MYTASKS_DATABASE=/tmp/tasks.db
export MYTASKS_NOTIFICATIONS_ENABLED=false
mytasks list
Live-Konfigurationsneuladung
func watchConfig() {
viper.WatchConfig()
viper.OnConfigChange(func(e fsnotify.Event) {
fmt.Println("Konfigurationsdatei geändert:", e.Name)
// Konfigurationsabhängige Komponenten neu laden
})
}
Best Practices
1. Verwenden Sie den Cobra-Generator für Konsistenz
cobra-cli init
cobra-cli add serve
cobra-cli add create
2. Organisieren Sie Befehle in separaten Dateien
Halten Sie jeden Befehl in seiner eigenen Datei unter dem cmd/-Verzeichnis für die Wartbarkeit.
3. Nutzen Sie dauerhafte Flags
Verwenden Sie dauerhafte Flags für Optionen, die auf alle Unterbefehle zutreffen:
rootCmd.PersistentFlags().StringP("output", "o", "text",
"AusgabefORMAT (text, json, yaml)")
4. Implementieren Sie eine ordnungsgemäße Fehlerbehandlung
var rootCmd = &cobra.Command{
Use: "myapp",
Short: "Meine Anwendung",
RunE: func(cmd *cobra.Command, args []string) error {
if err := doSomething(); err != nil {
return fmt.Errorf("Fehler beim Ausführen von etwas: %w", err)
}
return nil
},
}
5. Bieten Sie sinnvolle Hilfetexte an
var cmd = &cobra.Command{
Use: "deploy [Umgebung]",
Short: "Anwendung in der angegebenen Umgebung bereitstellen",
Long: `Bereitstellen baut und stellt Ihre Anwendung in der
angegebenen Umgebung bereit. Unterstützte Umgebungen sind:
- Entwicklung (dev)
- Staging
- Produktion (prod)`,
Example: ` myapp deploy staging
myapp deploy production --version=1.2.3`,
}
6. Setzen Sie sinnvolle Standardwerte
func init() {
viper.SetDefault("port", 8080)
viper.SetDefault("timeout", "30s")
viper.SetDefault("retries", 3)
}
7. Validieren Sie die Konfiguration
func validateConfig() error {
port := viper.GetInt("port")
if port < 1024 || port > 65535 {
return fmt.Errorf("ungültiger Port: %d", port)
}
return nil
}
Testen von CLI-Anwendungen
Testen von Befehlen
// cmd/root_test.go
package cmd
import (
"bytes"
"testing"
)
func TestRootCommand(t *testing.T) {
cmd := rootCmd
b := bytes.NewBufferString("")
cmd.SetOut(b)
cmd.SetArgs([]string{"--help"})
if err := cmd.Execute(); err != nil {
t.Fatalf("command execution failed: %v", err)
}
}
Testen mit verschiedenen Konfigurationen
func TestWithConfig(t *testing.T) {
viper.Set("database", "/tmp/test.db")
viper.Set("debug", true)
// Führen Sie Ihre Tests aus
viper.Reset() // Aufräumen
}
Generieren von Shell-Vervollständigungen
Cobra kann Vervollständigungsskripte für verschiedene Shells generieren:
// cmd/completion.go
package cmd
import (
"os"
"github.com/spf13/cobra"
)
var completionCmd = &cobra.Command{
Use: "completion [bash|zsh|fish|powershell]",
Short: "Generiert ein Vervollständigungsskript",
Long: `Um Vervollständigungen zu laden:
Bash:
$ source <(mytasks completion bash)
Zsh:
$ source <(mytasks completion zsh)
Fish:
$ mytasks completion fish | source
PowerShell:
PS> mytasks completion powershell | Out-String | Invoke-Expression
`,
DisableFlagsInUseLine: true,
ValidArgs: []string{"bash", "zsh", "fish", "powershell"},
Args: cobra.ExactValidArgs(1),
Run: func(cmd *cobra.Command, args []string) {
switch args[0] {
case "bash":
cmd.Root().GenBashCompletion(os.Stdout)
case "zsh":
cmd.Root().GenZshCompletion(os.Stdout)
case "fish":
cmd.Root().GenFishCompletion(os.Stdout, true)
case "powershell":
cmd.Root().GenPowerShellCompletionWithDesc(os.Stdout)
}
},
}
func init() {
rootCmd.AddCommand(completionCmd)
}
Bauen und Verteilung
Bauen für mehrere Plattformen
# Linux
GOOS=linux GOARCH=amd64 go build -o mytasks-linux-amd64
# macOS
GOOS=darwin GOARCH=amd64 go build -o mytasks-darwin-amd64
GOOS=darwin GOARCH=arm64 go build -o mytasks-darwin-arm64
# Windows
GOOS=windows GOARCH=amd64 go build -o mytasks-windows-amd64.exe
Reduzieren der Binärdateigröße
go build -ldflags="-s -w" -o mytasks
Hinzufügen von Versionsinformationen
// cmd/version.go
package cmd
import (
"fmt"
"github.com/spf13/cobra"
)
var (
Version = "dev"
Commit = "none"
BuildTime = "unknown"
)
var versionCmd = &cobra.Command{
Use: "version",
Short: "Gibt Versionsinformationen aus",
Run: func(cmd *cobra.Command, args []string) {
fmt.Printf("Version: %s\n", Version)
fmt.Printf("Commit: %s\n", Commit)
fmt.Printf("Built: %s\n", BuildTime)
},
}
func init() {
rootCmd.AddCommand(versionCmd)
}
Mit Versionsinformationen bauen:
go build -ldflags="-X 'mytasks/cmd.Version=1.0.0' \
-X 'mytasks/cmd.Commit=$(git rev-parse HEAD)' \
-X 'mytasks/cmd.BuildTime=$(date -u +%Y-%m-%dT%H:%M:%SZ)'" \
-o mytasks
Praxisbeispiele
Beliebte Open-Source-Tools, die mit Cobra und Viper erstellt wurden:
- kubectl: Kubernetes-Befehlszeilen-Tool
- Hugo: Statische Website-Generator
- GitHub CLI (gh): Offizielles GitHub-CLI
- Docker CLI: Container-Verwaltung
- Helm: Kubernetes-Paketmanager
- Skaffold: Kubernetes-Entwicklungs-Workflow-Tool
- Cobra CLI: Selbsthosting - Cobra verwendet sich selbst!
Über traditionelle DevOps-Tools hinaus erstrecken sich die CLI-Fähigkeiten von Go auf KI- und maschinelles Lernen. Wenn Sie daran interessiert sind, CLI-Tools zu erstellen, die mit großen Sprachmodellen interagieren, werfen Sie einen Blick auf unseren Leitfaden zu Einschränkung von LLMs mit strukturierten Ausgaben unter Verwendung von Ollama und Go, der zeigt, wie man Go-Anwendungen erstellt, die mit KI-Modellen arbeiten.
Nützliche Ressourcen
- Cobra GitHub Repository
- Viper GitHub Repository
- Cobra Benutzerhandbuch
- Viper Dokumentation
- Go CLI-Anwendungs-Best Practices
Fazit
Cobra und Viper bieten zusammen eine leistungsstarke Grundlage für die Erstellung professioneller CLI-Anwendungen in Go. Cobra verwaltet die Befehlsstruktur, Flag-Analyse und Hilfegenerierung, während Viper die Konfiguration aus mehreren Quellen mit intelligenter Priorisierung verwaltet.
Diese Kombination ermöglicht es Ihnen, CLI-Tools zu erstellen, die:
- Einfach zu bedienen mit intuitiven Befehlen und automatischer Hilfe
- Flexibel mit mehreren Konfigurationsquellen
- Professionell mit Shell-Vervollständigungen und korrekter Fehlerbehandlung
- Wartbar mit sauberer Code-Organisation
- Plattformübergreifend auf verschiedenen Systemen
Egal, ob Sie Entwicklertools, Systemutilities oder DevOps-Automatisierung erstellen, Cobra und Viper bieten die solide Grundlage, die Sie benötigen, um CLI-Anwendungen zu erstellen, die die Benutzer lieben werden.