#!/usr/bin/env bash # https://github.com/ianthehenry/sd # ecd1ab8d3fc3a829d8abfb8bf1e3722c9c99407b __sd_join_path() { echo "$@" | tr ' ' / } __sd_which() { echo "$1" } __sd_cat() { ${SD_CAT:-cat} "$1" } __sd_edit() { ${SD_EDITOR:-${VISUAL:-${EDITOR:-vi}}} "$1" } __sd_directory_help() { local target="$1" local i file helpfile help command local -a commands helps if [[ -d "$target" ]]; then helpfile="$target/help" else helpfile="$target.help" fi # in case you have `sd help` as a command alias, we don't # want to print that when you just run "sd" by itself. # Admittedly we don't check this in any other place, which # could be considered a bug, but whatever it's a weird little # hack and no one uses this. if [[ -e "$helpfile" && ! -x "$helpfile" ]]; then __sd_cat "$helpfile" echo else command=$(basename "$target") echo "$command commands" echo fi commands=() helps=() for file in "$target"/*; do if [[ ! -x "$file" ]]; then continue fi command=$(basename "$file") if [[ -d "$file" ]]; then helpfile="$file/help" if [[ -f "$helpfile" ]]; then help=$(head -n1 "$helpfile") else help="$command commands" fi command="$command ..." else helpfile="$file.help" if [[ -f "$helpfile" ]]; then help=$(head -n1 "$helpfile") else help=$(sed -nE -e '/^#!/d' -e '/^#/{s/^# *//; p; q;}' "$file") fi fi commands+=("$command") helps+=("$help") done if [[ ${#commands[@]} -eq 0 ]]; then echo "(no subcommands found)" else local max_length=0 local length for command in "${commands[@]}"; do length=${#command} max_length=$((length > max_length ? length : max_length)) done if [[ -n ${ZSH_EVAL_CONTEXT+x} ]]; then set -o LOCAL_OPTIONS # we need to ensure we have 0-indexed arrays # in order for this loop to work properly. # zsh doesn't support ${!commands[@]} expansion set -o KSH_ARRAYS fi for ((i = 0; i < ${#commands[@]}; i++)); do printf "%-${max_length}s -- %s\n" "${commands[i]}" "${helps[i]}" done fi } __sd_help() { local target=$1 if [[ -d "$target" ]]; then __sd_directory_help "$target" elif [[ -f "$target.help" ]]; then __sd_cat "$target.help" else help=$(sed -nE -e '/^#!/d' -e $':start\n /^#/{ s/^# ?//; p; \nb start\n }' "$target") if [[ -z "$help" ]]; then echo "there is no help for you here" >&2 exit 1 else echo "$help" fi fi } __sd_print_template() { if [[ -f "$1/template" ]]; then cat "$1/template" else if [[ "$1" = "${SD_ROOT:-$HOME/sd}" ]]; then cat <&2 exit 1 fi if [[ -f "$target" ]]; then echo "error: command prefix $target is a regular file" >&2 exit 1 fi body="$*" script="$target"/"$(__sd_join_path "${command_path[@]}")" if [[ -e "$script" ]]; then echo "$script already exists!" >&2 exit 1 fi dir="$(dirname "$script")" mkdir -p "$dir" ( __sd_print_template "$dir" if [[ -n "$body" ]]; then printf "\n%s\n" "$body" fi ) >"$script" chmod +x "$script" if [[ -z "$body" ]]; then __sd_edit "$script" fi } __sd_new_user_help() { root=$1 echo >&2 "error: $root not found" echo >&2 echo >&2 "It looks like you don't have a script directory yet!" echo >&2 echo >&2 "Get started by creating your first script:" echo >&2 echo >&2 " sd hello --new 'echo \"Hello, sd!\"'" echo >&2 echo >&2 "And then run it like this:" echo >&2 echo >&2 " sd hello" } __sd() { set -euo pipefail local root=${SD_ROOT:-$HOME/sd} if [[ -e "$root" && ! -d "$root" ]]; then echo "error: $root is not a directory" >&2 exit 1 fi local target=$root local arg while [[ $# -gt 0 ]]; do arg="$1" if [[ -d "$target/$arg" ]]; then target="$target/$arg" shift elif [[ -f "$target/$arg" ]]; then target="$target/$arg" shift break else break fi done local found_help="false" local found_new="false" local found_edit="false" local found_cat="false" local found_which="false" local found_really="false" for arg in "$@"; do case "$arg" in --help) found_help=true ;; --new) found_new=true ;; --edit) found_edit=true ;; --cat) found_cat=true ;; --which) found_which=true ;; --really) found_really=true ;; esac done # you're allowed to run --new even if there is no # script directory root, in order to bootstrap it if [[ "$found_really" = "true" || "$found_new" = "false" ]]; then if [[ ! -d "$root" ]]; then __sd_new_user_help "$root" exit 1 fi fi if [[ "$found_really" = "true" ]]; then local -a preserved=() for arg in "$@"; do case "$arg" in '--really') shift; break ;; *) preserved+=("$arg"); shift ;; esac done if [[ ${#preserved[@]} -gt 0 ]]; then set -- "${preserved[@]}" "$@" fi elif [[ "$found_new" = "true" ]]; then __sd_new "$target" "$@" exit 0 elif [[ "$found_help" = "true" ]]; then __sd_help "$target" exit 0 elif [[ "$found_edit" = "true" ]]; then __sd_edit "$target" exit 0 elif [[ "$found_cat" = "true" ]]; then __sd_cat "$target" exit 0 elif [[ "$found_which" = "true" ]]; then __sd_which "$target" exit 0 fi if [[ -d "$target" ]]; then __sd_directory_help "$target" if [[ $# -gt 0 ]]; then echo >&2 echo "$target/$(__sd_join_path "$@") not found" >&2 exit 1 fi elif [[ -x "$target" ]]; then SD="$(dirname "$target")" exec "$target" "$@" else __sd_cat "$@" fi } # If you source this file and use function-mode sd, we want to # wrap the execution in a subshell so that set -e doesn't kill # the interactive shell, and so that we can exec the subshell # without destroying the interactive shell. sd() ( __sd "$@" ) # If you source this file, it will define the function sd, # which you can use from your shell. If you *run* this file # as an executable, it will just invoke that function. We # have to do some tricks to detect that reliably on bash/zsh. if [[ -n ${ZSH_EVAL_CONTEXT+x} ]]; then # we are in zsh if [[ "$ZSH_EVAL_CONTEXT" = toplevel ]]; then __sd "$@" fi elif [[ -n ${BASH_SOURCE[0]+x} ]]; then # we are in bash if [[ "${BASH_SOURCE[0]}" = "$0" ]]; then __sd "$@" fi fi