Skip to content

A simple Go library for executing external commands with flexible I/O control, timeout support, and cross-platform shell integration

License

Notifications You must be signed in to change notification settings

seungyeop-lee/easycmd

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 

History

22 Commits
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

easycmd

Go ์–ธ์–ด๋กœ ์ž‘์„ฑ๋œ ๋ช…๋ น์–ด ์‹คํ–‰ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ž…๋‹ˆ๋‹ค. ์™ธ๋ถ€ ๋ช…๋ น์–ด๋ฅผ ๊ฐ„ํŽธํ•˜๊ฒŒ ์‹คํ–‰ํ•˜๊ณ  ํ‘œ์ค€ ์ž…์ถœ๋ ฅ์„ ์ œ์–ดํ•  ์ˆ˜ ์žˆ๋Š” ๋ž˜ํผ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

์„ค์น˜

go get github.com/seungyeop-lee/easycmd

๊ธฐ๋ณธ ์‚ฌ์šฉ๋ฒ•

๊ฐ„๋‹จํ•œ ๋ช…๋ น์–ด ์‹คํ–‰

package main

import (
    "github.com/seungyeop-lee/easycmd"
)

func main() {
    cmd := easycmd.New()
    err := cmd.Run("echo hello world")
    if err != nil {
        panic(err)
    }
}

ํ‘œ์ค€ ์ถœ๋ ฅ ์บก์ฒ˜

package main

import (
    "bytes"
    "fmt"
    "github.com/seungyeop-lee/easycmd"
)

func main() {
    out := &bytes.Buffer{}
    cmd := easycmd.New(easycmd.WithStdOut(out))

    err := cmd.Run("echo hello world")
    if err != nil {
        panic(err)
    }

    fmt.Println("์ถœ๋ ฅ:", out.String()) // ์ถœ๋ ฅ: hello world
}

๊ณ ๊ธ‰ ์‚ฌ์šฉ๋ฒ•

Shell ๋ช…๋ น์–ด ์‹คํ–‰

cmd := easycmd.New()

// bash๋กœ ๋ž˜ํ•‘๋œ ๋ช…๋ น์–ด ์‹คํ–‰
err := cmd.RunShell("(cd .. && pwd)")

// ๋ฉ€ํ‹ฐ๋ผ์ธ shell ์Šคํฌ๋ฆฝํŠธ ์‹คํ–‰
err = cmd.RunShell(`
    pwd
    ls -al
`)

PowerShell ๋ช…๋ น์–ด ์‹คํ–‰ (Windows)

cmd := easycmd.New()
err := cmd.RunPowershell("Get-Location")

ํŠน์ • ๋””๋ ‰ํ† ๋ฆฌ์—์„œ ์‹คํ–‰

cmd := easycmd.New()

// ๊ธฐ๋ณธ ๋ช…๋ น์–ด๋ฅผ ํŠน์ • ๋””๋ ‰ํ† ๋ฆฌ์—์„œ ์‹คํ–‰
err := cmd.RunWithDir("ls", "/tmp")

// Shell ๋ช…๋ น์–ด๋ฅผ ํŠน์ • ๋””๋ ‰ํ† ๋ฆฌ์—์„œ ์‹คํ–‰
err = cmd.RunShellWithDir("pwd && ls", "/tmp")

// PowerShell ๋ช…๋ น์–ด๋ฅผ ํŠน์ • ๋””๋ ‰ํ† ๋ฆฌ์—์„œ ์‹คํ–‰
err = cmd.RunPowershellWithDir("Get-ChildItem", "C:\\temp")

์ปค์Šคํ…€ ์„ค์ •

import (
    "os"
    "strings"
    "time"
)

cmd := easycmd.New(
    easycmd.WithStdIn(strings.NewReader("input")), // ํ‘œ์ค€ ์ž…๋ ฅ ์„ค์ •
    easycmd.WithStdOut(os.Stdout),                 // ํ‘œ์ค€ ์ถœ๋ ฅ ์„ค์ •
    easycmd.WithStdErr(os.Stderr),                 // ํ‘œ์ค€ ์—๋Ÿฌ ์„ค์ •
    easycmd.WithTimeoutSeconds(30),                // ํƒ€์ž„์•„์›ƒ ์„ค์ • (30์ดˆ)
    easycmd.WithEnv([]string{"VAR=value"}),        // ํ™˜๊ฒฝ๋ณ€์ˆ˜ ์„ค์ •
)

err := cmd.Run("cat") // ํ‘œ์ค€ ์ž…๋ ฅ์—์„œ "input"์„ ์ฝ์–ด์„œ ์ถœ๋ ฅ

๋””๋ฒ„๊ทธ ๋ชจ๋“œ

import (
    "bytes"
    "fmt"
)

// ๊ธฐ๋ณธ ๋””๋ฒ„๊ทธ ๋ชจ๋“œ (๋””๋ฒ„๊ทธ ์ถœ๋ ฅ์€ stderr๋กœ)
cmd := easycmd.New(easycmd.WithDebug())
err := cmd.Run("echo hello world")

// ์ปค์Šคํ…€ ๋””๋ฒ„๊ทธ ์ถœ๋ ฅ ์ŠคํŠธ๋ฆผ
debugOut := &bytes.Buffer{}
cmd = easycmd.New(easycmd.WithDebug(debugOut))
err = cmd.Run("echo hello world")

fmt.Println("๋””๋ฒ„๊ทธ ์ถœ๋ ฅ:", debugOut.String())

ํƒ€์ž„์•„์›ƒ ์„ค์ •

// ๊ถŒ์žฅ: ์ง๊ด€์ ์ธ API ์‚ฌ์šฉ
cmd := easycmd.New(easycmd.WithTimeoutSeconds(5))    // 5์ดˆ
err := cmd.Run("sleep 3") // ์ •์ƒ ์™„๋ฃŒ

cmd = easycmd.New(easycmd.WithTimeoutMillis(1500))   // 1.5์ดˆ
err = cmd.Run("sleep 3")  // 1.5์ดˆ ํ›„ ํƒ€์ž„์•„์›ƒ

// time.Duration ์ง์ ‘ ์‚ฌ์šฉ๋„ ๊ฐ€๋Šฅ
import "time"
cmd = easycmd.New(easycmd.WithTimeout(5 * time.Second))
err = cmd.Run("sleep 10") // 5์ดˆ ํ›„ ํƒ€์ž„์•„์›ƒ

if err != nil {
    fmt.Printf("๋ช…๋ น์–ด ์‹คํ–‰ ์‹คํŒจ: %v\n", err)
}

โš ๏ธ ์ฃผ์˜์‚ฌํ•ญ: WithTimeout()์— ์ˆซ์ž๋งŒ ์ „๋‹ฌํ•˜๋ฉด ๋‚˜๋…ธ์ดˆ ๋‹จ์œ„๋กœ ํ•ด์„๋ฉ๋‹ˆ๋‹ค!

// โŒ ์ž˜๋ชป๋œ ์‚ฌ์šฉ๋ฒ•
cmd := easycmd.New(easycmd.WithTimeout(5))  // 5๋‚˜๋…ธ์ดˆ (๊ฑฐ์˜ ์ฆ‰์‹œ ํƒ€์ž„์•„์›ƒ)

// โœ… ์˜ฌ๋ฐ”๋ฅธ ์‚ฌ์šฉ๋ฒ•
cmd := easycmd.New(easycmd.WithTimeoutSeconds(5))       // 5์ดˆ
cmd := easycmd.New(easycmd.WithTimeout(5 * time.Second)) // 5์ดˆ

ํ™˜๊ฒฝ๋ณ€์ˆ˜ ์„ค์ •

cmd := easycmd.New(
    easycmd.WithEnv([]string{
        "MY_VAR=hello",
        "ANOTHER_VAR=world",
    }),
)

// ํ™˜๊ฒฝ๋ณ€์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์‰˜ ๋ช…๋ น์–ด ์‹คํ–‰
err := cmd.RunShell("echo $MY_VAR $ANOTHER_VAR")

๋ณตํ•ฉ ์„ค์ • ์‚ฌ์šฉ

import (
    "bytes"
    "strings"
    "time"
)

out := &bytes.Buffer{}
debugOut := &bytes.Buffer{}
input := strings.NewReader("test input\n")

cmd := easycmd.New(
    easycmd.WithStdIn(input),
    easycmd.WithStdOut(out),
    easycmd.WithStdErr(os.Stderr),
    easycmd.WithDebug(debugOut),
    easycmd.WithTimeoutSeconds(10),                 // 10์ดˆ ํƒ€์ž„์•„์›ƒ
    easycmd.WithEnv([]string{"LANG=ko_KR.UTF-8"}),
)

err := cmd.RunShell("cat && echo 'processing...' >&2")

API ๋ ˆํผ๋Ÿฐ์Šค

์ฃผ์š” ๋ฉ”์„œ๋“œ

  • New(configApplies ...configApply) *Cmd: ์ƒˆ๋กœ์šด Cmd ์ธ์Šคํ„ด์Šค ์ƒ์„ฑ
  • Run(commandStr string) error: ๊ธฐ๋ณธ ๋ช…๋ น์–ด ์‹คํ–‰
  • RunShell(commandStr string) error: bash๋กœ ๋ž˜ํ•‘๋œ ๋ช…๋ น์–ด ์‹คํ–‰
  • RunPowershell(commandStr string) error: PowerShell๋กœ ๋ž˜ํ•‘๋œ ๋ช…๋ น์–ด ์‹คํ–‰
  • RunWithDir(commandStr string, runDirStr string) error: ํŠน์ • ๋””๋ ‰ํ† ๋ฆฌ์—์„œ ๊ธฐ๋ณธ ๋ช…๋ น์–ด ์‹คํ–‰
  • RunShellWithDir(commandStr string, runDirStr string) error: ํŠน์ • ๋””๋ ‰ํ† ๋ฆฌ์—์„œ Shell ๋ช…๋ น์–ด ์‹คํ–‰
  • RunPowershellWithDir(commandStr string, runDirStr string) error: ํŠน์ • ๋””๋ ‰ํ† ๋ฆฌ์—์„œ PowerShell ๋ช…๋ น์–ด ์‹คํ–‰

์„ค์ • ํ•จ์ˆ˜

  • WithStdIn(reader io.Reader) configApply: ํ‘œ์ค€ ์ž…๋ ฅ ์„ค์ •
  • WithStdOut(writer io.Writer) configApply: ํ‘œ์ค€ ์ถœ๋ ฅ ์„ค์ •
  • WithStdErr(writer io.Writer) configApply: ํ‘œ์ค€ ์—๋Ÿฌ ์„ค์ •
  • WithDebug(debugOut ...io.Writer) configApply: ๋””๋ฒ„๊ทธ ๋ชจ๋“œ ํ™œ์„ฑํ™” ๋ฐ ๋””๋ฒ„๊ทธ ์ถœ๋ ฅ ์ŠคํŠธ๋ฆผ ์„ค์ •
  • WithTimeout(timeout time.Duration) configApply: ๋ช…๋ น์–ด ์‹คํ–‰ ํƒ€์ž„์•„์›ƒ ์„ค์ • (time.Duration)
  • WithTimeoutSeconds(seconds int) configApply: ๋ช…๋ น์–ด ์‹คํ–‰ ํƒ€์ž„์•„์›ƒ ์„ค์ • (์ดˆ ๋‹จ์œ„) โญ ๊ถŒ์žฅ
  • WithTimeoutMillis(millis int) configApply: ๋ช…๋ น์–ด ์‹คํ–‰ ํƒ€์ž„์•„์›ƒ ์„ค์ • (๋ฐ€๋ฆฌ์ดˆ ๋‹จ์œ„) โญ ๊ถŒ์žฅ
  • WithEnv(env []string) configApply: ํ™˜๊ฒฝ๋ณ€์ˆ˜ ์„ค์ •

๋””๋ฒ„๊ทธ ๋ชจ๋“œ ์ถœ๋ ฅ ๋‚ด์šฉ

๋””๋ฒ„๊ทธ ๋ชจ๋“œ๊ฐ€ ํ™œ์„ฑํ™”๋˜๋ฉด ๋‹ค์Œ ์ •๋ณด๋“ค์ด ์ถœ๋ ฅ๋ฉ๋‹ˆ๋‹ค:

  • ํŒŒ์‹ฑ๋œ ๋ช…๋ น์–ด ๋ฌธ์ž์—ด
  • ์‹คํ–‰๋  ๋ช…๋ น์–ด ์ด๋ฆ„
  • ๋ช…๋ น์–ด ์ธ์ˆ˜ ๋ฐฐ์—ด
  • ์‹คํ–‰ ๋””๋ ‰ํ† ๋ฆฌ (์„ค์ •๋œ ๊ฒฝ์šฐ)
  • ํƒ€์ž„์•„์›ƒ ์„ค์ • (์„ค์ •๋œ ๊ฒฝ์šฐ)
  • ํ™˜๊ฒฝ๋ณ€์ˆ˜ ๊ฐœ์ˆ˜ (์„ค์ •๋œ ๊ฒฝ์šฐ)
  • ๋ช…๋ น์–ด ์‹คํ–‰ ์‹œ์ž‘/์™„๋ฃŒ/์‹คํŒจ ๋ฉ”์‹œ์ง€
  • ๋ช…๋ น์–ด ์‹คํ–‰ ์‹œ๊ฐ„ ์ธก์ •

์—๋Ÿฌ ์ฒ˜๋ฆฌ

cmd := easycmd.New()
err := cmd.Run("invalid-command")
if err != nil {
    fmt.Printf("๋ช…๋ น์–ด ์‹คํ–‰ ์‹คํŒจ: %v\n", err)
}

// ๋นˆ ๋ช…๋ น์–ด์— ๋Œ€ํ•œ ํŠน๋ณ„ํ•œ ์—๋Ÿฌ
if errors.Is(err, easycmd.EmptyCmdError) {
    fmt.Println("๋นˆ ๋ช…๋ น์–ด์ž…๋‹ˆ๋‹ค")
}

ํƒ€์ž„์•„์›ƒ ์—๋Ÿฌ ์œ ํ˜•

ํƒ€์ž„์•„์›ƒ ์„ค์ • ์‹œ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋Š” ๋‘ ๊ฐ€์ง€ ์—๋Ÿฌ ์œ ํ˜•:

cmd := easycmd.New(easycmd.WithTimeoutMillis(1)) // ๋งค์šฐ ์งง์€ ํƒ€์ž„์•„์›ƒ
err := cmd.Run("sleep 1")

if err != nil {
    errMsg := err.Error()

    if strings.Contains(errMsg, "๋ช…๋ น์–ด ์‹œ์ž‘ ์‹คํŒจ") {
        fmt.Println("๋ช…๋ น์–ด ์‹œ์ž‘ ์ „ ํƒ€์ž„์•„์›ƒ ๋ฐœ์ƒ")
    } else if strings.Contains(errMsg, "๋ช…๋ น์–ด ์‹คํ–‰ ํƒ€์ž„์•„์›ƒ") {
        fmt.Println("๋ช…๋ น์–ด ์‹คํ–‰ ์ค‘ ํƒ€์ž„์•„์›ƒ ๋ฐœ์ƒ")
    }
}

๋ผ์ด์„ ์Šค

์ด ํ”„๋กœ์ ํŠธ๋Š” Apache License 2.0 ํ•˜์— ๋ฐฐํฌ๋ฉ๋‹ˆ๋‹ค. ์ž์„ธํ•œ ๋‚ด์šฉ์€ LICENSE ํŒŒ์ผ์„ ์ฐธ์กฐํ•˜์„ธ์š”.

About

A simple Go library for executing external commands with flexible I/O control, timeout support, and cross-platform shell integration

Resources

License

Stars

Watchers

Forks

Languages