ในโลกของการพัฒนาซอฟต์แวร์ อินเทอร์เฟซบรรทัดคำสั่ง (CLI) มีบทบาทสำคัญในการทำงานอัตโนมัติ และมอบวิธีที่สะดวกสำหรับผู้ใช้ในการโต้ตอบกับซอฟต์แวร์ผ่านคำสั่งแบบข้อความ
ในบล็อกโพสต์นี้ เราจะสำรวจวิธีสร้างเครื่องมือ CLI โดยใช้ภาษาการเขียนโปรแกรม Go โดยใช้ประโยชน์จากไลบรารียอดนิยม เช่น Cobra และ Viper

ทำไม CLI ถึงอยู่ใน GO?

Go เป็นภาษาโปรแกรมสมัยใหม่ที่ได้รับความนิยมอย่างมากในช่วงไม่กี่ปีที่ผ่านมา เนื่องจากความเรียบง่าย ประสิทธิภาพ และการสนับสนุนที่แข็งแกร่งสำหรับการทำงานพร้อมกัน Go เป็นตัวเลือกที่ยอดเยี่ยมสำหรับการสร้าง CLI เนื่องจากสามารถคอมไพล์ได้อย่างรวดเร็ว สร้างไบนารีแบบสแตนด์อโลนที่สามารถทำงานบนระบบปฏิบัติการและสถาปัตยกรรมที่หลากหลาย และไม่มีการพึ่งพาภายนอกหรือข้อกำหนดรันไทม์

การสร้าง CLI ด้วยวิธีดั้งเดิม

ก่อนที่เราจะเจาะลึกไลบรารี Cobra และ Viper เรามาสำรวจว่าเราจะสร้างเครื่องมือ CLI พื้นฐานโดยใช้ Go โดยไม่ต้องใช้ไลบรารีภายนอกได้อย่างไร ด้านล่างนี้เป็นตัวอย่างของ CLI ธรรมดาที่รับอินพุตจากผู้ใช้และดำเนินการเฉพาะตามคำสั่งที่ให้ไว้

func main() {
    command := flag.String("command", "", "Command to execute")
    flag.Parse()

    if *command == "" {
       fmt.Println("Please provide a command.")
       flag.Usage()
       return
    }

    switch *command {
    case "greet":
       fmt.Println("Hello, World!")
    case "time":
       fmt.Println("Current time:", time.Now().Format("15:04:05"))
    default:
       fmt.Println("Unknown command:", command)
    }
}

งูเข้าช่วยเหลือ…

แม้ว่าวิธีการข้างต้นใช้ได้กับ CLI แบบธรรมดา แต่ก็อาจยุ่งยากและบำรุงรักษาได้ยากเนื่องจากแอปพลิเคชันมีความซับซ้อนมากขึ้น นั่นคือจุดที่ห้องสมุดอย่าง Cobra และ Viper เข้ามาช่วยเหลือ
เพื่อให้เข้าใจการใช้งานห้องสมุดเหล่านี้ เราจะสร้างแอปพลิเคชัน todo cli ง่ายๆ ซึ่งคุณสามารถสร้าง อัปเดต ค้นหา และลบงานสิ่งที่ต้องทำได้

งูเห่า

Cobra เป็นไลบรารีที่ทรงพลังสำหรับการสร้างแอปพลิเคชัน CLI ที่ทันสมัยและเต็มไปด้วยฟีเจอร์มากมายใน Go โดยมอบเฟรมเวิร์กที่แข็งแกร่งสำหรับการสร้างคำสั่ง CLI รวมถึงการสนับสนุนคำสั่งย่อยที่ซ้อนกัน แฟล็กโกลบอลและโลคัล คำสั่งวิธีใช้อัตโนมัติ และแฟล็กที่สอดคล้องกับ POSIX อย่างสมบูรณ์ในเวอร์ชันสั้นและยาว
เริ่มต้นโครง cli สำหรับโปรเจ็กต์โดยใช้ cobra init สั่งการ.

cobra-cli init gotodo

มันจะเริ่มต้นโปรเจ็กต์ gotodo ด้วยไลบรารีงูเห่า คุณจะสังเกตเห็นว่ามันสร้างไฟล์บางไฟล์ในโปรเจ็กต์
ต่อไป เราสามารถเพิ่มคำสั่งเพิ่มเติมให้กับ CLI ของเราได้โดยใช้คำสั่ง cobra add

cobra add list

คำสั่งนี้สร้างไฟล์ใหม่ชื่อ list.go ภายใต้ cmd directory ซึ่งมีการใช้งานสำหรับคำสั่ง list เราสามารถทำซ้ำขั้นตอนนี้เพื่อเพิ่มคำสั่งเพิ่มเติมได้ตามต้องการ

การตั้งค่าคำสั่งย่อยด้วย Cobra

Cobra ช่วยให้เราสามารถสร้างคำสั่งย่อยแบบซ้อนกัน ซึ่งสามารถจัดเตรียมโครงสร้างแบบลำดับชั้นให้กับ CLI ของเราได้
ตัวอย่างเช่น เราสามารถเพิ่มคำสั่งค้นหาและคำสั่งย่อยที่เสร็จสมบูรณ์ภายใต้คำสั่งนั้นได้โดยใช้คำสั่งต่อไปนี้:

cobra add search
cobra add search completed

คำสั่งเหล่านี้สร้างไฟล์ที่จำเป็นสำหรับคำสั่งค้นหาและคำสั่งย่อยที่เสร็จสมบูรณ์ ตอนนี้เราสามารถกำหนดพฤติกรรมและการใช้งานคำสั่งเหล่านี้ในไฟล์ที่เกี่ยวข้องได้

การปักธงด้วย Cobra

แฟล็กจำเป็นสำหรับการจัดเตรียมตัวเลือกและพารามิเตอร์ให้กับคำสั่ง CLI ของเรา Cobra ให้การสนับสนุนทั้งธงท้องถิ่นและธงถาวร

แฟล็กท้องถิ่น
แฟล็กท้องถิ่นนั้นเฉพาะกับคำสั่งเดียว และไม่ใช้กับคำสั่งหรือคำสั่งย่อยอื่นใด เราสามารถกำหนดแฟล็กท้องถิ่นโดยใช้เมธอด Flags() ในคำสั่ง
ตัวอย่างเช่น หากต้องการเพิ่มการตั้งค่าสถานะคำหลักให้กับคำสั่งค้นหา เราสามารถใช้โค้ดต่อไปนี้:

searchCmd.Flags().StringP("keyword", "k", "", "Keyword for the task name to search for.")

โค้ดนี้เพิ่ม — แฟล็กคีย์เวิร์ดพร้อมชวเลข -k ให้กับคำสั่งค้นหา ช่วยให้ผู้ใช้สามารถระบุคีย์เวิร์ดที่จะค้นหาในชื่องานได้

gotodo search --keyword test

การตั้งค่าสถานะถาวร
การตั้งค่าสถานะถาวรจะถูกแชร์ระหว่างคำสั่งและคำสั่งย่อย พวกมันอนุญาตให้เรากำหนดแฟล็กที่ใช้กับคำสั่งและคำสั่งย่อยทั้งหมด เราสามารถกำหนดแฟล็กถาวรได้โดยใช้เมธอด PersistentFlags() ในคำสั่ง
ตัวอย่างเช่น หากต้องการเพิ่มแฟล็กเอาต์พุตให้กับคำสั่ง search เราสามารถใช้โค้ดต่อไปนี้:

searchCmd.PersistentFlags().StringP("output", "o", "", "Output format [table, json, yaml]")

โค้ดนี้จะเพิ่ม — แฟล็กเอาต์พุตพร้อมชวเลข -o ให้กับคำสั่งค้นหา ช่วยให้ผู้ใช้สามารถระบุรูปแบบเอาต์พุตสำหรับผลการค้นหาได้

gotodo search --output yaml

การตั้งค่าข้อโต้แย้งกับ Cobra

นอกจากแฟล็กแล้ว Cobra ยังรองรับอาร์กิวเมนต์ซึ่งเป็นพารามิเตอร์ที่ส่งผ่านไปยังคำสั่งโดยตรง อาร์กิวเมนต์มีประโยชน์สำหรับการจัดเตรียมค่าอินพุตเฉพาะที่คำสั่งต้องการ
ตัวอย่างเช่น สมมติว่าเราต้องการเพิ่มคำสั่งที่สมบูรณ์เพื่อทำเครื่องหมายงานว่าเสร็จสมบูรณ์ เราสามารถกำหนดคำสั่งนี้ด้วยอาร์กิวเมนต์ที่แสดงถึงรหัสงานที่ต้องทำให้เสร็จ ด้านล่างนี้เป็นตัวอย่างการใช้งาน:

var completeCmd = &cobra.Command{
    Use:   "complete",
    Short: "Complete a todo task",
    Long:  ``,
    Args:  cobra.ExactArgs(1),
    Run: func(cmd *cobra.Command, args []string) {
       // Reading Args 0
       taskID, err := strconv.Atoi(args[0])
       if err != nil {                     
          log.Fatal(err) 
       }

       err = service.CompleteTodoTask(taskID)
       if err != nil {
          log.Fatal(err)
       }
       fmt.Println("Task successfully completed.")
    }
}

func init() {
    rootCmd.AddCommand(completeCmd)
}

ในตัวอย่างนี้ เราตรวจสอบว่าจำนวนอาร์กิวเมนต์มีอย่างน้อยหนึ่งรายการหรือไม่ ถ้าไม่เช่นนั้น เราจะแสดงข้อความแสดงข้อผิดพลาด มิฉะนั้น เราจะเรียกอาร์กิวเมนต์แรก (รหัสงาน) และดำเนินการให้เสร็จสิ้นโดยใช้รหัสนั้น

gotodo complete 1

ไวเปอร์

Viper เป็นโซลูชันการกำหนดค่าที่ทรงพลังสำหรับแอปพลิเคชัน Go รวมถึง CLI ช่วยให้เราสามารถอ่านและจัดการการตั้งค่าแอปพลิเคชันจากรูปแบบไฟล์การกำหนดค่าต่างๆ เช่น JSON, TOML, YAML และ HCL Viper ยังรองรับการตั้งค่าเริ่มต้นและการจัดการตัวแปรสภาพแวดล้อม ทำให้มีความยืดหยุ่นและสะดวกสูง
ด้านล่างนี้คือตัวอย่างวิธีที่เราสามารถโหลดงาน JSON ที่มีอยู่โดยใช้ Viper:

var loadCmd = &cobra.Command{
    Use:   "load",
    Short: "Load the existing todo task json",
    Long:  ``,
  
    Run: func(cmd *cobra.Command, args []string) {
       var existingTasks models.Todos

       viper.SetConfigFile(jsonFile) 
       err := viper.ReadInConfig()  
       if err != nil {              
          log.Fatal(err)
       } 

       err = viper.Unmarshal(&existingTasks) 
       if err != nil {                       
          log.Fatal(err) 
       } 

       err = service.LoadExistingJSON(existingTasks)
       if err != nil {
          log.Fatal(err)
       }
    }
}

func init() {
    rootCmd.AddCommand(loadCmd)
    loadFlags := loadCmd.Flags()

    loadFlags.StringVar(&jsonFile, "jsonFile", "", "json file for existing tasks"+
       "")
    cobra.MarkFlagRequired(loadFlags, "jsonFile")
}

ในตัวอย่างนี้ เรากำหนดคำสั่ง load ที่คาดหวังแฟล็ก jsonFile ที่แสดงไฟล์ JSON เพื่อใช้โหลดงาน เราใช้ Viper เพื่อผูกค่าแฟล็กกับการตั้งค่าคอนฟิกูเรชันที่เกี่ยวข้อง (jsonFile)

gotodo load --jsonFile storage.json

บทสรุป

ในบล็อกโพสต์นี้ เราได้สำรวจกระบวนการสร้างเครื่องมือ CLI ใน Go โดยใช้ไลบรารี Cobra และ Viper เราเรียนรู้วิธีตั้งค่าคำสั่ง คำสั่งย่อย แฟล็ก และอาร์กิวเมนต์โดยใช้ Cobra ทำให้เราสามารถสร้างแอปพลิเคชัน CLI ที่ยืดหยุ่นและมีฟีเจอร์มากมาย นอกจากนี้ เรายังค้นพบวิธีที่ Viper สามารถใช้จัดการการตั้งค่าแอปพลิเคชัน มอบประสบการณ์การกำหนดค่าที่ราบรื่น

ขอบคุณสำหรับเวลาและความสนใจของคุณ Happy Coding !
ซอร์สโค้ดสำหรับตัวอย่างมีอยู่ที่ mehulgohil/gotodo