-
-
Save progrium/d782e574bb2e97d5ebdc to your computer and use it in GitHub Desktop.
curl -s "$url" | tar -zxC "$dest" |
func downloadAndInstall(url, dest string) error { | |
resp, err := http.Get(url) | |
if err != nil { | |
return error | |
} | |
defer resp.Body.Close() | |
zip, err := gzip.NewReader(resp.Body) | |
if err != nil { | |
return error | |
} | |
defer zip.Close() | |
archive := tar.NewReader(zip) | |
for { | |
header, err := archive.Next() | |
if err == io.EOF { | |
break | |
} | |
if err != nil { | |
return err | |
} | |
filename := fmt.Sprintf("%s/%s", dest, header.Name) | |
file, err := os.Create(filename) | |
if err != nil { | |
return err | |
} | |
defer file.Close() | |
_, err := io.Copy(file, archive) | |
if err != nil { | |
return err | |
} | |
} | |
} |
The comparison is impressive at the first glance, but I think it is a little bit unfair: we are comparing a higher-level declarative style of coding with a finer-grained imperative code. I think we can reach a similar brevity in Go if we introduce some concepts like streams, etc. and we write some higher-level functions handling that.
In the example above, for example, we have the ability to catch an error and handle it in Go, while the bash script probably just crashes or has a behavior with (uncontrolled/unexpected?) side-effects. The control/safety/behavior of the code is also another (hidden) factor in the comparison, that I loved, but perhaps it's hiding too many relevant considerations (even if it is not claiming that one is better than the other) :)
I respectfully submit that you should write the corresponding program in C. That is the comparison that I think about most often when using Go 😉
Would like to see examples of how the Go version could be improved, using the typical practices of an experienced Go programmer. Any takers?
The bash example would need 'set -o pipefail' to replicate the error handling in the Go version through the HTTP download.
This is what I struggle with in Go. I feel it has some interesting ideas, but it's not very nice to read.
This is I prefer Python. But I'd be curious to see how to improve the Go code to make it more succinct.
@cablehead set -o pipefail
just deals with error code propagation. You can't replicate the immediate abort-on-failure of the Go code using just pipes (i.e. tar
will still run no matter what w/ a pipes solution). You'd have to write a full script w/ separate commands to avoid running tar
if curl
errored.
@abevoelker Thanks for the clarification. Oof, yeah, my understanding was that set -o pipefail
would abort if the curl fails, before the tar runs. You're right though:
andy@hop:~/tmp$ cat 1.sh
#!/bin/bash
false | echo "foo"
echo $?
andy@hop:~/tmp$ ./1.sh
foo
0
andy@hop:~/tmp$ cat 2.sh
#!/bin/bash
set -o pipefail
false | echo "foo"
echo $?
andy@hop:~/tmp$ ./2.sh
foo
1
I definitely see @progrium's point, but in that case, once both versions are safe, the bash version wouldn't be as clearly terse.
@msabramo Python's concurrency story is woeful though, and I fear that asyncio is a long winded wrong turn.
Thank you for pointing that out, I guess. I was trying to communicate "effective" standard library, hence quoting "standard". Just for the record,
http
,gzip
,tar
,fmt
,os
, andio
are not Go built-ins.