Live data from Hacker News

Techniques I use to create a great user experience for shell scripts

nochlin.com

201–210 of 281 posts

Re: Techniques I use to create a great user experience for shell scripts

#201

Earlier quoted context omitted.

Addendum after reading the script: * #!/bin/bash instead of #!/usr/bin/env bash * [ instead of [[ * -z instead of actually checking how many arguments you got passed and trusting the end user if they do something weird like pass an empty string to your program * echo instead of printf * `print_and_execute sdk install java $DEFAULT_JAVA_VERSION` who asked you to install things? * `grep -h "^sdk use" "./prepare_$fork.s…

> * #!/bin/bash instead of #!/usr/bin/env bash Except that'll pick up an old (2006!) (unsupported, I'm guessing) version of bash (3.2.57) on my macbook rather than the useful version (5.2.26) installed by homebrew. > -z instead of actually checking how many arguments you got I think that's fine here, though? It's specifically wanting the first argument to be a non-empty string to be interpolated into a filename later…

The GP is pointing out [bad bash, good bash] and not [good bash, bad bash]. It was unclear to me at first as well.

You two are in violent agreement.

Re: Techniques I use to create a great user experience for shell scripts

#202

Earlier quoted context omitted.

You mean import os os.rename(“src.txt”, “dest.txt”) ?

Yes. And it is only downhill from there. Now, show us `mycommand | sed 's/ugly/beautiful/g' | awk -F: '{print $2,$4}' 1> something.report 2> err.log` in Python.

import os

os.system('mycommand | sed 's/ugly/beautiful/g' | awk -F: '{print $2,$4}' 1> something.report 2> err.log')

Re: Techniques I use to create a great user experience for shell scripts

#203
post #152
post #136

Earlier quoted context omitted.

As a bash casual, these suggestions are a reminder of why I avoid using bash when I can. That's a whole armory of footguns right there.

What is better?

I think Python is overused, but this is exactly what Python is great for. Python3 is already installed or trivial to install on almost everything, it has an enormous library of built-ins for nearly everything you'll need to do in a script like this, and for all of its faults it has a syntax that's usually pretty hard to subtly screw up in ways that will only bite you a month or two down the road.

My general rule of thumb is that bash is fine when the equivalent Python would mostly be a whole bunch of `subprocess.run` commands. But as soon as you're trying to do a bunch of logic and you're reaching for functions and conditionals and cases... just break out Python.

Re: Techniques I use to create a great user experience for shell scripts

#204

Earlier quoted context omitted.

> * #!/bin/bash instead of #!/usr/bin/env bash Except that'll pick up an old (2006!) (unsupported, I'm guessing) version of bash (3.2.57) on my macbook rather than the useful version (5.2.26) installed by homebrew. > -z instead of actually checking how many arguments you got I think that's fine here, though? It's specifically wanting the first argument to be a non-empty string to be interpolated into a filename later…

The GP is pointing out [bad bash, good bash] and not [good bash, bad bash]. It was unclear to me at first as well. You two are in violent agreement.

No, they're not. The script they're critiquing uses #!/bin/bash, so they have to have been saying that #!/usr/bin/env bash is better.

Re: Techniques I use to create a great user experience for shell scripts

#205
post #92

Earlier quoted context omitted.

The form you propose runs `command`, which may have undesired side-effects. I always thought `which` to be standard, but TIL `command` (sh builtin) is[0]. [0]: https://hynek.me/til/which-not-posix/

To be clear: both alternatives shown below will invoke the same thing (`command -v gtimeout`). if [ -x "$(command -v gtimeout)" ]; then and if command -v gtimeout >/dev/null; then The first invokes it in a sub shell (and captures the output), the second invokes it directly and discards the output, using the return status of `command` as the input to `if`. The superficial reason the second is "preferred" is that it's…

Ah! I misread the parent, if thought he meant `if command` to look for a random command (e.g. `if grep`)

Re: Techniques I use to create a great user experience for shell scripts

#206

Earlier quoted context omitted.

You mean import os os.rename(“src.txt”, “dest.txt”) ?

Yes. And it is only downhill from there. Now, show us `mycommand | sed 's/ugly/beautiful/g' | awk -F: '{print $2,$4}' 1> something.report 2> err.log` in Python.

That looks like a snippet from a command session which is a perfectly great place to be using sh syntax.

If it became unwieldy you’d turn it into a script:

  #!/bin/sh

  beautify() {
    sed -e ‘
      s/ugly/beautiful/g
      …other stuff
    ‘
  }

  select() {
    awk ‘
      {print $2, $4}
      …other stuff
    ‘
  }

  mycommand | beautify | select
For me, now it’s starting to look like it could be safer to do these things in a real language.

Re: Techniques I use to create a great user experience for shell scripts

#207
post #128

Earlier quoted context omitted.

But since there isn’t, even if you make one, people won’t want to rely on it as a dependency.

Can add it to bash?

It’s a bit more complicated. POSIX shell != bash, for example the default shell (/bin/sh) on macOS is now zsh, on Ubuntu it’s dash. Bash may still be installed, but may be years older than 2024. At a certain point, you’re better off embracing a dependency that just does everything better, like Python or Oil shell, for example.

Re: Techniques I use to create a great user experience for shell scripts

#208
If you want a great script user experience, I highly recommend avoiding the use of pipefail. It causes your script to die unexpectedly with no output. You can add traps and error handlers and try to dig out of PIPESTATUS the offending failed intermediate pipe just to tell the user why the program is exiting unexpectedly, but you can't resume code execution from where the exception happened. You're also now writing a complicated ass program that should probably be in a more complete language.

Instead, just check $? and whether a pipe's output has returned anything at all ([ -z "$FOO" ]) or if it looks similar to what you expect. This is good enough for 99% of scripts and allows you to fail gracefully or even just keep going despite the error (which is good enough for 99.99% of cases). You can also still check intermediate pipe return status from PIPESTATUS and handle those errors gracefully too.

Re: Techniques I use to create a great user experience for shell scripts

#210

Earlier quoted context omitted.

You mean import os os.rename(“src.txt”, “dest.txt”) ?

Yes. And it is only downhill from there. Now, show us `mycommand | sed 's/ugly/beautiful/g' | awk -F: '{print $2,$4}' 1> something.report 2> err.log` in Python.

Just ask chatgpt and you’ll get a script, probably makes some tests too if you ask for it.
Post reply on HN