Techniques I use to create a great user experience for shell scripts
101–110 of 281 posts
Re: Techniques I use to create a great user experience for shell scripts
#102Don't output ANSI colour codes directly - your output could redirect to a file, or perhaps the user simply prefers no colour. Use tput instead, and add a little snippet like this to the top of your script: command -v tput &>/dev/null && [ -t 1 ] && [ -z "${NO_COLOR:-}" ] || tput() { true; } This checks that the tput command exists (using the bash 'command' builtin rather than which(1) - surprisingly, which can't alwa…
There should just be a command for this. Like echo with a color flag that does something if you’re in a tty.
Re: Techniques I use to create a great user experience for shell scripts
#103Earlier quoted context omitted.
For fun, I took a crack at your example and came up with this craziness (with the caveat it's late and I didn't spend much time on it), which is made a bit more awkward because grep doesn't do capturing groups: netstat -aln \ | grep ESTABLISHED \ | awk '{print $4}' \ | grep -Po '\:\d+$' \ | grep -Po '\d+' \ | sort \ | uniq -c \ | sort -r \ | head -n 10 Changing the awk field to 5 instead of 4 should get you remote po…
I know this sounds like nit-picking but bear with me. It's the point I'm trying to make: 1. Your script outputs an error when run, because 'bash' itself doesn't have netstat as a built-in. That's an external command. In my WSL2, I had to install it. You can't declaratively require this up-front, you script has to have an explicit check... or it'll just fail half-way through. Or do nothing. Or who knows!? PowerShell h…
Technically speaking though, there's no reason you couldn't do that all in bash. It's not the shell that's the problem here (at least, to an extent, passing via text I guess is partly a shell problem). There's no reason you couldn't have an application objNetStat that exported JSON objects and another app that filtered those objects, and another that could group them and another that could sort them. Realistically "Sort-Object Count -Descending -Top 10" could be a fancy alias for "sort | uniq -c | sort -r | head -n 10". And if we're not counting flags and arguments to a function as complexity, if we had our hypothetical objNetstat, I can do the whole thing in one step:
objNetstat --json \
| jq -c '[.[] | select(.state == "ESTABLISHED")] | group_by(.port) | map({port:.[0].port, count: map(.host) | length}) | sort_by(.count) | reverse | [limit(10;.[])]'
One step, single parsing to read in the json. Obviously I'm being a little ridiculous here, and I'm not entirely sure jq's DSL is better than a long shell pipe. But the point is that linux and linux shells could do this if anyone cared enough to write it, and some shells like fish have taken some baby steps towards making shells take advantage of modern compute. Why RedHat or one of the many BSDs hasn't is anyone's guess. My two big bets on it are the aversion to "monolithic" tools (see also systemd), and ironically not breaking old scripts / current systems. The fish shell is great, and I've built a couple shell scripts in it for my own use, and I can't share them with my co-workers who are on Bash/ZSH because fish scripts aren't Bash compatible. Likewise I have to translate anyone's bash scripts into fish if I want to take advantage of any fish features. So even though fish might be better, I'm not going to convince all my co-workers to jump over at once, and without critical mass, we'll be stuck with bash pipelines and python scripts for anything complex.Re: Techniques I use to create a great user experience for shell scripts
#104Nowhere in this list did I see “use shellcheck.” On the scale of care, “the script can blow up in surprising ways” severely outweighs “error messages are in red.” Also, as someone else pointed out, what if I’m redirecting to a file?
Re: Techniques I use to create a great user experience for shell scripts
#105Every time I see a “good” bash script it reminds me of how incredibly primitive every shell is other than PowerShell. Validating parameters - a built in declarative feature! E.g.: ValidateNotNullOrEmpty. Showing progress — also built in, and doesn’t pollute the output stream so you can process returned text AND see progress at the same time. (Write-Progress) Error handling — Try { } Catch { } Finally { } works just l…
Bash is crap and powershell an abomination with a few good ideas. fish, Python, and oilshell (ysh) are ultimately on better footing.
Re: Techniques I use to create a great user experience for shell scripts
#106 sh -x $SCRIPT
shows a debugging trace on the script in a verbose way, it's unvaluable on errors.You can use it as a shebang too:
#!/bin/sh -xRe: Techniques I use to create a great user experience for shell scripts
#107Not trying to offend anyone here but I think shell scripts are the wrong solution for anything over ~50 lines of code. Use a better programming language. Go, Typescript, Rust, Python, and even Perl come to mind.
Try running a 6 month old Python project that you haven't run in that time and report back. Meanwhile, 10 year old Bash scripts I've written still run unmodified. Winner by a mile (from a software-longevity and low-maintenance perspective at least): Bash
Re: Techniques I use to create a great user experience for shell scripts
#108if [ "$(uname -s)" == "Linux” ]; then stuff-goes-here else # Assume MacOS While probably true for most folks, that’s hardly what I’d call great for everybody not on Linux or a Mac.
Eh. It’s true for most, and if not, it’s probably still a *BSD, so there’s a good chance that anything written for a Mac will still work. That said, I’ve never used any of the BSDs, so I may be way off here.
Re: Techniques I use to create a great user experience for shell scripts
#109Earlier quoted context omitted.
> shell scripts are the wrong solution for anything over ~50 lines of code. I don't think LOC is the correct criterion. I do solve many problems with bash and I enjoy the simplicity of shell coding. I even have long bash scripts. But I do agree that shell scripting is the right solution only if = you can solve the problem quickly = you don't need data structures = you don't need math = you don't need concurrency
In my opinion, shell scripting is the right tool when you need to do a lot of calling programs, piping, and redirecting. Such programs end up being cumbersome in "proper" languages.
but yea, shell is foremost a composition language/environment
Re: Techniques I use to create a great user experience for shell scripts
#110Earlier quoted context omitted.
I know this sounds like nit-picking but bear with me. It's the point I'm trying to make: 1. Your script outputs an error when run, because 'bash' itself doesn't have netstat as a built-in. That's an external command. In my WSL2, I had to install it. You can't declaratively require this up-front, you script has to have an explicit check... or it'll just fail half-way through. Or do nothing. Or who knows!? PowerShell h…
Sure, I'm not arguing that having a set of well defined outputs and passing objects around wouldn't be better. You're talking to someone that often laments that SmallTalk was not more popular. But you'd need to get the entire OSS community to land on a single object representation and then get them to independently change all the tools to start outputting the object version. PowerShell and Microsoft have the advantag…
All joking aside, that's not a bad solution to the underlying problem. Fundamentally, unstructured data in shell pipelines is much of the issue, and JSON can be used to provide that structure. I'm seeing more and more tools emit or accept JSON. If one can pinch their nose and ignore the performance overhead of repeatedly generating and parsing JSON, it's a workable solution.
Years ago, a project idea I was really interested for a while was to try to write a shell in Rust that works more like PowerShell.
Where I got stuck was the fundamentals: PowerShell heavily leans on the managed virtual machine and the shared memory space and typed objects that enables.
Languages like C, C++, and Rust don't really have direct equivalents of this and would have to emulate it, quite literally. At that point you have none of the benefits of Rust and all of the downsides. May as well just use pwsh and be done with it!
Since then I've noticed JSON filling this role of "object exchange" between distinct processes that may not even be written in the same programming language.
I feel like this is going to be a bit like UTF-8 in Linux. Back in the early 2000s, Windows had proper Unicode support with UTF-16, and Linux had only codepages on top of ASCII. Instead of catching up by changing over to UTF-16, Linux adopted UTF-8 which in some ways gave it better Unicode support than Windows. I suspect JSON in the shell will be the same. Eventually there will be a Linux shell where everything is always JSON and it will work just like PowerShell, except it'll support multiple processes in multiple languages and hence leapfrog Windows.