3 ms·
It seems like a waste of precious syntax to dedicate backticks to running shell commands. What's between the backticks is not even portable; the commands rely
by kazinator 2y ago
It seems like a waste of precious syntax to dedicate backticks to running shell commands.
What's between the backticks is not even portable; the commands rely on an operating-system-specific command interpreter.
> puts `ls`.lines.map { |name| name.strip.length } # prints the lengths of the filenames
Fantastic example, except for the commandment violation: "thou shalt not parse the output of 'ls'"!
You really want to read the directory and map over the stat function (or equivalent) to get the length.
2> (flow "."
open-directory
get-lines
(mapcar [chain stat .size]))
(727 2466 21 4096 643 16612 5724 163 707 319 352135 140 51 0 4096
114898 1172428 1328258 4096 4096 4096 29953 4096 4096 0 27 4096
4096 35585 8450 968 40960 14610 4096 14 755128 1325258 4096 17283
218 471 104 4096 99707 1255 4096 129 4096 721 9625 401 15658
4096 235 98 1861 664 23944 4286 4096 1024 0)
- Too 2y agoThey have inherited the second biggest mistake of shellscripts; Requiring the user to manually check $? after each command. No thanks. Anything that doesn't have error handling enabled by default goes straight in the trash bin.
- nomilk 2y agoI couldn't spot any way to make something like `ls -j` (j is an illegal option) throw an exception in ruby (as opposed to simply outputting the system error message). The closest I could find is what you suggest (checking $?), or using something like this [1], which would require changing syntax: system('ls -j', exception: true) Would be great to know if there's some easy callback or, ideally, a global setting one can make so a ruby exception is thrown if there's an error running system commands using the backtick syntax. [1] https://stackoverflow.com/a/54132729/5783745 https://stackoverflow.com/a/54132729/5783745
- tasuki 2y agoIt's not required to check $? after each command. It's only when calling out to the shell. How would you propose to handle that? Throw an exception on a shell command which fails? Some would say calling out to shell is an anti pattern by itself. Others would say exceptions are an anti pattern. (Just use appropriate return type and there's no need for exceptions ever!)
- stouset 2y agoThis is only true for backticks, which are somewhat intended for non-serious use. If you want exceptions for subprocess failure, `system` does the trick. There’s really no need for this kind of over-the-top response.
- latexr 2y ago> They have inherited the second biggest mistake of shellscripts; Requiring the user to manually check $? after each command. This isn’t true for Ruby nor shell scripts. In Ruby you have `system` or `Open3`. In shell scripts you: if my_command then on_success else on_failure fi Shellcheck even warns you of that. https://www.shellcheck.net/wiki/SC2181 https://www.shellcheck.net/wiki/SC2181
- jerska 2y agoYou’re checking the return value of the command here. Do you wrap all calls in an if? I believe the author was talking about set -e (often used with -o pipefail), so that any unhandled error simply exits the script.
- latexr 2y ago> Do you wrap all calls in an if? I don’t need to check the exit status of every command. > I believe the author was talking about set -e (often used with -o pipefail), so that any unhandled error simply exits the script. I have no idea how you could get that impression from the section I quoted. How would setting one option once at the top of the script mean “Requiring the user to manually check $? after each command”?
- oopsallmagic 2y agoHow is it supposed to know how you want your errors handled by default? Handling errors is the programmer's job.
- Too 2y agoThere are lots of techniques to make it obvious that errors can happen and need to be handled. 1. Make it difficult to ignore that a function can return an error. This is the golang approach. Errors are part of the return values and unused return values are compiler errors. 2. Make it impossible to use parts of the return value having an error state. Rust does this with the Result sum type and pattern matching. 3. Tailor for the happy-path and throw exceptions in case there are any errors. With optional means to catch them if recovering errors is desired. This is how most other languages function. Hiding the error status in another variable, that is super easy to overlook and that the programmer might not even know exists, then continuing despite this variable not being checked will inevitably introduce bugs allowing faulty data in your system.
- kouteiheika 2y ago> You really want to read the directory and map over the stat function (or equivalent) to get the length. Indeed. Equivalent in Ruby: Dir["*"].map { |x| File.size(x) }
- kazinator 2y agoI avoided (flow (glob "*") (mapcar [chain stat .size])) because * skips entries starting with dot, which sends us down a certain distracting rabbit hole.
- pdntspa 2y agoAnd what psychopath is trying to parse the output of ls? None of this makes any sense to me, and I write Ruby for my day job.
- lucasoshiro 2y agoIt's about the syntax.
- lkuty 2y agoIs this Common Lisp? `mapcar` makes me think of it but I do not know about `flow`.
- lucasoshiro 2y ago> It seems like a waste of precious syntax to dedicate backticks to running shell commands. Except from Bash (where backticks also have the same purpose as Ruby), I only remember seeing backticks in: - Kotlin, for naming functions with phrases. The only use case that I remember for it was to creating more meaningful names for test functions. I don't think that it was so useful... - Lisp dialects for quasiquotes, which is meaningless in Ruby - Haskell, for making functions infix, and I can't see why would it be useful in Ruby (Ruby has it own way to make infix methods) - JS, for creating templates. In Ruby we can use double quotes for that > "thou shalt not parse the output of 'ls'" Yes, but it was only an example of associating backticks and the language features. Of course it is not ideal, but it is an example that everyone will understand. You'll probably won't want to use it (even because you can do it with Dir.glob('*').map { |f| f.length }). The same happened later in the text when I used a regex to find the Git branch. > You really want to read the directory and map over the stat function (or equivalent) to get the length. But that is not Ruby.