برنامه نویسی

چگونه می توان به درستی از cmd.exe در GO بدون بن بست خوانده شد؟

مقدمه

تعامل با فرآیندهای خارجی مانند cmd.exe در GO می تواند دلهره آور به نظر برسد ، به خصوص وقتی که از خواندن استاندارد می خواند (stdout) و خطای استاندارد (stderr). در این مقاله به برخی از موضوعات متداول که ممکن است با آنها روبرو شوید ، مانند بن بست و داده های بایت از دست رفته هنگام استفاده Read() عملکرد. ما راه حل های روشنی را برای مدیریت مؤثر دستورات ترمینال خود در ضمن اطمینان از اینکه برنامه GO شما برای خروجی بدون مشکل گوش می دهد ، در اختیار شما قرار می دهیم.

درک مشکل

هنگامی که لوله ها را با استفاده از GO ایجاد می کنید exec.Command()، شما باید در نظر بگیرید که چگونه می توانید خواندن همزمان از این لوله ها را مدیریت کنید. اگر یک عملیات خواندن در یک بلوک لوله به دلیل وجود داده ای وجود ندارد ، می تواند منجر به بن بست شود. این به ویژه هنگام لوله کشی هر دو مشکل ساز است stdout وت stderr همانطور که در مورد استفاده شما مشاهده می شود.

به عنوان مثال ، اگر سعی می کنید از stdout لوله ، اما هیچ داده ای در دسترس نیست ، عملیات خواندن تا زمانی که داده ها یا لوله بسته نشود مسدود می شود. اگر منطق برنامه شما برای رسیدگی به هر دو تنظیم نشده است stdout وت stderr به طور هم زمان ، شما ممکن است با وضعیت بن بست باشید.

راه حل گام به گام

برای اداره مؤثر چندین جریان ، یکی از راه حل های توصیه شده استفاده از آن است گودال برای خواندن مداوم از هر دو stdout وت stderr، اطمینان از عدم وجود مسدود کردن. در اینجا نحوه اجرای این موضوع آورده شده است:

ساختار پوسته اصلاح شده

ابتدا اطمینان حاصل کنید که واردات لازم را دارید:

import (
    "bufio"
    "fmt"
    "io"
    "os"
    "os/exec"
    "time"
)

این ابزارهای لازم برای کار با دستورات و بافرهای خروجی را به طور مؤثر فراهم می کند.

عملکرد Startshell

در شما StartShell عملکرد ، می توانید گوروهای اضافی اضافه کنید تا هر دو جریان خروجی را به صورت غیر همزمان کنترل کنید:

func StartShell() (*Shell, error) {
    c := exec.Command("cmd.exe")
    stdin, err := c.StdinPipe()
    if err != nil {
        return nil, err
    }
    stdout, err := c.StdoutPipe()
    if err != nil {
        return nil, err
    }
    stderr, err := c.StderrPipe()
    if err != nil {
        return nil, err
    }

    err = c.Start()
    if err != nil {
        return nil, err
    }

    s := Shell{c.Process.Pid, stdin, stdout, stderr}
    go s.readOutput()  // Start reading stdout
    go s.readError()   // Start reading stderr

    // Sleep to give time to shell to start
    time.Sleep(100 * time.Millisecond)

    return &s, nil
}

عملکردهای بهتر خواندن

اصلاح Shell ساختار شامل بافر برای ذخیره خروجی:

type Shell struct {
    Pid       int
    StdinPipe io.WriteCloser
    StdoutPipe io.ReadCloser
    StderrPipe io.ReadCloser
    stdoutBuf []byte
    stderrBuf []byte
}

سپس توابع خواندن را پیاده سازی کنید:

func (s *Shell) readOutput() {
    scanner := bufio.NewScanner(s.StdoutPipe)
    for scanner.Scan() {
        s.stdoutBuf = append(s.stdoutBuf, scanner.Bytes()...)
    }
}

func (s *Shell) readError() {
    scanner := bufio.NewScanner(s.StderrPipe)
    for scanner.Scan() {
        s.stderrBuf = append(s.stderrBuf, scanner.Bytes()...)
    }
}

روش ها را بخوانید

برای جلوگیری از مسدود کردن از بافرهایی که جمع کرده اید بخوانید:

func (s *Shell) ReadOut() []byte {
    out := s.stdoutBuf
    s.stdoutBuf = nil  // Clear the buffer after reading
    return out
}

func (s *Shell) ReadErr() []byte {
    err := s.stderrBuf
    s.stderrBuf = nil  // Clear the buffer after reading
    return err
}

تابع اصلی

شما main.go اکنون به این شکل است:

func main() {
    s, err := StartShell()
    if err != nil {
        fmt.Println("Error Starting shell --->", err)
        return
    }

    for {
        var input string
        scanner := bufio.NewScanner(os.Stdin)
        scanner.Scan()
        input = scanner.Text()
        if err = s.Write(input); err != nil {
            fmt.Println("Error Running Command in shell --->", err)
        }
        fmt.Print(string(s.ReadOut()))
        fmt.Print(string(s.ReadErr()))
    }
}

سوالات متداول (متداول)

Q1: چرا خواندن از stdout یا stderr باعث بن بست؟

A1: بن بست هنگامی رخ می دهد که یک عملیات خواندن در انتظار در دسترس بودن داده ها مسدود شود اما هیچ داده ای در دست نیست و باعث آویز این برنامه می شود. اجرای goroutines برای رسیدگی به خواندن می تواند به جلوگیری از این امر کمک کند.

Q2: چگونه می توانم اطمینان حاصل کنم که تمام خروجی ها از آن خوانده شده است cmd.exe؟

A2: از goroutines برای خواندن از هر دو جریان خروجی استفاده کنید و حتماً بافر را حفظ کنید تا بتوانید پس از هر خواندن به داده ها دسترسی پیدا کرده و پاک کنید.

Q3: اگر دستور من به ورودی کاربر نیاز داشته باشد ، چه می شود؟

A3: اطمینان حاصل کنید که مکانیسم خواندن شما با بررسی خروجی های مورد انتظار و برقراری ارتباط با کاربر ، به طرز فجیعی از ورودی ها استفاده می کند.

پایان

با استفاده از goroutines و بافر برای هر دو stdout وت stderr، می توانید از بن بست جلوگیری کرده و به طور کارآمد اجرای فرمان را در برنامه های GO خود مدیریت کنید. این تکنیک نه تنها نحوه تعامل شما با زیرمجموعه ها را بهبود می بخشد بلکه با ارائه یک رابط پاسخگو برای برنامه های خط فرمان ، تجربه کاربر را نیز غنی می کند.

نوشته های مشابه

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

دکمه بازگشت به بالا