Uberswe
Uberswe

Reputation: 1058

How can i make a go program that can remove itself?

This all started when I wanted to make a program that could update itself. I figured I need the program to download a new version and run a function that copies the new program and replaces the original with the downloaded version.

I tried to make this problem as small as possible, how can I make a program that calls another program to remove itself, here is my attempt:

package main

import (
    "flag"
    "fmt"
    "log"
    "os"
    "os/exec"
    "time"
)

func main()  {
    fmt.Println("program started")
    remove := flag.Bool("rm", false, "removes test")
    flag.Parse()

    if *remove {
        // Wait 5 seconds to let other program finish
        time.Sleep(5 * time.Second)
        // Try to remove program that started this program
        fmt.Println("running rm")
        err := os.Remove("./test")
        if err != nil {
            log.Fatalf("os.Remove() failed with %s\n", err)
        }
    } else {
        // Call the second program which will remove ./test which is currently running
        fmt.Println("running remove program")
        cmd := exec.Command("./remove", "-rm")
        err := cmd.Start()
        if err != nil {
            log.Fatalf("cmd.Run() failed with %s\n", err)
        }
    }
}

Here is how I call this via cli.

uberswe$ go build -o test
uberswe$ go build -o remove
uberswe$ ./test
program started
running remove program
uberswe$ ls -la
total 9048
drwxr-xr-x@  6 uberswe  staff      192 Apr 14 15:55 .
drwxr-xr-x@ 56 uberswe  staff     1792 Apr 14 15:36 ..
drwxr-xr-x@  6 uberswe  staff      192 Apr 14 15:55 .idea
-rw-r--r--@  1 uberswe  staff      680 Apr 14 15:55 main.go
-rwxr-xr-x@  1 uberswe  staff  2311528 Apr 14 15:55 remove
-rwxr-xr-x@  1 uberswe  staff  2311528 Apr 14 15:55 test

So in summary: How can I make a program that can remove itself either on its own or via a second command/program?

Bonus if it is portable to different operating systems.

Upvotes: 1

Views: 1993

Answers (3)

Carlos
Carlos

Reputation: 103

I know this is an old thread, but I think the easiest way to do this on Windows without any (non-default) additional programs would be:

package main

import (
    "os/exec"
    "os"
)

func main() {
    cmd := exec.Command("cmd.exe","/c", "del " + os.Args[0])
    cmd.Start()
}

It can be easily adapted to auto-update itself:

import (
    "os/exec"
    "os"
)

func main() {
    updated := update()
    if updated != "" {
        cmd := exec.Command("cmd.exe","/c", "start cmd.exe /c move " + updated + " " + os.Args[0])
        cmd.Start()
    }
}

func update() (string) {
    // do something to download new version
    return "progtmp.exe"
}

There could be a race condition where the main program is still running before cmd.exe executes the commands, although I have not had that problem so far. A small delay could be added if needed with something like:

ping 1.1.1.1 -n 1 -w 3000 > NUL & move tmp.exe prog.exe

Upvotes: 0

Uberswe
Uberswe

Reputation: 1058

So an example of how I can solve this which works on Mac OS, Linux and Windows is the following code.

package main

import (
    "flag"
    "fmt"
    "log"
    "os"
    "os/exec"
    "runtime"
    "time"
)

func main()  {
    fmt.Println("program started")
    remove := flag.Bool("rm", false, "removes test")
    flag.Parse()

    if *remove {
        var err error
        // Wait 5 seconds to let other program finish
        time.Sleep(5 * time.Second)
        // Try to remove program that started this program
        fmt.Println("running rm")
        if runtime.GOOS == "windows" {
            err = os.Remove("./test.exe")
        } else {
            err = os.Remove("./test")
        }
        if err != nil {
            log.Fatalf("os.Remove() failed with %s\n", err)
        }
    } else {
        var cmd *exec.Cmd
        // Call the second remove program which will remove ./test which is currently running
        fmt.Println("running remove program")
        if runtime.GOOS == "windows" {
            cmd = exec.Command("./remove.exe", "-rm")
        } else {
            cmd = exec.Command("./remove", "-rm")
        }
        cmd.Stdout = os.Stdout
        cmd.Stderr = os.Stderr
        err := cmd.Start()
        if err != nil {
            log.Fatalf("cmd.Run() failed with %s\n", err)
        }
    }
    fmt.Println("Finished running program")
}

This program will remove itself if you run it like so

uberswe$ go build -o test
uberswe$ go build -o remove
uberswe$ ./test

On windows I had to make one change which is to add the .exe extension in order for windows cmd to recognize the binary as an executable file. I was able to make the application test call on remove to delete itself.

Upvotes: 1

Chris
Chris

Reputation: 166

Since your goal is for an application to update itself, I would move this functionality into a second "updater" application. The executable file (depending on OS) might be locked otherwise plus you still have the issue of restarting the app. The flow would be like this:

  • main program spawns updater and terminates itself
  • updater waits for main program to terminate
  • updater replaces executable file and restarts app (if desired)

Upvotes: 1

Related Questions