Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/ianthehenry/sd
a cozy nest for your scripts
https://github.com/ianthehenry/sd
bash organization scripting shell zsh
Last synced: about 2 months ago
JSON representation
a cozy nest for your scripts
- Host: GitHub
- URL: https://github.com/ianthehenry/sd
- Owner: ianthehenry
- License: mit
- Created: 2021-11-26T07:06:03.000Z (about 3 years ago)
- Default Branch: master
- Last Pushed: 2024-03-19T21:53:16.000Z (9 months ago)
- Last Synced: 2024-08-01T22:58:18.060Z (5 months ago)
- Topics: bash, organization, scripting, shell, zsh
- Language: Shell
- Homepage:
- Size: 28.3 KB
- Stars: 703
- Watchers: 6
- Forks: 15
- Open Issues: 3
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# `sd`: my `s`cript `d`irectory
- [Usage](#usage)
- [Installation](#installation)
- [Changelog](#changelog)Has this ever happened to you?
*Black and white video plays of someone struggling to find a shell script they wrote a year ago and stuffed into their `~/bin` without giving it a very meaningful name.*
Don't you hate it when you can't find the scripts you need, when you need it? Well now there's a better way!
*Color fills the screen. Someone holds `sd` up to the camera, and flashes a winning smile. They've found the script on their first try.*
Introducing `sd`, the script directory for the refined, sophisticated professional. Simply organize your scripts in a logical directory hierarchy, and let `sd` take care of the rest!
$ tree ~/sd
/Users/ian/sd
├── blog
│ ├── edit
│ ├── preview
│ └── publish
├── nix
│ ├── diff
│ ├── info
│ └── sync
└── tmux
└── initAnd now instead of typing `~/sd/blog/publish`, you can just type `sd blog publish` -- a savings of nearly three whole characters!
But wait! There's more! You'll wonder how you ever lived without `sd`'s best-in-class tab completion:
$ sd nix
diff -- prints what will happen if you run sync
info -- prints package description
sync -- make user environment match ~/dotfiles/user.nixSimply write a one-line comment in your script, and you'll never be left scratching your head over how you were supposed to call it!
# uhh
Hi okay sorry. [Take a look at this blog post for a real introduction and a fancy asciinema demo of how it works.](https://ianthehenry.com/posts/sd-my-script-directory/)
# Usage
The default behavior for `sd foo bar` is:
- If `~/sd/foo` is an executable file, execute `~/sd/foo bar`.
- If `~/sd/foo/bar` is an executable file, execute it with no arguments.
- If `~/sd/foo/bar` is a directory, this is the same is `sd foo bar --help` (it prints usage information).
- If `~/sd/foo/bar` is a non-executable regular file, this is the same is `sd foo bar --cat` (it just prints the file out).There are some special flags that are significant to `sd`. If you supply any one of these arguments, `sd` will not invoke your script, and will do something fancier instead.
$ sd foo bar --help
$ sd foo bar --new
$ sd foo bar --edit
$ sd foo bar --cat
$ sd foo bar --which
$ sd foo bar --really## `--help`
Print the contents of a help file, or generate a help file from comments in a script.
For executables, `sd` looks for a file with the same name but the `.help` extension. For example, `sd nix diff --help` would look for a file called `~/sd/nix/diff.help`, and print it out.
For directories, `sd` looks for a file that's just called `help`. So `sd nix --help` would look for `~/sd/nix/help`.
If there is no help file for an executable, `sd` will print the first comment block in the file instead. `sd` currently only recognizes bash-style `#` comments.
For example:
$ cat ~/sd/nix/sync
```bash
#!/usr/bin/env bash# make user environment match ~/dotfiles/user.nix
#
# This will remove any packages you've installed with nix-env
# but have not added to user.nix. To see exactly what this
# will do, run:
#
# sd nix diffset -euo pipefail
# maybe this should be configurable
nix-env -irf ~/dotfiles/user.nix
```That will produce the following help output (note that it only prints the first contiguous comment block):
```
$ sd nix sync --help
make user environment match ~/dotfiles/user.nixThis will remove any packages you've installed with nix-env
but have not added to user.nix. To see exactly what this
will do, run:sd nix diff
```If you run `--help` for a directory, it will also print out a command listing after the help text:
```
$ sd nix --help
nix commandsinstall -- use --latest to install from nixpkgs-unstable
shell -- add gcroots for shell.nix
diff -- prints what will happen if you run sync
info -- prints package description
sync -- make user environment match ~/dotfiles/user.nix
```## `--new`
Everything to the left of `--new` is considered a command path, and everything to the right of `--new` is considered the command body. For example:
$ sd foo bar --new echo hi
Will try to create a new command at `~/sd/foo/bar` with an initial contents of `echo hi`.
Actually, to be more precise, it will create this script:
$ cat ~/sd/foo/bar
```bash
#!/usr/bin/env bashset -euo pipefail
echo hi
```Assuming the default template.
If no body is supplied after `--new`, `sd` will open the script for editing.
### custom script templates
You can customize the template used by `--new` by creating a file called `template`, either in `~/sd` or one of its subdirectories.
`sd` will try to find a template by walking recursively up the directory hierarchy. For example, if you run:
```
$ sd foo bar baz --new
````sd` will try to find a template at `~/sd/foo/bar/template` first, then fall back to `~/sd/foo/template`, then `~/sd/template`. If it doesn't find any template file, it will use the default bash template shown above.
(There is no need to make your `template` executable -- `sd` will take care of that for you.)
When `--new` is used to create an inline script, that script will always go at the *end* of your template file. There is currently no way to customize this.
## `--cat`
Prints the contents of the script. See `SD_CAT` below.
## `--edit`
Open the script in an editor. See `SD_EDITOR` below.
## `--which`
Prints the path of the script.
## `--really`
Suppress special handling of all of the other special flags. This allows you to pass `--help` or `--new` as arguments to your actual script, instead of being interpreted by `sd`. For example:
$ sd foo bar --help --really
Will invoke:
~/sd/foo/bar --help
The first occurrence of the `--really` argument will be removed from the arguments passed to the script, so if you need to pass a literal `--really`, you must pass it twice to `sd`. For example:
$ sd foo bar --help --really --really
Will invoke:
$ ~/sd/foo/bar --help --really
# Context
When a script is invoked, `sd` will set the environment variable `SD` to the directory that the script was found in -- in other words, `$(dirname "$0")`.
This makes it slightly more convenient to refer to shared helper files or other scripts relative to the executing script.
# Options
`sd` respects some environment variables:
- `SD_ROOT`: location of the script directory. Defaults to `$HOME/sd`.
- `SD_EDITOR`: used by `sd foo --edit` and `sd foo --new`. Defaults to `$VISUAL`, then `$EDITOR`, then finally falls back to `vi` if neither of those are set.
- `SD_CAT`: program used when printing files, in case you want to use something like [`bat`](https://github.com/sharkdp/bat). Defaults to `cat`.# Installation
There are two ways to use `sd`:
1. source the `sd` file, which will define the shell function `sd`
2. treat `sd` as a regular executable and put it somewhere on your `PATH`I prefer to use `sd` as a regular executable, but the function approach is more convenient if you already use a shell plugin manager that knows how to set up `fpath` automatically.
Note that you cannot invoke "recursive `sd`" (that is, write scripts that themselves invoke `sd`) if you use the function approach, unless you're writing zsh scripts. But you probably shouldn't.
## Installation as a regular script
## Using Nix
As far as I know, [Nix](https://search.nixos.org/packages?channel=unstable&query=script-directory) is the only package manager with `sd` pre-packaged (as `nixpkgs.script-directory`).
`sd` is also [available in home manager](https://github.com/nix-community/home-manager/blob/master/modules/programs/script-directory.nix). You can install it by adding something like this to your `~/.config/home-manager/home.nix`:
```nix
{...}: {
home.programs.script-directory = {
script-directory = {
enable = true;
settings = {
# SD_ROOT = "${config.home.homeDirectory}/custom-script-directory";
# SD_EDITOR = "vim";
# SD_CAT = "bat";
};
};
};
home.programs.zsh = {
# The script-directory module doesn't automatically configure
# zsh completion, so we still have manually add this:
initExtra = ''
fpath+="${pkgs.script-directory}/share/zsh/site-functions"
'';
};
}
```## Without a package manager
1. Put the `sd` script somewhere on your `PATH`.
2. Put the `_sd` completion script somewhere on your `fpath`.I like to symlink `sd` to `~/bin`, which is already on my path. If you've cloned this repo to `~/src/sd`, you can do that by running something like:
$ ln -s ~/src/sd/sd ~/bin/sd
There isn't really a standard place in your home directory to put completion scripts, so unless you've made your own, you'll probably want to add your clone directly to your `fpath`. You should add that to your `.zshrc` file before the line where you call `compinit`. It should look something like this:
# ~/.zshrc
fpath=(~/src/sd $fpath)
autoload -U compinit
compinitIf you use a zsh framework like [`oh-my-zsh`](https://github.com/ohmyzsh/ohmyzsh), it probably calls `compinit` for you. In that case, just set your `fpath` before you source the framework's initialization script.
Note that changes you make to your `~/.zshrc` will only take effect for *future* shells you create, so to start enjoying `sd` immediately you'll also want to run these commands in your existing shells:
$ fpath=(~/src/sd $fpath)
$ compinit## As a shell function
You can just source `sd` in your `.zshrc` and set up completion manually (as described [above](#installation-as-a-regular-script)), but `sd` is designed to be compatible with shell plugin managers.
### [Antigen](https://github.com/zsh-users/antigen)
Add this line to your `.zshrc`:
```shell
antigen bundle ianthehenry/sd
```### [oh-my-zsh](https://github.com/ohmyzsh/ohmyzsh):
Clone this repo into your custom plugins directory:
```
$ git clone https://github.com/ianthehenry/sd.git ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/sd
```And then add it to the plugins list in your `~/.zshrc` before you source `oh-my-zsh`:
```
plugins+=(sd)
source "$ZSH/oh-my-zsh.sh"
```# bash/fish autocompletion support
Patrick Jackson contributed [an unofficial fish completion script](https://gist.github.com/patricksjackson/5065e4a9d8e825dafc7824112f17a5e6), which should be usable with some modification (as written it does not respect `SD_ROOT`, but it should act as a very good starting point if you use fish).
Bash doesn't support the fancy completion-with-description feature that is sort of the whole point of `sd`, but there are apparently ways to hack something similar.
# Changelog
## v1.1.0 2022-10-30
- fix a bug where `--help` would print every comment in the script
## v1.0.1 2022-04-17
- better error message if `~/sd` does not exist
- better error message if `~/sd` exists but is not a directory## v1.0.0 2022-02-27
`sd` is now released under the MIT license. There are no functional changes from the pre-1.0 releases.
## v0.3.0 2022-02-26
- scripts now run with the `SD` environment variable set to the directory they were found in
- autocompletion now completes arguments to commands instead of just commands
- only completes positional file arguments and the built-in flags (like `--help`)
- `sd` now only forks a subshell when invoked as a function
- `sd` now `exec`s scripts instead of `fork`+`exec`
- this fixes the rare issue where a long-running script could throw errors when it finished if you were editing the `sd` executable itself while the script was running, because `bash` was trying to execute the "rest" of the file and apparently doing so by byte index or something (??)
- this only affects me## v0.2.0 2022-02-24
- added per-directory `template` files, to override the `bash` default
## v0.1.1 2021-12-05
- fix a bug where `--new` wouldn't work unless provided with an initial script
## v0.1.0 2021-12-01
- added `--really`
- `dir.help` files are now `dir/help` filesYou used to be able to provide a description for a directory called `foo/` by writing a file called `foo.help` as a sibling of that directory.
Now directory help summaries are expected in `foo/help` instead.
This has the sort-of nice effect that `sd foo help` is sometimes similar to `sd foo --help`. Except that the latter also prints out subcommands.