How to delete a file using golang on program exit?

huangapple go评论91阅读模式
英文:

How to delete a file using golang on program exit?

问题

我已经制作了一个命令行应用程序,我在其中将文件夹压缩并共享到我的本地服务器上供他人下载。我想要做的是在关闭服务器时立即删除我复制的压缩文件夹。这是我的代码:

func main() {
    //flag to specify whether we will be uploading folder or a single file
    zipped := flag.Bool("z", false, "Use for zipping folders and serving them as a single file on the server.(Deletes the zipped file once the server closes.)")
    save := flag.Bool("s", false, "Use with -z for saving the zipped files locally even after the server closes.")
    flag.Parse()

    if len(flag.Args()) > 0 {
        if *zipped {
            fmt.Println("zipping...")
            flag.Args()[0] = ZipFile()

            if !(*save) {
                //I expect this to remove the file when I hit ctrl+c on cmd  
                defer os.Remove(flag.Args()[0])
            }
        }
        http.HandleFunc("/", ShareFile)
        fmt.Printf("Sharing file on %s:8080\n", GetOutboundIP())

        log.Fatal(http.ListenAndServe(":8080", nil))
    } else {
        fmt.Println("Invalid usage. No file mentioned. Use wshare -h for help.")
    }
}

当我按下Ctrl+C时,程序退出,main函数关闭,因此不应该执行os.Remove(xyz)吗?A tour of go中说,defer会在函数返回时执行表达式。在这里,我觉得main函数根本没有机会返回任何东西。

有什么解决办法可以实现我想做的事情吗?我脑海中有一些解决方案,比如等待按键等,但我希望这个程序非常简单,所以有没有办法在服务器关闭/程序退出时立即删除文件,而无需我进一步输入任何内容?

英文:

I have made a command line application where I am zipping up folders and sharing on my local server for others to download. What I want to do is delete my copy of the zipped folder as soon as I close the server. This is my code:

func main() {
    //flag to specify whether we will be uploading folder or a single file
    zipped := flag.Bool("z",false,"Use for zipping folders and serving them as a single file on the server.(Deletes the zipped file once the server closes.)")
    save := flag.Bool("s",false,"Use with -z for saving the zipped files locally even after the server closes.")
    flag.Parse()


    if len(flag.Args())>0{
        
        if *zipped{
            fmt.Println("zipping...")
            flag.Args()[0]=ZipFile()

            if !(*save){
              //I expect this to remove the file when I hit ctrl+c on cmd  
              defer os.Remove(flag.Args()[0])	
                 }
        }
        http.HandleFunc("/",ShareFile)
        fmt.Printf("Sharing file on %s:8080\n",GetOutboundIP())
    
        log.Fatal(http.ListenAndServe(":8080",nil))
    }else{
        fmt.Println("Invalid usage. No file mentioned. Use wshare -h for help.")
    }

}

When I hit ctrl-c, the program exits and main function closes and as a result,shouldn't os.Remove(xyz) get executed? A tour of go says, defer executes the expression when the function returns. Here, I don't feel main gets the oppurtunity to return anything at all.

What is a workaround to achieve what I am trying to do? I have some solutions in my head like wait for a keypress etc. but I want this program to be super simple,so is there a way to delete the file as soon as the server closes/program exits without requiring any further input from me?

答案1

得分: 23

这个问题已经在评论中得到了回答,但为了完整起见,我将在这里记录一下。

defer 只在程序和代码正常运行完毕时起作用。而如果使用命令停止程序或强制终止程序,会向程序发送一个信号并异常终止,这样就无法正常执行所有的 defer 语句。

如果你想在操作系统终止时进行清理工作,可以监听操作系统信号 - 参考这个示例

sigs := make(chan os.Signal, 1)
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
go func() {
  <- sigs
  cleanupAllTheThings()
  os.Exit(0)
}()

如果你在 main 函数中调用这段代码,它将保持一个 goroutine 在程序的整个生命周期中监听操作系统信号。而 cleanupAllTheThings() 函数需要尽可能快地运行而不阻塞,以确保有效性 - 你永远不知道操作系统何时会强制终止你的程序。

此外,这种方法无法保护你免受断电或内核崩溃的影响 - 所以通常在启动时或在单独的清理脚本中进行一些旧程序状态的清理工作是有意义的。

英文:

This has already been answered in the comments, but I'll document it here for completeness.

defer works only when the program and code you're using it in runs through its course normally. Stopping a program with with a command or killing it, on the other hand, sends a signal to the program and then terminates it abnormally, which does not allow the program to run all the defer statements cleanly.

If you want to cleanup on OS termination, you can listen for OS signals - code based on the example here:

sigs := make(chan os.Signal, 1)
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
go func() {
  &lt;- sigs
  cleanupAllTheThings()
  os.Exit(0)
}()

If you call this from main, it will keep a goroutine running for the life of your program listening to the OS signals. And the cleanupAllTheThings() function needs to be written to run as fast as possible without blocking to be effective - you never know when the OS is going to terminate you with prejudice.

Also, this will not protect you from someone pulling out the plug or a kernal panic - so it usually makes sense to have some kind cleanup of the old program state on startup or in a separate cleanup script.

huangapple
  • 本文由 发表于 2017年1月3日 03:15:34
  • 转载请务必保留本文链接:https://go.coder-hub.com/41432193.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定