~/sd is my
directory. It looks like this:
$ tree ~/sd /Users/ian/sd ├── blog │ ├── preview │ └── publish ├── book │ ├── open │ ├── pdf │ ├── progress │ └── typeset ├── cat ├── dim │ └── visualize ├── edit ├── git │ └── ugh ├── help ├── new ├── nix │ ├── diff │ ├── info │ └── sync ├── tmux │ ├── init │ └── restore └── which
sd is a command on my
PATH. It dispatches its arguments to my script directory.
$ sd blog publish
Will run the script
This sounds like a dumb way to save myself having to type a few characters. Which it is. But it’s also slightly more than that:
sd has nice tab completion. That’s the only real difference between
~/sd: those little descriptions of what the commands do. In case you skipped the video, it looks something like this:
$ sd nix <TAB> diff -- prints what will happen if you run sync info -- <package> prints package description sync -- make user environment match ~/dotfiles/user.nix
It’s not much. But it’s nice.
Those descriptions come from comments in the command files themselves, or from separate
command.help files (in case there is a lot to say, or the command is not a shell script).
For example, in the demo above, I run
sd help nix sync, which ends up running
cat ~/sd/nix/sync.help. The first line is all that ends up in the completion menu, but the rest of the text is there in case I forget how a command works.
a boring story about
sd one afternoon when I couldn’t find a script I wanted in my
~/bin. I knew it was there – and I knew what it did – but I couldn’t remember what I’d named it.
I had close to a hundred different scripts in
~/bin, all with names that made perfect sense when I added them:
I decided enough was enough, so I added a little structure, and
sd was born.
It’s much easier to remember what commands do when they’re namespaced like this. And it’s much easier to remember how to use them when you write their usage down. I’ve used
sd ever since, and I don’t think I could go back to a flat
I only have a few
sd commands on my laptop, because I don’t find myself writing a lot of complicated scripts at home. But
sd was written for work, where I had dozens of helpers all neatly categorized by project or tool or domain. It was good.
do you want to use
It’s not really a thing. It’s more of an idea. You can implement it yourself in one sitting, if you want.
But of course you don’t want to figure out how to write your own shell completion. Shell completion is some arcane nonsense. You want me to have already done it. But here’s the thing: I only wrote
zsh completions because I only ever use
I’ve sometimes thought about rewriting
sd as something other than a pile of shell and publishing it and publicizing it and adding
bash support and what have you. But you know what? I probably won’t ever do that. But maybe someone else will, if they like the idea enough.1
You’re welcome to use my implementation, though, if you use
zsh. It works on macOS and Linux. It lives in my dotfiles repo because I want it on every machine I use and I’m very lazy. See also
sd-common are the scripts like
cat that must exist in
~/sd or something will probably break (I install them as symlinks to my dotfiles repo).
If you want completions to work, you have to add this in your
~/bin/completions is wherever you end up putting the
One last note:
sd only looks for executables, so you can add non-executable helpers that you can
source from multiple scripts in the same directory. When
sd invokes a command,
$0 is the absolute path to the command, so you can use
dirname "$0" to get at your helpers. Like you’d expect.
sd affected you
Being able to type
sd new foo command instead of
vim ~/bin/command, typing the shebang, trying to run it, realizing I forgot to make it executable, running it again… it’s made my life slightly better. And because it’s so easy to save snippets for later, I find that I’m much more likely to do it – and a lot less likely to find myself combing through
ctrl-r a few months later.
Otherwise it’s, you know, it’s not lifechanging. It won’t help you to appreciate the smaller beauties all around you. You’ll still wake up feeling groggy. Lately I’ve been using it to make user-friendly wrappers around
nix-env as part of my series on How to Learn Nix, which has been nice.
what would you have done differently
sd newsupport a
--escape argument and treat the rest of the arguments as the script, for easily stashing oneliners:
sd new foo bar -- echo foobar.
- Figure out how to share logic between
sdand the autocomplete script
_sd. Lotta copy-paste right now.
- Some nice way to generate command completion for command arguments, instead of just for commands themselves. Especially for commands like
sd editwhich are much harder to use than they should be.
bashsupport I guess
- In the case that
sd, treat it as part of the script path, so you can make very lightweight command wrappers that work without having to type
ln -s $(which sd) ~/bin/some-command, and then you can type
some-command fooinstead of
sd some-command foo.
sd foo bar --helpinstead of
sd help foo bar, because it’s slightly annoying to reposition your cursor. But that’s kind of a whole can of worms.
- yes the real solution here is to not use shell and find some nice alternative that actually understands how command line arguments work but there really just isn’t you know it’s 2021 and biden is president and bash is still the only thing we have don’t talk to me about oil shell i know all about oil shell
sd is like a 95/20 situation. It’s pretty good even in its neglected, janky state. As long as you’re using
If you do this, please tell me, so I can switch to your implementation. ↩︎