I didn't like the idea. I prefer the alternative approach: _I_ decide the order of dirs in the PATH env. If I introduce an executable with a name, that overrides a system one - I probably do that intentionally.
If I introduce an alias (like `grep='grep --binary-files=without-match --ignore-case --color=auto`) that matches the name of a system binary - I probably do that intentionally.
And if I EVER need to call grep without my alias - I just prefix it with a backslash: \grep will search with case sensitivity and no color and will scan binaries.
Looked so backwards to me, too. However, I decided to give it a go, anyway. Now, I have some scripts and small commands which start with a comma, and it looks neat and time saving.
Yes, I can do path ordering to override usual commands. However, having a set of odd-job scripts which start with a comma gives a nice namespacing capability alongside a well narrowed-down tab-completion experience.
While it's not the neatest thing around, it works surprisingly well.
Another idea which looks useless until you start using is text expanders (i.e.: Espanso and TextExpander).
Either adding your script directory in front of the PATH, or creating `alias` that provide a full path to your script where a conflict exists, makes a whole lot more sense to me.
I've never had this collision problem yet, despite appending my script directory to the end, but I'll use either of the above solutions if that ever becomes a problem.
One rarely actually needs to shadow binaries. Some cases could indeed be covered by introducing an alias that binds the binary's name to call a different copy of that binary.
You use shadowing to fix issues where you install some software that expects you to have a sane and ~recent version of some tool like git, but you don't as your system provides that binary and unfortunately it is either not sane (not GENERALLY sane [while it could be sane for system scripts]) or not recent enough. In that case the program's function would simply fail if it would call the system's binary and you shadow the binary with your version to fix that.
> adding your script directory in front of the PATH
That's a poor advice for the scripts you call relatively frequently. Instead, (as a general approach, we aren't discussing some particular script) don't use shadowing for scripts: just pick a non-conflicting script name and append the script's dir to $PATH.
Just on your first suggestion, this also means that if a person or process can drop a file (unknown to you) into your ~/bin/ then they can wreak havoc. Eg they can override `sudo` to capture your password, or override `rm` to send your files somewhere interesting, and so on.
Btw on the second suggestion, I think there's a command named `command` that can help with that sort of thing, avoids recursive pitfalls.
That would require someone to already want to sabotage me in particular, learn my private workflows, and also have write access to my home folder. At that point, All is Lost.
Don't tell people to sacrifice agency for apocalypse insurance that doesn't work, lol
No, don't do that as a precaution. As others have already answered correctly - it's too late to worry about such things if a malicious agent has write access to your ${HOME} dir.
I do this, and routinely shadow commands with my own wrappers to do things like set environment variables.
And then there’s Claude. It deletes whatever it finds at ~/.local/bin/claude, so I have to use a shell function instead to invoke the full path to my wrapper.
My goal is to prevent Claude from blowing up my computer by erasing things it shouldn't touch. So the philosophy of my sanboxing is "You get write access to $allowlist, and read access to everything except for $blocklist".
I'm not concerned about data exfiltration, as implementing it well in a dev tool is too difficult, so my rules are limited to blocking highly sensitive folders by name.
Random flags added to core tools are done with aliases, which do not affect the launched processes, not by shadowing them in ~/bin. Shadowing in ~/bin are for cases where a newer (compared to the system-wide version) or custom version of a tool is needed.
Not really, since if one usually does that - they probably understand the possible consequences and don't shadow whatever they like, but do it carefully.
On MacOS I shadow that way just curl and git binaries to the versions installed from homebrew and nothing has broken (yet). I know that tar on MacOS is also a weirdo that I'd rather shadow with the homebrew's gtar, but their args are different and I of course understand that there's a high probability of something in system to be bound to mac's version of tar, so here I better remember to use 'sane' tar as gtar or use an alias (instead of shadowing the binary) for tar to use gtar (because aliases are for users, not for system scripts/processes).
And on my home desktop's Debian - I don't even use shadowing of binaries at all (never needed it).
Also, I just realized: I change PATH env via my shell's rc script (~/.zshrc), so I probably could worry even less about shadowing system binaries (like tar on MacOS) possibly breaking things.
Seemed like it was more than that, but the comment is ambiguous. I took it to mean "show me all the commands which are shadowed" not "is this command shadowed"...
Others have already given valid answers: grep is not ripgrep [their params don't match], so it's a bad idea to alias 'grep' to use ripgrep. But it's okay to alias 'ripgrep' (or 'rg' or whatever) to use ripgrep with some args.
Worth pointing out that with Nix/NixOS this problem doesn't exist.
The problem in other distros is that if you prefix PATH so that it contains your executable "foo", and then run a program that invokes "foo" from PATH and expects it to do something else, the program breaks.
With Nix, this problem does not exist because all installed programs invoke all other programs not via PATH but via full absolute paths starting with /nix/store/HASH...
[user@nixos:~]$ which grep
/run/current-system/sw/bin/grep
[user@nixos:~]$ ls -l /run/current-system/sw/bin/grep
lrwxrwxrwx 1 root root 65 Jan 1 1970 /run/current-system/sw/bin/grep -> /nix/store/737jwbhw8ji13x9s88z3wpp8pxaqla92-gnugrep-3.12/bin/grep
Basically, it is still in your environment, so I don't see how he can claim that this problem doesn't exist in Nix, unless you use flakes like a proper Nix afficionado.
Glad it worked for OP, but I've never once in 30+ years of this had a conflict that did something I didn't want. ~/bin/ is early in my PATH, and for a good reason. Things I put in there I want to take precedence, so I use this to purposely override provided bins. (Though I can only think of one time I wanted to do that, too.)
Tangentially related. Don't ever put "." in your PATH. I used to do this to avoid typing the "./" to execute something in my current directory. BAD IDEA. It can turn a typo into a fork bomb. I took down a production server trying to save typing two characters.
I like to follow my own convention where I name files with shell scripts with an extension: .sh for POSIX-compatible scripts, .bash for scripts with bashisms or .zsh for scripts with zshisms.
If I ever wanted to achieve what you initially wanted to achieve - I could use something like
alias -s sh=sh
alias -s bash=bash
alias -s zsh=zsh
Just like I do bind .txt and .conf to 'less', .pdf to 'qpdf', .json to 'ijq', video formats to 'mpv' and so on.
A trip down the recursion hole. Also, scripts will inherit the relative path so they will have different absolute paths from each other. Seems easier to just type ./ so it's kinda funny in a "UNIX haters handbook" kind of way, but it's not even a fault in linux's command interface in that case. We've all been there.
Oh, that's without even going into the security risks and loss of portability.
Presumably a script that aliases a common thing or something and then it uses the same. E.g. someone adds ./sed that has some default params and calls sed. You’re intended to call it with ~/not-in-path/defaulted/sed and it is supposed to then call sed but instead calls itself if it’s earlier in the path hierarchy.
Might even be as simple as “detect if I’m running gnu sed or bsd sed and use the appropriate one”. Obviously you can not have this problem by being smart about other things but defense in depth right?
One could set an env var to their local bin dir which is otherwise not in the path, like L=/home/ahepp/.local/bin, and then do $L/mycommand. Doesn't meet the OP's requirement of no shift key.
Or prefix files in the local bin dir with a couple letters from your username, like /home/ahepp/.local/bin/ah-mycommand
Kinda makes no sense to me: so you don't use '--' as a prefix, you use it in the middle of an alias, so you first have to autocomplete, say, 'gi' not to 'git' but to 'git--progress'. What does that alias do? Doesn't it call git with some args? If so - why not just alias it to git?
There’s this program on nix that lets you type a comma, then any application name that exists anywhere in the Nix repos. It then downloads that app and runs it once, without “installing” it. Sometimes I find myself running something dozens of times this way before I realize it should probably be in my config.
I'm not sure I'll ever understand why they replaced their working ~50 line shell script with a Rust program that just shells out to the same nix-* commands. I appreciate that there are some safety benefits, but that program is just not complex enough to benefit.
Prefixing commands solves the namespace problem and discoverability (at least partly). I use a slightly more sophisticated method, which helps me remember which custom utilities are available and how to use them: sd [1], a light wrapper written for zsh that, in addition to namespaces, provides autocompletion, custom help texts + some other QoL enhancements. Can definitely recommend if you're looking for something a bit more fancy.
Using commas in filenames feels kind of weird to me, but I do use a comma as the initiator for my Bash key sequences.
For example:
,, expands to $
,h expands to --help
,v expands to --version
,s prefixes sudo
You put keyseqs in ~/.inputc, set a keyseq-timeout, and it just works.
Global aliases are a zsh feature and not avaliable in bash. So if you want:
openssl ,v
to expand to...
openssl --version
readline seems like the way to go.
Then again most of the examples OP gave are usually available as short options, and aliasing ,s to sudo is certainly possible. So the only one which makes sense to me is ,,=$. But it's probably not worth the trouble to my muscle memory.
It's not a completely non special character: for instance in bash it's special inside braces in the syntax where "/{,usr/}bin" expands to "/bin /usr/bin". But the need to start that syntax with the open brace will remind you about the need to escape a literal comma there if you ever want one.
Oh, that might be true, I do remember encountering some escaping issues when creating a more complex POSIX (or bash) script that involved lists and iterating through stuff.
I see Bash only uses commas in Brace expansions:
file{1,2,3}.txt
# file1.txt file2.txt file3.txt
I guess it would only be a problem if you want to expand
This is one of those ideas that is so simple and elegant that it makes you think “why did I never think of doing this?!”
Neat trick! I don’t think I’ll namespace everything this way, because there’s some aliases and commands I run so often that the comma would get annoying, but for other less frequently used helper scripts then this will be perfect!
I do something similar with build trees, naming them +build, +cross-arm etc.
This convention was suggested by the GNU Arch version control system years ago (maybe 20??), but it's really useful for the same tab completion reason and I have kept it for almost two decades, even when I switched to git.
But that's the killer feature for me! I always forget the little commands I've written over the years, whereas a leading comma will easily let me list them.
This is utterly unhinged. I freaking love it. It reminds me of the old joke about prisoners and numbers for jokes (Stanislaw Lem has a similar concept in a book):
A prisoner, new to a particular cell block, was surprised to discover that his fellow inmates passed much of their day by calling out numbers, after which they would laugh heartily for a few moments. Every few minutes an inmate would call out a number and everyone would laugh, and then, after a few moments of silence, someone else would call out a number, and once again laughter. The inmate asked one of the other inmates whom he'd come to know to explain this strange behavior to him.
"It's simple", came the reply. "We know all of our jokes by heart, and there's really no reason to tell them at lenght. Instead, we simply call them out by number."
Though this was strange to him, the new inmate thought he'd join in on the fun. After a few weeks listening to the jokes, he took some initiative and called out "number 27!". But nobody laughed. This seemed very strange to him, since he'd heard others call out that same number, with everyone laughing afterwards. After waiting and waiting, with still no laughter, he finally asked: "why is it that when others call out that joke you laugh, and when I called it, nobody laughed?".
People tend to want some separation between what's theirs and what's others. Other programs/scripts quite often put something into ~/.local/bin, so it's not yours actually, it's theirs.
I personally use both, each for different purposes.
I snapshot my entire home directory every hour (using btrfs+snapper), but I exclude ~/.local/ from the snapshots. So I use ~/.local/bin/ for third-party binaries, since there's no reason to back those up; and ~/bin/ for scripts that I wrote myself, since I definitely want to back those up.
This is a pretty idiosyncratic use though, so I'd be surprised if many other people treated both directories this way.
I prefer ~/bin/ for my scripts, links to specific commands, etc.
~/.local/bin is tedious to write, when I want to see directory content and - most important - I treat whole ~/.local/ as managed automatically by other services and volatile.
Personally I use ~/opt//bin where ~/opt is a ‘one stop shop’ containing various things, including a symlink to ~/local and directories or symlinks for things that don't play well with others (e.g. cargo, go), and an ~/opt/prefer/bin that goes at the start of PATH containing symlinks to resolve naming conflicts.
(Anything that modifies standard behaviour is not in PATH, but instead a shell function present only in interactive shells, so as not to break scripts.)
Unix lore: Early unix had two-letter names for most common names to make them easy to type on crappy terminals, but no one* letter command names because the easier were reserved for personal use.
I thought was for mixin externally provided systems like Homebrew, local is for machine or org-level customizations, and ~ is for user-level customizations.
/opt showed up as a place for packaged software, where each package (directory) has its own bin/, lib/, man/, and so on, to keep it self-contained rather than installing its files in the main hierarchy. ~/opt is just a per-user equivalent, analogous to /usr/local vs ~/.local.
The advantage of /opt is that multi-file software stays together. The disadvantage is that PATHs get long.
I do this, and it's a huge quality of life improvement. No so much because of shadowing existing binaries, but for better command auto-complete. For example: I have a bunch of tmux utilities and all start with `,t` which is not a polluted command-name prefix compared to just `t`.
But I'm now facing the problem that LLM agents don't like this, and when I instruct them to run certain tools, they remove the leading comma. It's normally fixed with one extra sentence in the prompt, but still inconvenient.
instead of using ~/bin I use ~/installed/bin, sometimes I need build a command from source then install it, which might have share/ man/ etc so I can avoid installing them under the home dir.
Nice although I think the ASCII comma feels wrong as part of a filename even if for purely aesthetic reasons.
If we want to stay within (lowercase) alphabetic Latin characters I think prefixing with the least common letters or bigrams that start a word (x, q, y, z, j) is best.
`y' for instance only autocompletes to `yes' and `ypdomainname' on my path.
Choosing a unique bigram is actually quite easy and a fun exercise.
And we can always use uppercase Latin letters since commands very rarely use never mind start with those.
As a non-native English speaker I just name them in my native language or using British English spelling.
I have a command named "decolour", which strips (most) ANSI escape codes. Clear as day what it does, almost nobody uses this spelling when naming commands that later land as part of a distribution.
Properly manage PATH for the context you're in and this is a non-issue. This is the solution used by most programming environments these days, you don't carry around the entire npm or PyPI ecosystem all the time, only when you activate it.
Then again, I don't really believe in performing complex operations manually and directly from a shell, so I don't really understand the use-case for having many small utilities in PATH to begin with.
This is one of those "obvious in hindsight" tricks. The comma prefix gives you a namespace that's guaranteed to never collide with system binaries, shell builtins, or anything from a package manager.
I do something similar with my personal scripts — prefix them with a short namespace. The real win isn't just avoiding collisions though, it's tab completion. Type the prefix and tab, and you immediately see all your custom stuff without wading through hundreds of system commands.
The 2009 date on this is wild. Some of these simple unix conventions age better than most frameworks.
I think its a fairly good idea - but for myself, i had already mapped csh’s default history character (!) to a comma (,) for the same reason - no shift key to invoke.
In many contexts in which I am trying to deconflict namespaces, I use my initials. I hadn't thought about it in this particular context, though now that I do, it seems fortunate that I am ced rather than sed.
I don’t think this is a terrible idea, though stylistically it bothers me. I suppose you could simply have a prefix command router that would essentially do the same thing. I also started using “task” recently and it’s been a game changer for my CLI life.
It is like make but designed specifically for the way non-C(++) users - people like me for example adding scripts like "make run" and "make build" to my node/python/PHP/etc repos - use it. It is great! I still don't use it literally just because make is already installed on any *nix system I encounter day to day.
I tried a variant or this idea so many years ago after I leaned git and rearranged some of my personal tools as subcommands (like git) of a single executable named "dude,"
can someone explain security consideration of placing scripts into $HOME?
Some time ago I moved all my scripts to /usr/local/bin, because I feel that this is better from security perspective.
There are no security implications, on the contrary.
It is objectively cleaner to keep your user scripts in your home, that way they are only in _your_ PATH, whereas putting them in /usr/[local/]bin implicitly adds them to every [service] user on the machine, which I can see creating obscure undesired effets.
Not even mentioning the potential issues with packages that could override your scripts at install, unexpected shadowing of service binaries, setuid security implications, etc.
I appreciate the idea, but the comma just looks horrible to me as part of a filename. I can imagine someone unfamiliar with the naming scheme to get confused.
I'd prefer to use underscore (when writing BASH scripts, I name all my local variables starting with underscore), but a simple two or three letter prefix would also work. I don't like the idea of a punctuation prefix as punctuation usually has a specific meaning somewhere and including it as the first character in a filename looks wrong. (e.g. Comma is typically used as a list separator and it's a bit of cognitive dissonance to see it not used in that context)
> I don't like the idea of a punctuation prefix as punctuation usually has a specific meaning somewhere and including it as the first character in a filename looks wrong.
Well dotfiles demonstrate that punctuation can have a special meaning in filenames.
I'm not convinced by "quicker to type" arguments as that's rarely the bottleneck, so I'm perfectly happy with using underscores in filenames and variables. I wouldn't use underscore as the beginning character of a filename unless it had a specific meaning to me (e.g. temporary files), so I'd be more inclined to use a two or three character prefix instead.
That’s a more meaningful prefix than “,” at the expense of a couple more key strokes. I consider that to still be a win in the book of tab completions.
Nowadays, I tend to skip using a personal prefix and just try to name commands with a suitable verb in front (e.g. "backupMySQL") and ensure that there's no name collisions.
This was actually the same feeling I had when I tried to learn perl. I just had a visceral dislike for "my" as the keyword to declare a local variable.
I read this blog a few years ago, and implemented it soon after with a refresh of my rc files and shortcuts. Gamechanger - has helped me every single day since. It’s easy to remember, autocompletes easily, and adds a little flair of personalization.
I didn't like the idea. I prefer the alternative approach: _I_ decide the order of dirs in the PATH env. If I introduce an executable with a name, that overrides a system one - I probably do that intentionally.
If I introduce an alias (like `grep='grep --binary-files=without-match --ignore-case --color=auto`) that matches the name of a system binary - I probably do that intentionally.
And if I EVER need to call grep without my alias - I just prefix it with a backslash: \grep will search with case sensitivity and no color and will scan binaries.
Looked so backwards to me, too. However, I decided to give it a go, anyway. Now, I have some scripts and small commands which start with a comma, and it looks neat and time saving.
Yes, I can do path ordering to override usual commands. However, having a set of odd-job scripts which start with a comma gives a nice namespacing capability alongside a well narrowed-down tab-completion experience.
While it's not the neatest thing around, it works surprisingly well.
Another idea which looks useless until you start using is text expanders (i.e.: Espanso and TextExpander).
I never knew that what I've known as 'hotstrings' (since the AutoHotKey days) other sometimes also call 'text expanders'.
Love Alfred Snippets for this same text expander need.
Either adding your script directory in front of the PATH, or creating `alias` that provide a full path to your script where a conflict exists, makes a whole lot more sense to me.
I've never had this collision problem yet, despite appending my script directory to the end, but I'll use either of the above solutions if that ever becomes a problem.
From my own aliases:
There are others with flags added. These are the ones that override the builtin MacOS versions that aren't up-to-date.One rarely actually needs to shadow binaries. Some cases could indeed be covered by introducing an alias that binds the binary's name to call a different copy of that binary.
You use shadowing to fix issues where you install some software that expects you to have a sane and ~recent version of some tool like git, but you don't as your system provides that binary and unfortunately it is either not sane (not GENERALLY sane [while it could be sane for system scripts]) or not recent enough. In that case the program's function would simply fail if it would call the system's binary and you shadow the binary with your version to fix that.
> adding your script directory in front of the PATH
That's a poor advice for the scripts you call relatively frequently. Instead, (as a general approach, we aren't discussing some particular script) don't use shadowing for scripts: just pick a non-conflicting script name and append the script's dir to $PATH.
> That's a poor advice for the scripts you call relatively frequently.
Why? It protects you from someone else (cough updated packages introducing new commands cough) picking a name you already use.
TIL: Backslash overrides alias - wow!
Thanks, mathfailure - this genuinely improves my life!
Just on your first suggestion, this also means that if a person or process can drop a file (unknown to you) into your ~/bin/ then they can wreak havoc. Eg they can override `sudo` to capture your password, or override `rm` to send your files somewhere interesting, and so on.
Btw on the second suggestion, I think there's a command named `command` that can help with that sort of thing, avoids recursive pitfalls.
That would require someone to already want to sabotage me in particular, learn my private workflows, and also have write access to my home folder. At that point, All is Lost.
Don't tell people to sacrifice agency for apocalypse insurance that doesn't work, lol
If someone can drop a file in your ~/bin, they can also edit your shell’s startup files to add their malicious command.
I think it's already game over if they have access to your home directory. They can also edit your path at that point.
The issue of rootless malicious command overrides is solved by typing the whole path, such as "/bin/sudo".
No, don't do that as a precaution. As others have already answered correctly - it's too late to worry about such things if a malicious agent has write access to your ${HOME} dir.
While true, what you describe is very unlikely to happen and most definitely won’t happens on systems where i’m the only users.
I do this, and routinely shadow commands with my own wrappers to do things like set environment variables.
And then there’s Claude. It deletes whatever it finds at ~/.local/bin/claude, so I have to use a shell function instead to invoke the full path to my wrapper.
You can use an alias, which takes priority over $PATH. e.g. I have this in .zhsrc to override the "claude" executable to run it in the OS sandbox:
How does your sandbox ruleset look? I've been using containers on Linux but I don't have a solution for macOS.
Here's my ruleset https://gist.github.com/eugene1g/ad3ff9783396e2cf35354689cc6...
My goal is to prevent Claude from blowing up my computer by erasing things it shouldn't touch. So the philosophy of my sanboxing is "You get write access to $allowlist, and read access to everything except for $blocklist".
I'm not concerned about data exfiltration, as implementing it well in a dev tool is too difficult, so my rules are limited to blocking highly sensitive folders by name.
Any severe side effects so far? Have you set PATH up somehow so it is effect only on interactive prompt, and not in the launched processes?
Because I cannot imagine much 3rd party scripts working with random flags added to core tools
I also do this.
Random flags added to core tools are done with aliases, which do not affect the launched processes, not by shadowing them in ~/bin. Shadowing in ~/bin are for cases where a newer (compared to the system-wide version) or custom version of a tool is needed.
Not really, since if one usually does that - they probably understand the possible consequences and don't shadow whatever they like, but do it carefully.
On MacOS I shadow that way just curl and git binaries to the versions installed from homebrew and nothing has broken (yet). I know that tar on MacOS is also a weirdo that I'd rather shadow with the homebrew's gtar, but their args are different and I of course understand that there's a high probability of something in system to be bound to mac's version of tar, so here I better remember to use 'sane' tar as gtar or use an alias (instead of shadowing the binary) for tar to use gtar (because aliases are for users, not for system scripts/processes).
And on my home desktop's Debian - I don't even use shadowing of binaries at all (never needed it).
Also, I just realized: I change PATH env via my shell's rc script (~/.zshrc), so I probably could worry even less about shadowing system binaries (like tar on MacOS) possibly breaking things.
I do the same thing, but I also have a command that shows me what functions or scripts might be shadowing other scripts
Care to share?
the sibling answer but with `-a` before command name, will display all path hits for a command.
Seemed like it was more than that, but the comment is ambiguous. I took it to mean "show me all the commands which are shadowed" not "is this command shadowed"...
> If I introduce an executable with a name, that overrides a system one
... and breaks existing scripts that reference the system one, right?
Not if it is an alias.
But yes if it’s another executable.
curious if you're customizing anyway, why not use eg ripgrep?
Others have already given valid answers: grep is not ripgrep [their params don't match], so it's a bad idea to alias 'grep' to use ripgrep. But it's okay to alias 'ripgrep' (or 'rg' or whatever) to use ripgrep with some args.
repgrep's CLI options and general behavior are different from grep. I tend to use both for different things.
Not OP, but I use ripgrep and customize it with an alias as well, so it applies equally there
Worth pointing out that with Nix/NixOS this problem doesn't exist.
The problem in other distros is that if you prefix PATH so that it contains your executable "foo", and then run a program that invokes "foo" from PATH and expects it to do something else, the program breaks.
With Nix, this problem does not exist because all installed programs invoke all other programs not via PATH but via full absolute paths starting with /nix/store/HASH...
The "solution" of only ever using full absolute paths works on any unix system, doesn't it?
So if I want to use grep in a small script, do I have to write:
/nix/store/grep-hash -flags files | /nix/store/head-hash
instead of: "grep -flags files | head"?
Glad it worked for OP, but I've never once in 30+ years of this had a conflict that did something I didn't want. ~/bin/ is early in my PATH, and for a good reason. Things I put in there I want to take precedence, so I use this to purposely override provided bins. (Though I can only think of one time I wanted to do that, too.)
Tangentially related. Don't ever put "." in your PATH. I used to do this to avoid typing the "./" to execute something in my current directory. BAD IDEA. It can turn a typo into a fork bomb. I took down a production server trying to save typing two characters.
I like to follow my own convention where I name files with shell scripts with an extension: .sh for POSIX-compatible scripts, .bash for scripts with bashisms or .zsh for scripts with zshisms.
If I ever wanted to achieve what you initially wanted to achieve - I could use something like
alias -s sh=sh
alias -s bash=bash
alias -s zsh=zsh
Just like I do bind .txt and .conf to 'less', .pdf to 'qpdf', .json to 'ijq', video formats to 'mpv' and so on.
Might I ask exactly what the typo was?
Elaborate?? "." has been at the end of my PATH for like 20 years.
Just to save the trouble of writing './'?
Why does this go wrong and in what situation?
A trip down the recursion hole. Also, scripts will inherit the relative path so they will have different absolute paths from each other. Seems easier to just type ./ so it's kinda funny in a "UNIX haters handbook" kind of way, but it's not even a fault in linux's command interface in that case. We've all been there.
Oh, that's without even going into the security risks and loss of portability.
Presumably a script that aliases a common thing or something and then it uses the same. E.g. someone adds ./sed that has some default params and calls sed. You’re intended to call it with ~/not-in-path/defaulted/sed and it is supposed to then call sed but instead calls itself if it’s earlier in the path hierarchy.
Might even be as simple as “detect if I’m running gnu sed or bsd sed and use the appropriate one”. Obviously you can not have this problem by being smart about other things but defense in depth right?
lol. What a beautiful footgun — for such a tiny optimization.
One could set an env var to their local bin dir which is otherwise not in the path, like L=/home/ahepp/.local/bin, and then do $L/mycommand. Doesn't meet the OP's requirement of no shift key.
Or prefix files in the local bin dir with a couple letters from your username, like /home/ahepp/.local/bin/ah-mycommand
Most of my aliases contain `--` for the same reason, `git--progress`, `grep--rIn`, `nvidia--kill`, `ollama--restart`, `rsync--cp`, `pdf--nup`...
Easy autocomplete, I know there won't be any collision, and which command is mine.
Kinda makes no sense to me: so you don't use '--' as a prefix, you use it in the middle of an alias, so you first have to autocomplete, say, 'gi' not to 'git' but to 'git--progress'. What does that alias do? Doesn't it call git with some args? If so - why not just alias it to git?
Great hack!
There’s this program on nix that lets you type a comma, then any application name that exists anywhere in the Nix repos. It then downloads that app and runs it once, without “installing” it. Sometimes I find myself running something dozens of times this way before I realize it should probably be in my config.
https://github.com/nix-community/comma
I'm not sure I'll ever understand why they replaced their working ~50 line shell script with a Rust program that just shells out to the same nix-* commands. I appreciate that there are some safety benefits, but that program is just not complex enough to benefit.
Prefixing commands solves the namespace problem and discoverability (at least partly). I use a slightly more sophisticated method, which helps me remember which custom utilities are available and how to use them: sd [1], a light wrapper written for zsh that, in addition to namespaces, provides autocompletion, custom help texts + some other QoL enhancements. Can definitely recommend if you're looking for something a bit more fancy.
[1] https://github.com/ianthehenry/sd
Using commas in filenames feels kind of weird to me, but I do use a comma as the initiator for my Bash key sequences. For example: ,, expands to $ ,h expands to --help ,v expands to --version ,s prefixes sudo
You put keyseqs in ~/.inputc, set a keyseq-timeout, and it just works.
You could also do this sort of thing with XCompose, yes?
would an alias just work in this use-case?
Global aliases are a zsh feature and not avaliable in bash. So if you want:
to expand to... readline seems like the way to go.Then again most of the examples OP gave are usually available as short options, and aliasing ,s to sudo is certainly possible. So the only one which makes sense to me is ,,=$. But it's probably not worth the trouble to my muscle memory.
also. did you mean .inputrc ?
It's not a completely non special character: for instance in bash it's special inside braces in the syntax where "/{,usr/}bin" expands to "/bin /usr/bin". But the need to start that syntax with the open brace will remind you about the need to escape a literal comma there if you ever want one.
You may enjoy learning about the [ binary.
Until someone forces you to use a file system that cannot tolerate commas...
Which file system would that be?
What about using the filename in arrays in bash/sh?
But Bash arrays don’t use comma, what’s the problem?
Oh, that might be true, I do remember encountering some escaping issues when creating a more complex POSIX (or bash) script that involved lists and iterating through stuff.
I see Bash only uses commas in Brace expansions:
file{1,2,3}.txt # file1.txt file2.txt file3.txt
I guess it would only be a problem if you want to expand
Imagine seeing this code:
Ah, I see you're a man of culture as well!
Have you met Bash? It’s a shrine to space-delimited everything lol
I reworded my comment for clarity now.
You never used CVS/RCS with its “,v” files?
This is one of those ideas that is so simple and elegant that it makes you think “why did I never think of doing this?!”
Neat trick! I don’t think I’ll namespace everything this way, because there’s some aliases and commands I run so often that the comma would get annoying, but for other less frequently used helper scripts then this will be perfect!
I do something similar with build trees, naming them +build, +cross-arm etc.
This convention was suggested by the GNU Arch version control system years ago (maybe 20??), but it's really useful for the same tab completion reason and I have kept it for almost two decades, even when I switched to git.
It was suggested by Tom Lord (RIP), who used it heavily long before he wrote GNU Arch.
File names or directories starting with a comma where considered “junk”, and ones with a plus sign I think where considered “precious”.
Maybe then try ending your commands with a comma so that you don’t break first-char autocomplete !
But that's the killer feature for me! I always forget the little commands I've written over the years, whereas a leading comma will easily let me list them.
I use a different prefix character, e.g. "[", but I have been doing this for years
I started using a prefix because I like very short script names that are easy to type
I prefer giving scripts numbers instead of names
Something like "[number"
I use prefixes and suffixes to group related scripts together, e.g., scripts that run other scripts
I have an executable directory like ~/bin but it's not called bin. It contains 100s of short scripts
> I prefer giving scripts numbers instead of names
> Something like "[number"
> It contains 100s of short scripts
So you call scripts like [1 [2 [3 [4 ... and remember what each one of them does? If yes - that's nuts, I'd visit a doctor.
This is utterly unhinged. I freaking love it. It reminds me of the old joke about prisoners and numbers for jokes (Stanislaw Lem has a similar concept in a book):
A prisoner, new to a particular cell block, was surprised to discover that his fellow inmates passed much of their day by calling out numbers, after which they would laugh heartily for a few moments. Every few minutes an inmate would call out a number and everyone would laugh, and then, after a few moments of silence, someone else would call out a number, and once again laughter. The inmate asked one of the other inmates whom he'd come to know to explain this strange behavior to him.
"It's simple", came the reply. "We know all of our jokes by heart, and there's really no reason to tell them at lenght. Instead, we simply call them out by number."
Though this was strange to him, the new inmate thought he'd join in on the fun. After a few weeks listening to the jokes, he took some initiative and called out "number 27!". But nobody laughed. This seemed very strange to him, since he'd heard others call out that same number, with everyone laughing afterwards. After waiting and waiting, with still no laughter, he finally asked: "why is it that when others call out that joke you laugh, and when I called it, nobody laughed?".
The reply promptly came: "You told it wrong".
do you publish dotfiles and scripts anywhere? I'm interested to see them
Why so many people use ~/bin/? What’s wrong with ~/.local/bin?
People tend to want some separation between what's theirs and what's others. Other programs/scripts quite often put something into ~/.local/bin, so it's not yours actually, it's theirs.
I personally use both, each for different purposes.
I snapshot my entire home directory every hour (using btrfs+snapper), but I exclude ~/.local/ from the snapshots. So I use ~/.local/bin/ for third-party binaries, since there's no reason to back those up; and ~/bin/ for scripts that I wrote myself, since I definitely want to back those up.
This is a pretty idiosyncratic use though, so I'd be surprised if many other people treated both directories this way.
Random things are installed in ~/.local/bin. In ~/bin I have only what I put there.
I prefer ~/bin/ for my scripts, links to specific commands, etc.
~/.local/bin is tedious to write, when I want to see directory content and - most important - I treat whole ~/.local/ as managed automatically by other services and volatile.
I use ~/.local/bin for installed programs, and ~/bin for my own scripts.
Personally I use ~/opt//bin where ~/opt is a ‘one stop shop’ containing various things, including a symlink to ~/local and directories or symlinks for things that don't play well with others (e.g. cargo, go), and an ~/opt/prefer/bin that goes at the start of PATH containing symlinks to resolve naming conflicts.
(Anything that modifies standard behaviour is not in PATH, but instead a shell function present only in interactive shells, so as not to break scripts.)
Unix lore: Early unix had two-letter names for most common names to make them easy to type on crappy terminals, but no one* letter command names because the easier were reserved for personal use.
What's the difference between opt and local?
I thought was for mixin externally provided systems like Homebrew, local is for machine or org-level customizations, and ~ is for user-level customizations.
/opt showed up as a place for packaged software, where each package (directory) has its own bin/, lib/, man/, and so on, to keep it self-contained rather than installing its files in the main hierarchy. ~/opt is just a per-user equivalent, analogous to /usr/local vs ~/.local.
The advantage of /opt is that multi-file software stays together. The disadvantage is that PATHs get long.
The latter is XDG.
~/bin predates it.
And of course you can use both.
Why would you want to store your binaries in a hidden directory?
It kind of goes against the idea why dotfiles are dot-prefixed.
~/bin/ preceeds the XDG Base Directory Specification.
~/.local was only invented around 2003 and gained widespread usage maybe 15 years or so ago...
People used ~/bin already in the 90s ;-)
Nothing. I also use `~/.local/bin/`
I do this, and it's a huge quality of life improvement. No so much because of shadowing existing binaries, but for better command auto-complete. For example: I have a bunch of tmux utilities and all start with `,t` which is not a polluted command-name prefix compared to just `t`.
But I'm now facing the problem that LLM agents don't like this, and when I instruct them to run certain tools, they remove the leading comma. It's normally fixed with one extra sentence in the prompt, but still inconvenient.
This has been a popular topic nearly every time the post makes the HN front page.
* https://news.ycombinator.com/item?id=40769362 (2024, 169 comments)
* https://news.ycombinator.com/item?id=31846902 (2022, 123 comments)
* https://news.ycombinator.com/item?id=22778988 (2020, 90 comments)
Clever hack! <3 I also do namespacing yet in a different way.
I create a home directory "x" for executables that I want to manage as files, and don't want on PATH or as alias.
To run foo: ~/x/foo
For example I have GNU date as ~/x/date so it's independent of the system BSD date.
instead of using ~/bin I use ~/installed/bin, sometimes I need build a command from source then install it, which might have share/ man/ etc so I can avoid installing them under the home dir.
It’s clever, but is not aesthetic. A comma feels unnatural in the fs.
So did the dot in dotfiles originally. You’ll get used to it if you want to.
It doesn't have to be a literal file, it can be an alias.
That doesn't make it "feel" less "unnatural".
Nice although I think the ASCII comma feels wrong as part of a filename even if for purely aesthetic reasons.
If we want to stay within (lowercase) alphabetic Latin characters I think prefixing with the least common letters or bigrams that start a word (x, q, y, z, j) is best.
`y' for instance only autocompletes to `yes' and `ypdomainname' on my path.
Choosing a unique bigram is actually quite easy and a fun exercise.
And we can always use uppercase Latin letters since commands very rarely use never mind start with those.
Its some what natural to german spkrs who use a special set of double quotes to start a quote in print.
As a non-native English speaker I just name them in my native language or using British English spelling.
I have a command named "decolour", which strips (most) ANSI escape codes. Clear as day what it does, almost nobody uses this spelling when naming commands that later land as part of a distribution.
Finally a post that is relevant to what I have been looking for quite some time.
Also, kudos to keeping it so concise and to the point, thats some prime writing.
Properly manage PATH for the context you're in and this is a non-issue. This is the solution used by most programming environments these days, you don't carry around the entire npm or PyPI ecosystem all the time, only when you activate it.
Then again, I don't really believe in performing complex operations manually and directly from a shell, so I don't really understand the use-case for having many small utilities in PATH to begin with.
This is one of those "obvious in hindsight" tricks. The comma prefix gives you a namespace that's guaranteed to never collide with system binaries, shell builtins, or anything from a package manager.
I do something similar with my personal scripts — prefix them with a short namespace. The real win isn't just avoiding collisions though, it's tab completion. Type the prefix and tab, and you immediately see all your custom stuff without wading through hundreds of system commands.
The 2009 date on this is wild. Some of these simple unix conventions age better than most frameworks.
I prefer all my custom commands as 1 letter.
On my most frequently used machine/dev env this means -
e for vim
m for mise
n for pnpm
c for Claude
x for codex
r for uv run
j for just
I use fish abbreviations for this, as they expand to the full command in the shell history.
d for deploy to production
f for friday
I think its a fairly good idea - but for myself, i had already mapped csh’s default history character (!) to a comma (,) for the same reason - no shift key to invoke.
In many contexts in which I am trying to deconflict namespaces, I use my initials. I hadn't thought about it in this particular context, though now that I do, it seems fortunate that I am ced rather than sed.
I don’t think this is a terrible idea, though stylistically it bothers me. I suppose you could simply have a prefix command router that would essentially do the same thing. I also started using “task” recently and it’s been a game changer for my CLI life.
What is task?
It is like make but designed specifically for the way non-C(++) users - people like me for example adding scripts like "make run" and "make build" to my node/python/PHP/etc repos - use it. It is great! I still don't use it literally just because make is already installed on any *nix system I encounter day to day.
I used task previously and now use mise for it since I have a mise version file usually anyway.
Interesting, though I never had enough custom scripts to justify this, I prefer oh-my-zsh plugin style short aliases instead, i.e. https://github.com/ohmyzsh/ohmyzsh/tree/master/plugins/git
,Start all of your commands with a comma
Should be titled Prefix your script names with a comma. Current title is a little clickbait-y through its ambiguity.
Agree.
I thought the title meant I should type ,ls instead of ls.
,sudo make me a sammich
Like so?
This is a really good practical step if you worry about name collisions
quick, easy and consistent. entirely voluntary.
Bravo
I would have never thought of that. Funny that a comma can be used like that.
Off-topic: What the hell is that font on this website? And why does the "a" look like that?
Gentium is a very beautiful font: https://software.sil.org/gentium/
I tried a variant or this idea so many years ago after I leaned git and rearranged some of my personal tools as subcommands (like git) of a single executable named "dude,"
It went weird pretty quickly...
.. examples?
can someone explain security consideration of placing scripts into $HOME? Some time ago I moved all my scripts to /usr/local/bin, because I feel that this is better from security perspective.
There are no security implications, on the contrary.
It is objectively cleaner to keep your user scripts in your home, that way they are only in _your_ PATH, whereas putting them in /usr/[local/]bin implicitly adds them to every [service] user on the machine, which I can see creating obscure undesired effets.
Not even mentioning the potential issues with packages that could override your scripts at install, unexpected shadowing of service binaries, setuid security implications, etc.
Someone with access to your home dir can also set your $PATH and aliases to anything they want, so I don’t see any extra security considerations here.
(2009)
Previous discussions:
2024: https://news.ycombinator.com/item?id=40769362
2022: https://news.ycombinator.com/item?id=31846902
2020: https://news.ycombinator.com/item?id=22778988
> Like many Unix users, I long ago created a ~/bin/ directory in my home directory
`.local/bin` seems to be much more common in my experience for this use case. And for good reason.
~/bin is actually created per default on OpenSUSE (though it's removal has been discussed several times).
Unclutter your $HOME!
similarly, I start all my underscorends with an underscore
This is just brilliant. Thanks.
I appreciate the idea, but the comma just looks horrible to me as part of a filename. I can imagine someone unfamiliar with the naming scheme to get confused.
I'd prefer to use underscore (when writing BASH scripts, I name all my local variables starting with underscore), but a simple two or three letter prefix would also work. I don't like the idea of a punctuation prefix as punctuation usually has a specific meaning somewhere and including it as the first character in a filename looks wrong. (e.g. Comma is typically used as a list separator and it's a bit of cognitive dissonance to see it not used in that context)
Underscore requires pressing Shift, however.
> I don't like the idea of a punctuation prefix as punctuation usually has a specific meaning somewhere and including it as the first character in a filename looks wrong.
So you don’t use dotfiles? ;)
Well dotfiles demonstrate that punctuation can have a special meaning in filenames.
I'm not convinced by "quicker to type" arguments as that's rarely the bottleneck, so I'm perfectly happy with using underscores in filenames and variables. I wouldn't use underscore as the beginning character of a filename unless it had a specific meaning to me (e.g. temporary files), so I'd be more inclined to use a two or three character prefix instead.
For me it’s not about quickness, but about strain. Like in RSI.
I use my_ as a prefix.
That’s a more meaningful prefix than “,” at the expense of a couple more key strokes. I consider that to still be a win in the book of tab completions.
I would replace underscore with “-“ or “.”
I used to use "do" as a prefix e.g. "doBackup"
Nowadays, I tend to skip using a personal prefix and just try to name commands with a suitable verb in front (e.g. "backupMySQL") and ensure that there's no name collisions.
Whenever I see "my" as a prefix, it feels like such a childish "my first Sony" thing. I hate official sites using that.
This was actually the same feeling I had when I tried to learn perl. I just had a visceral dislike for "my" as the keyword to declare a local variable.
I read this blog a few years ago, and implemented it soon after with a refresh of my rc files and shortcuts. Gamechanger - has helped me every single day since. It’s easy to remember, autocompletes easily, and adds a little flair of personalization.
[dead]