# # keep -- Main function for bashkeep; provided with a list of NAMEs, whether # shell functions or variables, writes the current definition of each NAME to a # directory $BASHKEEP (defaults to ~/.bashkeep.d) with a .bash suffix, each of # which is reloaded each time this file is called. This allows you to quickly # arrange to keep that useful shell function or variable you made inline on # subsequent logins. # # Consider a shell function declared inline with the NAME 'ayy': # # $ ayy() { printf '%s\n' lmao ; } # $ ayy # lmao # $ keep ayy # $ keep # ayy # $ exit # # Then, on next login, the function is redefined for you: # # $ ayy # lmao # # To get rid of it: # # $ keep -d ayy # keep() { # Figure out the directory to which we're reading and writing these scripts local bashkeep bashkeep=${XDG_DATA_HOME:-"$HOME"/.local/share}/bashkeep mkdir -p -- "$bashkeep" || return # Parse options local opt delete local OPTERR OPTIND OPTARG while getopts 'dh' opt ; do case $opt in # -d given; means delete the keepfiles for the given names d) delete=1 ;; # -h given; means show help h) cat <&2 return 2 ;; esac done shift "$((OPTIND-1))" # If any arguments left, we must be either keeping or deleting if (($#)) ; then # Start keeping count of any errors local -i errors errors=0 # Iterate through the NAMEs given local name for name do # Check NAMEs for validity case $name in # NAME must start with letters or an underscore, and contain no # characters besides letters, numbers, underscores, or dashes [!a-zA-Z_]*|*[!a-zA-Z0-9_-]*) printf 'bash: %s: %s not a valid NAME\n' \ "${FUNCNAME[0]}" "$name" >&2 ((errors++)) ;; # NAME is valid, proceed *) # If -d was given, delete the keep files for the NAME if ((delete)) ; then rm -- "$bashkeep"/"$name".bash # Save a function elif [[ $(type -t "$name") = 'function' ]] ; then declare -f -- "$name" >"$bashkeep"/"$name".bash # Save a variable elif declare -p -- "$name" >/dev/null ; then declare -p -- "$name" >"$bashkeep"/"$name".bash fi || ((errors++)) ;; esac done # Return 1 if we accrued any errors, 0 otherwise return "$((errors > 0))" fi # Deleting is an error, since we need at least one argument if ((delete)) ; then printf 'bash: %s: must specify at least one NAME to delete\n' \ "${FUNCNAME[0]}" >&2 return 2 fi # Otherwise the user must want us to print all the NAMEs kept ( shopt -s nullglob for keep in "$bashkeep"/*.bash ; do ! [[ -d $keep ]] || continue keep=${keep##*/} keep=${keep%.bash} printf '%s\n' "$keep" done ) } # Load any existing scripts in bashkeep for bashkeep in "${XDG_DATA_HOME:-"$HOME"/.local/share}"/bashkeep/*.bash ; do [[ -e $bashkeep ]] || continue source "$bashkeep" done unset -v bashkeep