Golang - 按名称杀死进程

tgo*_*gos 4 command kill process go

如果您只知道进程名称,那么使用Go代码终止进程的有效方法是什么?我看到os包提供的一些功能如:

func FindProcess(pid int) (*Process, error)
func (p *Process) Kill() error
func (p *Process) Signal(sig Signal) error
Run Code Online (Sandbox Code Playgroud)

是否有一个好的/通常的做法来获得pid不必执行命令然后解析输出?

我找到了一种方法来使用如下命令取回pid:

  • echo $(ps cax | grep myapp | grep -o '^[ ]*[0-9]*')

而且我已经使用了它,exec.Command()但如果有更好的方法我想避免它.

Cla*_*ley 10

跨平台(第三方)解决方案

几个月来我已经实施了各种解决方案来执行此操作,并且由于某种原因我花了很长时间才找到gopsutil。它是一个第三方库,对您来说可能会或可能不会破坏交易,但它在我们的跨平台项目中完美运行。以下示例将杀死具有匹配名称的第一个进程,但它可以轻松地修改为杀死具有该名称的所有进程。

import "github.com/shirou/gopsutil/v3/process"

func KillProcess(name string) error {
    processes, err := process.Processes()
    if err != nil {
        return err
    }
    for _, p := range processes {
        n, err := p.Name()
        if err != nil {
            return err
        }
        if n == name {
            return p.Kill()
        }
    }
    return fmt.Errorf("process not found")
}
Run Code Online (Sandbox Code Playgroud)

具有上下文支持

作为额外的好处,该库还支持所有与进程相关的操作的上下文取消,包括进程查询和终止进程。

func KillAllProcessesCtx(ctx context.Context, name string) error {
    processes, err := process.ProcessesWithContext(ctx)
    if err != nil {
        return err
    }
    for _, p := range processes {
        n, err := p.NameWithContext(ctx)
        if err != nil {
            return err
        }
        if n == name {
            err = p.KillWithContext(ctx)
            if err != nil {
                return err
            }
        }
    }
    return nil
}
Run Code Online (Sandbox Code Playgroud)

优雅终止

该库还通过向进程发送您自己的信号来支持优雅终止。

// Do this
err = p.SendSignal(syscall.SIGINT)
        
// Instead of this
err = p.Kill()
Run Code Online (Sandbox Code Playgroud)


Pet*_*oor 7

运行外部命令可能是执行此操作的最佳方法.但是,以下代码至少在Ubuntu上运行,只要您是要杀死的进程的所有者.

// killprocess project main.go
package main

import (
    "bytes"
    "fmt"
    "io"
    "io/ioutil"
    "log"
    "os"
    "path/filepath"
    "strconv"
    "strings"
)

// args holds the commandline args
var args []string

// findAndKillProcess walks iterative through the /process directory tree
// looking up the process name found in each /proc/<pid>/status file. If
// the name matches the name in the argument the process with the corresponding
// <pid> will be killed.
func findAndKillProcess(path string, info os.FileInfo, err error) error {
    // We just return in case of errors, as they are likely due to insufficient
    // privileges. We shouldn't get any errors for accessing the information we
    // are interested in. Run as root (sudo) and log the error, in case you want
    // this information.
    if err != nil {
        // log.Println(err)
        return nil
    }

    // We are only interested in files with a path looking like /proc/<pid>/status.
    if strings.Count(path, "/") == 3 {
        if strings.Contains(path, "/status") {

            // Let's extract the middle part of the path with the <pid> and
            // convert the <pid> into an integer. Log an error if it fails.
            pid, err := strconv.Atoi(path[6:strings.LastIndex(path, "/")])
            if err != nil {
                log.Println(err)
                return nil
            }

            // The status file contains the name of the process in its first line.
            // The line looks like "Name: theProcess".
            // Log an error in case we cant read the file.
            f, err := ioutil.ReadFile(path)
            if err != nil {
                log.Println(err)
                return nil
            }

            // Extract the process name from within the first line in the buffer
            name := string(f[6:bytes.IndexByte(f, '\n')])

            if name == args[1] {
                fmt.Printf("PID: %d, Name: %s will be killed.\n", pid, name)
                proc, err := os.FindProcess(pid)
                if err != nil {
                    log.Println(err)
                }
                // Kill the process
                proc.Kill()

                // Let's return a fake error to abort the walk through the
                // rest of the /proc directory tree
                return io.EOF
            }

        }
    }

    return nil
}

// main is the entry point of any go application
func main() {
    args = os.Args
    if len(args) != 2 {
        log.Fatalln("Usage: killprocess <processname>")
    }
    fmt.Printf("trying to kill process \"%s\"\n", args[1])

    err := filepath.Walk("/proc", findAndKillProcess)
    if err != nil {
        if err == io.EOF {
            // Not an error, just a signal when we are done
            err = nil
        } else {
            log.Fatal(err)
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

这只是一个肯定可以改进的例子.我为Linux编写了这个,并在Ubuntu 15.10上测试了代码.它不会在Windows上运行.


tgo*_*gos 6

我终于使用了如下内容:

// `echo "sudo_password" | sudo -S [command]`
// is used in order to run the command with `sudo`

_, err := exec.Command("sh", "-c", "echo '"+ sudopassword +"' | sudo -S pkill -SIGINT my_app_name").Output()

if err != nil {
    // ...
} else {
    // ...
}
Run Code Online (Sandbox Code Playgroud)

我使用SIGINT信号来优雅地停止应用程序。

来自维基百科

  • SIGINT

    当用户希望中断该过程时,SIGINT信号将由其控制终端发送给该过程。通常,这是通过按Ctrl + C来启动的,但是在某些系统上,可以使用“删除”字符或“中断”键。

  • SIGKILL

    SIGKILL信号发送到进程以使其立即终止(终止)。与SIGTERM和SIGINT相比,该信号无法捕获或忽略,并且接收过程在接收到该信号后无法执行任何清理。下列例外情况适用: