aboutsummaryrefslogtreecommitdiff
path: root/bash/bashrc.d/apf.bash
blob: a9cd115e4f791e85039147359b140ae91fd29884 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
#
# apf -- arg-prepend-file -- Prepend 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 Bash 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.
#
# For example, given this simple program in our $PATH, printargs:
#
#   $ cat ~/.local/bin/printargs
#   #!/bin/sh
#   printf '%s\n' "$@"
#
# Which just prints its arguments:
#
#   $ printargs a b c
#   a
#   b
#   c
#
# We could do this:
#
#   $ printf '%s\0' -f --flag --option '? foo bar *' > "$HOME"/.printargsrc
#
#   $ apf "$HOME"/.printargsrc printargs a b c
#   -f
#   --flag
#   --option
#   ? foo bar *
#   a
#   b
#   c
#
# We could then make a permanent wrapper function with:
#
#   $ 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.
#
apf() {

    # Require at least two arguments, give usage otherwise
    if (($# < 2)) ; then
        printf 'bash: %s: usage: %s ARGFILE COMMAND [ARGS...]\n' \
            "$FUNCNAME" "$FUNCNAME" >&2
        return 2
    fi

    # First argument is the file containing the null-delimited arguments
    local argfile=$1
    shift

    # Check the arguments file makes sense
    if [[ ! -e $argfile ]] ; then
        printf 'bash: %s: %s: No such file or directory\n' \
            "$FUNCNAME" "$argfile"
        return 1
    elif [[ -d $argfile ]] ; then
        printf 'bash: %s: %s: Is a directory\n' \
            "$FUNCNAME" "$argfile"
        return 1
    elif [[ ! -r $argfile ]] ; then
        printf 'bash: %s: %s: Permission denied\n' \
            "$FUNCNAME" "$argfile"
        return 1
    fi

    # Read all the null-delimited arguments from the file
    local -a args
    local arg
    while IFS= read -d '' -r arg ; do
        args[${#args[@]}]=$arg
    done < "$argfile"

    # Next argument is the command to run
    local cmd=$1
    shift

    # Run the command with the retrieved arguments first, then the rest of the
    # command line as passed to the function
    command "$cmd" "${args[@]}" "$@"
}