Reputation: 2287
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?
Upvotes: 18
Views: 15353
Reputation: 17516
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() {
<- 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.
Upvotes: 24