[HN Gopher] Scripting with Go ___________________________________________________________________ Scripting with Go Author : synergy20 Score : 114 points Date : 2022-03-11 16:26 UTC (6 hours ago) (HTM) web link (bitfieldconsulting.com) (TXT) w3m dump (bitfieldconsulting.com) | mftb wrote: | It took me a while to find the link to the library "script" and | it's repo - https://github.com/bitfield/script | synergy20 wrote: | https://golangexample.com/making-it-easy-to-write-shell-like... | with quick examples, everything is pipe based just like | bash|fish|zsh|csh | 0xbadcafebee wrote: | Go script: p := Exec("man bogus") | p.SetError(nil) output, err := p.String() | fmt.Println(output) | | Shell script: man bogus | | ......I'm gonna stick with shell scripts. | nunez wrote: | There was a recent discussion in /r/golang about chaining methods | like this. The OP had a hard time writing tests for their | implementation since each part of the chain returned interfaces | that were relied on by following links within. The top answer in | the thread was that Golang wasn't really meant to make "Promise-" | type workflows possible due to it leaning heavily on code being | intentional and explicit. | | I agree with them. | | One of the problems I have with things like LINQ or Promises is | that they are immensely powerful but can be really difficult to | debug when things go wrong because of implicit errors within the | pipeline bubble up the stack without providing location context. | While the standard boilerplate around errors in Go is annoying, I | actually prefer it for two reasons: | | 1. It provides exactly where things went wrong while looking at | an exception trace, and 2. From a readability perspective, it is | much easier to understand what the author of the code meant to do | and, more importantly, what the follow-on error s mean. | Sometimes, errors aren't actually errors. | | Either way, OCaml/Haskell/F#'s approach with match expressions | and the `Result` type is the best way to deal with this, IMO. You | get the best of both worlds: explicit declaration with a very | expressive (triangular-like) "shape" to the code. | | So something like this: var data string = | script.File("test.txt").Match("Error").CountLines() | | Can be expressed like this: var foundLines int32 | = 0 var numLines int32 = match file.Open("test.txt") with | | Error e -> return e | Ok f -> match f.Readlines() with | | Error e -> return e | Ok lines -> match lines with | | /Error.*/ -> foundLines++ | _ -> // do nothign | return foundLines | | instead of: foundLines := 0 lines, err := | ioutil.ReadFile("test.txt") if err != nil { return | 0, err } for _, l := range lines { re, err := | regexp.MustCompile(`Error.*`) if err != nil { | return 0, err } matches := | re.FindStringSubmatch(`Error.*`) if len(matches) > 0 { | foundLines++ } } return foundLines, nil | | This way, you can see exactly where the error occurred but can | still see that `numLines` is generated through a pipeline. | | As far as the library itself, I personally wouldn't use something | like this, though I see the appeal. I turn to statically-typed | languages like Golang when a Bash script becomes too kludgey to | stand on its own (usually when I need to begin relying on mild | data structures) and when I care about type safety and | portability more than what Ruby or Python can give me. When I'm | writing a Go program, I want something that's testable that I | know can work anywhere. With Ruby or Python, unless I'm | distributing the script as a Docker image, I have to worry about | versions, environments, etc., none of which are pleasant. | | However, writing the error boilerplate is annoying, and I can see | developers who want to write something quick but spend 99% of | their time in Go using this to get something done with a tool | they know well. I've seen similar things in the Java world; hell, | that's the reason why Groovy and Kotlin exist! | | TL;DR: The "wrong thing" in the Bitfield article isn't | necessarily wrong; their library is useful, but niche; all hail | pattern-matching and the Result type. | hankchinaski wrote: | i am not sure i would personally use this library/approach when | scripting, a few reasons: | | Using a third-party libraries as core part of your infrastructure | (ci/cd scripts, provisioning, automation) implies a greater risk | to potential security issues, "framework tax" ie. having to | comply, learn, document, debug its custom APIs, having to deal | with potential limitations, issues that either needs to be fixed | upstream or result in the library being forked and therefore | maintained in house. I would rather either put together a set of | bash commands or - if the problem entails a more comprehensive | endeavour with greater complexity - put together a in-house | tool/library where i can make the right compromises from day one | kkfx wrote: | Honestly I'm not convinced. I understand the need of a so-called | "real" programming language ready available, that's what all | classic system have had, from SmallTalk workstation to LispM, but | for them there is not just the language, there is the complete | ecosystem build with the same language, so an user made script | have no difference than a system part of the user desktop. | | Unix decide to "simply" creating a system with a system language | and an user system with an user language, the shell. I do not | like much unix approach after having used Emacs a bit, but I do | understand it. On contrary I always fails to "craft scripts" in | "real" programming languages no matter what. I've tried in the | past to "go Perl", "go Python", "go TCL", yes I can write scripts | with them, I have written some etc but if I need something quick | I go for the shell, zsh to be more precise (tried others, all | failed at a certain point, from bash to elvish passing through | oil shell and few others) or being in Emacs (EXWM is my WM/DE) | Emacs itself depending on the case. | | I read various similar article for a language or another but in | the long run see no colleagues really choose a programming | language behind shell itself... | mkdirp wrote: | I'll have to check this out properly later. 10 years ago go devs | rejected the idea of introducing support hashbangs and now we're | left with having to use gorun[0], meaning, people are unlikely to | use go for script. | | I hope the go devs will reconsider. I'd love to be able to use go | for scripting. But as it stands, it's a sad state of affairs | because you have to rely on hacks. | | [0] https://github.com/erning/gorun/ | throwaway894345 wrote: | I'm not sure what definition you have for "scripts", but I'm | fine with running `go run main.go` or whatever. I don't need my | Go files to be executable (half the time I just run bash | scripts via `bash script.sh` anyway because that _always_ | works). The biggest impediment is the ceremony for | subprocessing out, but I 'm sure that could be abstracted | behind a library with a more pleasant veneer than os/exec, and | even if not whatever you lose in subprocess ergonomics you make | up for in static typing, not needing to Google/Stack Overflow | everything, and general absence of weird quirks. | moondev wrote: | you can now directly "go run" any import path | go run github.com/mikefarah/yq/v3@3.4.1 | | It also works fine in shebang that expects input of script | filepath at $1. For example "test.yaml" | #!/usr/bin/env -S go run github.com/mikefarah/yq/v3@3.4.1 r | --- some: yaml: here | | chmod +x test.yaml then ./test.yaml some.yaml | | returns test | shimst3r wrote: | While I see the benefit of this approach, I'm often baffled why | people want to go either 100 % POSIX builtins or 100 % scripting | language. | | The biggest benefit of the shell is its clearly defined input and | output (and error) interfaces. Most programming languages can | read from and write to stdin, stdout, and stderr. | | Why not use it and stick to KISS, replacing one cumbersome POSIX | utility at a time, suites for the task? Then you don't need to | chain methods using less idiomatic code. But then you wouldn't | need these kind of libraries either. | cogman10 wrote: | I think it boils down to the friction of starting and stopping | external processes. | | For example, you could in your scripting language use `find` to | search for files in a folder and do something with them, but | why do that when your language of choice almost certainly has | globbing capabilities? You could grep a file for a line, but | why do that when you can use your language's inbuilt regex | system? | | At least from the scripting side, the reason I tend to push | more towards using the language and less towards using external | processes is because most scripting languages can do what those | external processes do in one go. | | Perhaps it would make more sense if I were better at defining | scope :) | unfocussed_mike wrote: | Cross-platform deployment is why I switched from script plus | utilities to a go binary. | | I did manage to make a Windows batch file that replicated the | functionality of a linux/mac bash script, but configuring it | was no fun for customers on any platform, and then there were | the utilities themselves to deploy. | | The replacement binary has a very small platform-dependent | aspect, and I am not held back by the limits of batch files | when trying to achieve feature parity. | | It might be doable to deploy a powershell script, but then | there's installation work to do on the unix side instead. | pphysch wrote: | Let one-liners be one-liners, and bring in Go/etc when it | ceases to be a one-liner. But not before. | jjtheblunt wrote: | I agree in principle, but mastering the syntactic quirk-fest of | bash and other shells is really a bit weird, in that surprises | arise at runtime. | | maybe that's the compelling use of scripting with a statically | typed, thus compile-time at least partially low-hanging-fruit- | error-checked, language? | qbasic_forever wrote: | We really need a 'Bash: The Good Parts' book like Doug | Crockford did for Javascript. IMHO bash and the shell are in | a state that Javascript was ~2005--an old language/tool full | of complexities but that can be sharpened and honed down to | something beautiful. | | IMHO writing procedural style code with lots of if, loops, | etc. in the shell can quickly turn into an anti-pattern. Try | to stick to simple functions that are chained together in | pipelines. The only loop is typically one that processes | arguments and that's it. | b215826 wrote: | > _We really need a 'Bash: The Good Parts' book like Doug | Crockford did for Javascript._ | | Bash is incredibly less complex than Javascript and there | is such a resource: the "Bash guide" [1] and "Bash | pitfalls" [2] are both excellent resources that teach you | how to use Bash properly. | | [1] http://mywiki.wooledge.org/BashGuide | | [2] http://mywiki.wooledge.org/BashPitfalls | hnlmorg wrote: | I completely agree. | | This is the approach that I took with murex. It comes with a | stack of builtins for json, jsonlines, yaml, toml, csv, | sqlite3, and others. So you have all the power of data types | and intelligent management of structured data but also works | with regular POSIX CLI commands without any additional | boilerplate code when swapping between murex code and coreutils | (et al). So one could say I've spent a fair amount of time | studying this point you've raised :) | klabb3 wrote: | https://github.com/lmorg/murex | | (Don't be afraid to self promote!) | | One obvious benefit over what's suggested in the article is | that you can use it interactively first, with autocomplete | and such goodies, and transition smoothly to a script later. | freedomben wrote: | GP has promoted murex a few times on HN recently, which | doesn't bother me, but if I were them I'd be sensitive to | not wanting to overdo it as well. | cyberge99 wrote: | Is murex open source? A quick search of murex shell presented | me with beautiful seashells. | zto wrote: | A quick Google for murex cli -sea yields: | https://murex.rocks and https://github.com/lmorg/murex, | looks like a promising tool | darrenf wrote: | Not to mention the URL in @hnlmorg's HN profile! | gkfasdfasdf wrote: | Something which the shell script has which the go implementation | lacks (unless I missed it) is concurrency. When you have a shell | pipeline like 'cmd1 | cmd2 | cmd3' those cmds are actually | started at the same time and run in parallel. This is really | great for performance - you get multiple cores working on the | problem with very little effort. | skybrian wrote: | This Go library looks like a good one, with the caveats that it | hasn't reached 1.0 yet, and does have a couple of dependencies | that I didn't trace further. | | At one time jQuery was popular and this seems like a similar | thing, but for files? | | It's small enough that if you're worried about "other people's | code" you could fork it and maintain it yourself. | shadowofneptune wrote: | This article focused entirely on replacing the Unix shell. Does | this library also work for the Windows shell or PowerShell (iirc | PowerShell is POSIX)? I could see the value of having a single | script in a Go repository which works for all build systems. | vessenes wrote: | I like me some bash scripting. And I do a lot of it. And I write | a good amount of Go. | | I read the intent of the original package as a bit different than | how it's presented here; a pain point in using go for systems | programming on Posix systems with GNU tooling is interacting with | all the other really excellent tools there. There's an awful lot | of cruft to go spawn a shell script in Go without this lib. | | So, I cannot imagine using this to replace a short-ish bash | script, it's just too heavyweight to add go development into the | workflow. But, I can easily imagine using this when I want to do | some simple-to-medium-scale pipelined text processing from the | middle of a go program. | unfocussed_mike wrote: | > But, I can easily imagine using this when I want to do some | simple-to-medium-scale pipelined text processing from the | middle of a go program. | | Yeah, this is where I see it. | | I have a cross-platform utility in customer use that replaces a | bash script or a .bat file that did a simple job with curl and | a database client but was tricky for customers to configure and | deploy; I replaced it with an interactively-configurable go | command. | | In the middle of it is the replacement pipe; this library would | help here. Might use it in the rewrite. | GordonS wrote: | Thing is, bash is everywhere (or, at least a POSIX shell is) - | for some use cases that matters, because you can't control what | is available on the target machine. Even python isn't | guaranteed - OK python is _fairly_ ubiquitous, but what | version? | | Shell scripting is far from perfect, and sometimes it does take | a while to figure out how to do something that would be easy in | another language - but very often, she'll scripting is "good | enough". | qbasic_forever wrote: | It's interesting that go text templates have shell-like pipelines | but that never bled over into the rest of the language: | https://pkg.go.dev/text/template#hdr-Pipelines | | It seems like a shame that this kind of power and expressiveness | is reserved only for generating text. | flakiness wrote: | (Mostly trolling) You must be missing extension method that other | modern languages have, let alone the pipe operator of Elixir, R, | etc. ;-) | jerf wrote: | The places I've "shell scripted with Go" involved also using | some other code I already had in Go. The referenced page | doesn't show it, but being able to do a bit of quick shell | scripting-type manipulation on a file, then feed it to the Go | JSON decoder to get Go objects, call a few methods or | something, then maybe manipulate it a bit more on the way out, | is sort of thing that is the real killer feature, IMHO. | | The space of "shell-like libraries", not least of which is | shell itself, is so rich it's hard to beat out everything else | on its own merits. But having something as easy as shell that | integrates back into your Go ecosystem is very convenient. | | And I'm sure similar things are true for the other libraries in | other languages. | | So I would personally not present this as "here's something | awesome Go can uniquely do", but, "if you already have Go, be | aware of this tool/technique". | flakiness wrote: | Yeah, I agree with your point and can see the usefulness. | Sorry for trolling, just couldn't resist :-/ | jniedrauer wrote: | I may be in the minority here, but I personally prefer the "wrong | answer" highlighted at the beginning of this article. Scripts are | source code. They go in the repo, and they have the same | standards applied to them as any other source code. I would much | prefer that the code be explicit and rely on few if any third | party libraries. I have go scripts that have been functioning in | production for half a decade now without modification. They are | as "self documenting" as any other go code, and I do not require | esoteric knowledge about a third party library to re-familiarize | myself with them. | fhood wrote: | I think you are missing the point. The overarching motivation | here is "Normally one would do this in bash, but I want to use | go instead. Is there any way for me to do that while retaining | the aspects of bash that make it good for these tasks?" And the | author provides a solution. Dunno why you would do that, but | not really important. | _adamb wrote: | I agree. It would be much more readable if it simply had a few | comments too (something that can't really exist in a 1-liner) | and is infinitely flexible (ex: for each match, also make an | API call). | chubot wrote: | FWIW a lot of other host languages are more DSL-like, and have | similar libraries: | | https://github.com/oilshell/oil/wiki/Internal-DSLs-for-Shell | | It looks like this particular one relies on method chaining | script.Args().Concat().Match("Error").First(10).Stdout() | tedunangst wrote: | Seems like this could pair well with something like yaegi. | mbreese wrote: | If you're willing to add a new binfmt to your system, here's | another method for using golang to write "scripts" (executed with | gorun). It's not the same as a shebang/hashbang, but it works. | | https://blog.cloudflare.com/using-go-as-a-scripting-language... ___________________________________________________________________ (page generated 2022-03-11 23:00 UTC)