https://github.com/ianthehenry/sd Skip to content Toggle navigation Sign up * Product + Actions Automate any workflow + Packages Host and manage packages + Security Find and fix vulnerabilities + Codespaces Instant dev environments + Copilot Write better code with AI + Code review Manage code changes + Issues Plan and track work + Discussions Collaborate outside of code + Explore + All features + Documentation + GitHub Skills + Blog * Solutions + For + Enterprise + Teams + Startups + Education + By Solution + CI/CD & Automation + DevOps + DevSecOps + Case Studies + Customer Stories + Resources * Open Source + GitHub Sponsors Fund open source developers + The ReadME Project GitHub community articles + Repositories + Topics + Trending + Collections * Pricing [ ] * # In this repository All GitHub | Jump to | * No suggested jump to results * # In this repository All GitHub | Jump to | * # In this user All GitHub | Jump to | * # In this repository All GitHub | Jump to | Sign in Sign up {{ message }} ianthehenry / sd Public * Notifications * Fork 7 * Star 468 a cozy nest for your scripts License MIT license 468 stars 7 forks Star Notifications * Code * Issues 0 * Pull requests 0 * Discussions * Actions * Security * Insights More * Code * Issues * Pull requests * Discussions * Actions * Security * Insights ianthehenry/sd This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository. master Switch branches/tags [ ] Branches Tags Could not load branches Nothing to show {{ refName }} default View all branches Could not load tags Nothing to show {{ refName }} default View all tags Name already in use A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch? Cancel Create 2 branches 7 tags Code * Local * Codespaces * Clone HTTPS GitHub CLI [https://github.com/i] Use Git or checkout with SVN using the web URL. [gh repo clone ianthe] Work fast with our official CLI. Learn more. * Open with GitHub Desktop * Download ZIP Sign In Required Please sign in to use Codespaces. Launching GitHub Desktop If nothing happens, download GitHub Desktop and try again. Launching GitHub Desktop If nothing happens, download GitHub Desktop and try again. Launching Xcode If nothing happens, download Xcode and try again. Launching Visual Studio Code Your codespace will open once ready. There was a problem preparing your codespace, please try again. Latest commit @ianthehenry ianthehenry correct install instructions ... 4d4f7f1 Nov 21, 2022 correct install instructions fpath has to come before compinit! Sorry. 4d4f7f1 Git stats * 36 commits Files Permalink Failed to load latest commit information. Type Name Latest commit message Commit time sdefaults LICENSE README.md _sd sd sd.plugin.zsh View code [ ] sd: my script directory uhh Usage --help --new custom script templates --cat --edit --which --really Context Options Installation Installation as a regular script Installation as a shell function Antigen oh-my-zsh: sd help command vs. sd command --help bash/fish autocompletion support Changelog v1.1.0 2022-10-30 v1.0.1 2022-04-17 v1.0.0 2022-02-27 v0.3.0 2022-02-26 v0.2.0 2022-02-24 v0.1.1 2021-12-05 v0.1.0 2021-12-01 README.md sd: my script directory * Usage * Installation * 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 +-- init And 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.nix Simply 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. 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 #!/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 diff set -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.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 diff If you run --help for a directory, it will also print out a command listing after the help text: $ sd nix --help nix commands install -- 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 #!/usr/bin/env bash set -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. 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. This includes all of the helper scripts in sdefaults/ (explained below). Installation as a regular script sd is not currently packaged in any package manager that I am aware of, but it should be pretty easy if you want to package it for your distribution. It's just a single script and a single completion file. Until that day: 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 compinit If you use a zsh framework like oh-my-zsh, 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 Installation as a shell function You can just source sd in your .zshrc and set up completion manually (as described below), but sd is designed to be compatible with shell plugin managers. Antigen Add this line to your .zshrc: antigen bundle ianthehenry/sd oh-my-zsh: 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" sd help command vs. sd command --help There are some scripts in sdefaults/ that you can copy into your own ~/sd if you like. They'll let you type sd cat foo bar instead of sd foo bar --cat or sd new foo -- echo hi instead of sd foo --new echo hi (and so on for each of the built-in commands). These mostly exist for backwards compatibility with an earlier version of sd. You don't have to use them if you don't want to. Note that they will not work if you've installed sd as a shell function instead of an executable. bash/fish autocompletion support Patrick Jackson contributed an unofficial fish completion script, 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 execs 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 files You 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. About a cozy nest for your scripts Topics shell bash zsh organization scripting Resources Readme License MIT license Stars 468 stars Watchers 5 watching Forks 7 forks Releases 3 v1.1.0 Latest Oct 30, 2022 + 2 releases Contributors 2 * @ianthehenry ianthehenry Ian Henry * @gerardbosch gerardbosch Gerard Bosch Languages * Shell 100.0% Footer (c) 2022 GitHub, Inc. Footer navigation * Terms * Privacy * Security * Status * Docs * Contact GitHub * Pricing * API * Training * Blog * About You can't perform that action at this time. You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.