Ask HN: Share a shell script you like
Hi, I'd like to ask what are the shell scripts you enjoy using or find useful?
It might be something you incorporated to your terminal-based workflow. Or maybe some specific scripts that you often reuse. Or you have used it once, but it might be useful to other people. Or maybe you just have a script that is fun to use? Please share
My (not anymore) hidden intention is to gather your recommendations to build an open-source shell script registry https://spellbook.maczan.pl/ Source code is here for you if you want to self host or fork it https://github.com/jmaczan/spellbook
A script I sometimes use is a commands repeater https://registry.spellbook.maczan.pl/repeat-sh/spell.sh You can specify an interval and a flag to reset/keep the terminal's content after a script invocation
Thanks!
86 comments
[ 1.6 ms ] story [ 166 ms ] thread- quickly opening my personal wiki: https://github.com/francium/dotfiles/blob/master/bin/.local/...
- re-run a script when a file changes: https://github.com/francium/dotfiles/blob/master/bin/.local/...
For `while-watchdo` you, you run it like `while-watchdo "echo hi"`, then in my editor, I have a custom shortcut that does `touch .watchfile` causing the command, in this case `echo hi` to run. I prefer this to tools that retrigger commands as soon as you save _any_ file. Also works in docker containers, edit a file on host, command runs in a container.
- https://github.com/koalaman/shellcheck
The author converted it to a vim plugin with the same name, but I use a different vim plugin implementation [mergetool].
[dc]: https://github.com/whiteinge/dotfiles/blob/master/bin/diffco... [mergetool]: https://github.com/idbrii/vim-mergetool
https://github.com/Magnushhoie/fuz
As a nice side-effect, I no longer worry about where I store text (e.g. with Obsidian), as I know I'll find it again if it's there. It helps using memorable keywords though.
https://developer.run/37
build() { ... }
run() { ... }
cmd="$1"
shift
$cmd "$@"
As soon as any complexity or validation is needed I move to python, which is usually a better fit, and arguably even more portable.
The effort to document shell is also somewhat Pyrrhic. The benefit would be... more shell? A more modern shell?
Another goal might be to switch to a real language sooner. Go and Python are the obvious choices, but Swift and Java also support shebang's:
Dependencies are always tricky. swift-sh allows you to declare dependencies as comments after the import: https://github.com/mxcl/swift-shFor anything outside that you're gonna need NixOS or something to many distribution somewhat sane....
Why did they have to kill Linux Standard Base? I never got to experience it myself but it seems pretty great.
a python script with dependencies can be pushed and made available to the world through PyPI, it then becomes really easy to install in isolated environments with `pipx`
I have already adopted pipx run script.py in a project: https://github.com/dbohdan/structured-text-tools/blob/d8dc1a.... It has been convenient and has not caused me problems so far.
I've also had a hell of a time in the past trying to build small python docker images. If you really know what you're doing (and for a novice, that takes a lot of reading) you can usually halve the size of most official containers.
Like, how do I create a python venv that also contains the python runtime (not a symbolic link), and libpython, and how do I pip install something that has no wheels, and wtf are wheels. And now I realize that I absolutely need a pip repo to store the wheels I've built, or my pipelines will run for an hour every time. So I have to figure out how to add my repo to pip. And now, can I statically compile my wheels to make them easier to install in a generic container, without having to find libthis and libthat? Finally, how do I copy '/app' into a clean busybox container and activate the venv to run the app. If you do all of that you can have small (ish) containers and fast pipelines (except for the first run that build wheels and runs a script to upload recently built wheels to your repo so you never have to build them again). Phew. What a lot of work.
And remember, none of this is "Learning to program in Python", but if you're learning to program in Python from scratch nowadays, all of that stuff becomes extremely important. And the range of different tools is confusing. "Are we still using pip? What about venv? Wtf is poetry?"
Or I could simply spend all of that time learning enough Go so I can produce a single binary that can be dropped into a 'FROM scratch' container. Or, as is the case most of the time, I can use alpine Linux and `apk install bash jq curl` and do an astonishing amount of stuff, albeit in nasty bash.
```
```
I wanted to link to a nice web page about the history of the (external) true command but google can't find anything anymore.
Is it this one?
Score: m10k: 10^100, google: 0
If $0 happens to be just "foo", you calculate "foo/" which is incorrect.
$(dirname "$0") calculates ".".
Where it might make sense is if "foo" were on your path (which might include "."): one might then expect $0 to be "foo" as that's what you typed; but, if so, "." is the incorrect directory for all of the non-"." entries in your path! Thankfully, this isn't the case, though: if you run "foo", then $0 is an absolute path to the script.
So like, the next thing which could come up is "the caller did something crazy with $0 that we didn't expect", as, normally, argv[0] can be set to anything at all by the person who runs a program, no matter where the program happens to be located... but, then, if we aren't going to trust $0 as a useful "path" (lol) to getting the correct path at all, what are we even doing here? :(
But, thankfully-again, bash doesn't let that happen: if you write a runner program that calls execl and you override argv0 to just be "foo", bash fixes that; I am honestly not 100% sure of what the exact behavior here is to make that be the result (as there are multiple "obvious" ways to code that mechanism, or even which layer is in charge of it), but the reality is you don't seem to get to control $0 willy-nilly. (edit: in my 4th reading of this comment I realize this is actually the same behavior as the earlier paragraph involving the path and is almost certainly the same mechanism, whatever it happens to be precisely.)
So, I guess I am confused: do you have a concrete reproduction of how you actually got "$0" set to "foo" in the first place? I would honestly love to know and would thank you dearly, as I am sure that there will be some corner case that triggers in a lot of other software and I'll have another crazy/fun thing to look for when I do security audits ;P.
Regardless, the reason I do not use dirname is because I feel the actual reason why my code is a "silly hack" is because I am assuming that / is the directory separator; but, in my personal experience, systems (including embedded devices, restricted containers, Linux derivatives, etc.) that have a shell but do not have dirname--despite it being "POSIX"--are somewhat common whereas systems that have a shell capable of running my script which do not enforce use of / as a directory separator on $0 has maybe at most come up once or twice in my entire life (and it caused other things to break anyway ;P)... and, come to think of it, would also fail to be POSIX compliant anyway, just in a way that is much less likely than missing dirname.
(I will also point out that the behavior of dirname is inconsistent with respect to the root directory: my hack always trails the path with / and never leads to a doubled-up /--which, sure, is often allowable... but is a non-canonical path which I have seen cause security issues before and personally believe should be disallowed--while dirname eats the trailing / everywhere except for / itself, which sucks and is asking for pain.)
That doesn't seem to be the case.
I'm usually on zsh, but I get the same result even varying the shebang inside foo.Does every shell that is currently in use? I don't know. I have access to some embedded systems with BusyBox; let's try that:
Next, on Ubuntu: Next. invoking a script by passing it as an argument to a shell: > the behavior of dirname is inconsistent with respect to the root directoryThat is true. The result of dirname cannot be combined with a name without a slash, but the slash is already there in the root case, an annoying special case. Scripts often take the directory of $0 in order to combine it with relative paths.
Its only indirectly harmful: Your system might get unresponsive and require a hard boot. But then again you and other users might loose unsaved work.
I also curate a shell command cheatsheet: https://github.com/fastily/cheatsheet
It's probably my most used shell app. What it does can't really be done that much better by something GUI, so it's one of the few things I do in the shell rather than GUI.
normalize-audio is another one I like. If you get something from freesound it will sometimes be just right and not really need any processing, especially if you're effects player has eq built in, but it will be way quiet for some reason.
Seems like it would be pretty easy to make a calculator frontend that handled all the random misc tasks like this just be calling out as appropriate.
This script extracts URLs from a text input stream or text files using John Gruber's regular expressions. It requires GNU grep. If your system's default grep command isn't GNU, install ggrep and modify the script accordingly.
Save the script, make it executable, and try
The output should be Usage: Edit: The flag -w enables "Web URL" mode, which finds HTTP(S) URLs as well as just domain names with a path, query, and fragment based on a list of TLDs from 2014. Warning: it will miss new TLDs. You can update the list from https://data.iana.org/TLD/tlds-alpha-by-domain.txt.Source code:
I recommend normally using the script without -w and filtering the output for HTTP(S) URLs. The default regular expression (https://gist.github.com/gruber/249502) does not rely on a list of TLDs.
Edit: Added a warning to the original comment. Thanks for prompting me to.
Here is a version without the "Web URL" regex.
my favorite aliases
aliases are cool but in zsh there's the concept of global aliases makes equivalent to the ones i use the most are i also use starship for my prompt which gives me a lot of information about where i'm at and what's available in $PWD, but on top of that i also set the `chpwd` function to list the last five modified items