Search code examples
goprocesscommandkill

Golang - kill process by name


What would be an effective way to kill a process with Go code if you only know the process name? I see some functions provided by the os package like:

func FindProcess(pid int) (*Process, error)
func (p *Process) Kill() error
func (p *Process) Signal(sig Signal) error

Is there a good/common practice to get the pid without having to execute commands and then parse the output?

I have found a way to get back the pid using a command like the following:

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

and I have used it with exec.Command() but I would like to avoid it if there is a better approach.


Solution

  • Cross-Platform (3rd party) Solution

    I've implemented various solutions to do this for months now, and for some reason it took me that long to find gopsutil. It is a 3rd party library and that may or may not be a deal breaker for you, but it has worked flawlessly for our cross-platform projects. The following example will kill the first process with the matching name, but it can easily be adapted to kill all processes with the name.

    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")
    }
    

    With Context Support

    As an added bonus, the library also supports context cancellation on all process related operations including process queries, and killing the process.

    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
    }
    

    Graceful Termination

    The library also supports graceful termination by sending your own signal to the process.

    // Do this
    err = p.SendSignal(syscall.SIGINT)
            
    // Instead of this
    err = p.Kill()