Как я могу создать программу go, которая может сама себя удалить?

#go

# #Вперед

Вопрос:

Все это началось, когда я захотел создать программу, которая могла бы обновляться. Я подумал, что мне нужна программа для загрузки новой версии и запуска функции, которая копирует новую программу и заменяет оригинал загруженной версией.

Я попытался сделать эту проблему как можно меньше, как я могу создать программу, которая вызывает другую программу для удаления себя, вот моя попытка:

 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 %sn", 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 %sn", err)
        }
    }
}
 

Вот как я вызываю это через 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
 

Итак, вкратце: как я могу создать программу, которая может удалять себя самостоятельно или с помощью второй команды / программы?

Бонус, если он переносим на разные операционные системы.

Комментарии:

1. В Linux вы можете просто перезаписать запущенный исполняемый файл из самого себя. В Windows это не так просто.

2. @tkausl полезно знать, я также пробовал это с cmd.Start() помощью instead of cmd.Run() , и все равно ничего не происходит, когда я тестирую на Mac OS.

3. Почему вы даже вызываете rm вместо того, чтобы удалять файл из Go напрямую?

4. @tkausl хороший момент, я обновил свой вопрос, чтобы включить это, теперь я вижу, что файл также удаляется на Mac, но только если я вызываю свою программу с -rm помощью флага напрямую, а не при ./test вызовах ./remove -rm

5. Попробуйте настроить cmd.Stdout и cmd.Stderr для своих собственных потоков, чтобы увидеть результат вызываемой программы

Ответ №1:

Поскольку ваша цель состоит в том, чтобы приложение обновляло себя, я бы перенес эту функциональность во второе приложение «обновления». Исполняемый файл (в зависимости от ОС) может быть заблокирован в противном случае, плюс у вас все еще есть проблема с перезапуском приложения. Поток будет таким:

  • основная программа запускает программу обновления и завершает работу
  • программа обновления ожидает завершения основной программы
  • программа обновления заменяет исполняемый файл и перезапускает приложение (при желании)

Ответ №2:

Итак, примером того, как я могу решить эту проблему, которая работает на Mac OS, Linux и Windows, является следующий код.

 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 %sn", 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 %sn", err)
        }
    }
    fmt.Println("Finished running program")
}
 

Эта программа удалит себя, если вы запустите ее следующим образом

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

В Windows мне пришлось внести одно изменение, которое заключается в добавлении расширения .exe, чтобы Windows cmd распознала двоичный файл как исполняемый файл. Я смог заставить приложение test вызывать remove для удаления себя.