diff options
-rw-r--r-- | ISSUES.markdown | 1 | ||||
-rw-r--r-- | README.markdown | 2 | ||||
-rwxr-xr-x | bin/apf | 62 | ||||
-rw-r--r-- | man/man1/apf.1 | 74 |
4 files changed, 80 insertions, 59 deletions
diff --git a/ISSUES.markdown b/ISSUES.markdown index 72b87ea8..b94ab582 100644 --- a/ISSUES.markdown +++ b/ISSUES.markdown @@ -13,7 +13,6 @@ Known issues - Mostly done now: [tom@conan:~/.dotfiles/bin](git:master)$ grep bash * - apf:#!/usr/bin/env bash eds:#!/usr/bin/env bash han:#!/usr/bin/env bash diff --git a/README.markdown b/README.markdown index b443d618..47197122 100644 --- a/README.markdown +++ b/README.markdown @@ -380,7 +380,7 @@ Installed by the `install-bin` target: for unfolding HTTP headers, but it should work for most RFC 822 formats. * `apf(1)` prepends arguments to a command with ones read from a file, - intended as a framework for shell functions. + intended as a framework for shell wrappers or functions. * `ax(1)` evaluates an awk expression given on the command line; this is intended as a quick way to test how Awk would interpret a given expression. * `bel(1)` prints a terminal bell character. @@ -1,30 +1,52 @@ -#!/usr/bin/env bash -# Prepend arguments from a file to a command call +#!/bin/sh +# Prepend arguments from a file to the given arguments for a command self=apf -# Give up completely if no BASH_VERSINFO (<2.0) -[ -n "$BASH_VERSINFO" ] || exit - -# Require at least two arguments, give usage otherwise -if (($# < 2)) ; then +# Require at least two arguments +if [ "$#" -lt 2 ] ; then printf >&2 '%s: Need an arguments file and a command\n' "$self" exit 2 fi # First argument is the file containing the null-delimited arguments -argfile=$1 -shift +argf=$1 cmd=$2 +shift 2 -# Read all the null-delimited arguments from the file -declare -a args -while IFS= read -rd '' arg ; do - args[${#args[@]}]=$arg -done < "$argfile" +# If the file exists, we'll read it. If it doesn't, this is not an error (think +# personal config files like ~/.vimrc) +if [ -f "$argf" ] ; then -# Next argument is the command to run -cmd=$1 -shift + # Create a temporary directory with name in $td, and handle POSIX-ish traps to + # remove it when the script exits. + td= + cleanup() { + [ -n "$td" ] && rm -fr -- "$td" + if [ "$1" != EXIT ] ; then + trap - "$1" + kill "-$1" "$$" + fi + } + for sig in EXIT HUP INT TERM ; do + # shellcheck disable=SC2064 + trap "cleanup $sig" "$sig" + done + td=$(mktd "$self") || exit + + # Write the arguments in reverse to a temporary file + revf=$td/revf + sed '1!G;$!{h;d}' "$argf" > "$revf" || exit + + # Stack up all the arguments from the file. Skip blank lines and comments. + # An empty file is also fine. + while IFS= read -r arg ; do + case $arg in + '#'*) continue ;; + *[![:space:]]*) ;; + *) continue ;; + esac + set -- "$arg" "$@" + done < "$revf" +fi -# Run the command with the retrieved arguments first, then the rest of the -# command line as passed to the function -command "$cmd" "${args[@]}" "$@" +# Run the command with the changed arguments +exec "$cmd" "$@" diff --git a/man/man1/apf.1 b/man/man1/apf.1 index 67b3b25a..7cb2ab28 100644 --- a/man/man1/apf.1 +++ b/man/man1/apf.1 @@ -7,30 +7,34 @@ foorc foo --bar baz .SH DESCRIPTION -Add null-delimited arguments read from a file to a command's arguments before -running it. This is intended as a way of implementing *rc files for interactive -shell calls to programs that don't support such files, without having to use -broken environment variables (e.g. GREP_OPTIONS); this enables you to, for -example, use arguments with shell metacharacters and spaces in them that you do -not want expanded. - +Add newline-delimited arguments read from a file to a command's arguments +(before any given ones) before running it. This is intended as a quick way of +implementing *rc files for interactive shell calls to programs that don't +support such files, without having to use broken environment variables like GNU +grep(1)'s GREP_OPTIONS. +.P +This enables you to use arguments with shell metacharacters and spaces in them +that you do not want expanded. The only exception is that you cannot have +newlines in any of the arguments. This was done to keep POSIX sh(1) +compatibility. +.P For example, given this simple program in our $PATH, printargs: - - $ cat ~/.local/bin/printargs +.P + $ cat /usr/bin/printargs #!/bin/sh - printf '%s\n' "$@" - + printf '%s\\n' "$@" +.P Which just prints its arguments: - +.P $ printargs a b c a b c - +.P We could do this: - - $ printf '%s\0' -f --flag --option '? foo bar *' > "$HOME"/.printargsrc - +.P + $ printf '%s\\n' -f --flag --option '? foo bar *' > "$HOME"/.printargsrc +.P $ apf "$HOME"/.printargsrc printargs a b c -f --flag @@ -39,27 +43,23 @@ We could do this: a b c - -We could then make a permanent wrapper function with: - +.P +We could then make a permanent wrapper script in two line: +.P + $ cat >~/.local/bin/printargs + #!/bin/sh + exec apf "$HOME"/.printargsrc /usr/bin/printargs + ^D + $ chmod +x ~/.local/bin/printargs +.P +Or just a shell function, if it's needed interactively: +.P $ printargs() { apf "$HOME"/.printargsrc printargs "$@" ; } - - $ printargs a b c - -f - --flag - --option - ? foo bar * - a - b - c - - $ printf '%s\n' !-2:q >> "$HOME"/.bashrc - -This means you can edit the options in the *rc file, and don't have to redefine -a wrapper function. - -If you actually want those options to *always* be added, regardless of whether -you're in an interactive shell, you really should make an actual wrapper -script. +.P +It's not considered an error if the file doesn't exist or is empty. If it's a +directory or otherwise not byte-readable, an error will be printed to stderr, +but execution of the called program will continue anyway. Blank lines or lines +beginning with # are also ignored. Both leading and trailing whitespace is +preserved. .SH AUTHOR Tom Ryder <tom@sanctum.geek.nz> |