Search code examples
bashechoverbosity

Printing bash command line args with $@ inline


I want to add verbosity to my bash function by printing the command that it will run. What is the best way to print all arguments $@ inline?

ggtest ()
{
    echo 'git grep -n $@ -- "src/tests/*"'

    git grep -n "$@" -- "src/tests/*";
}

So that I can see an output such as:

$ ggtest "models and views"
git grep -n "models and views" -- "src/tests/*"
...

Solution

  • An overcomplicated version you can cut down to support only the specific shell releases you need support for:

    ggtest ()
    {
        # note the following explicitly exits if run in a shell w/o array support
        local -a cmd || return                    # declare a function-local array
        cmd=( git grep -n "$@" -- "src/tests/*" ) # store intended command in array
    
        # below here, we take a different approach based on running bash version
        case $BASH_VERSION in
    
          '') # no BASH_VERSION == we're running with a shell that's not bash at all
            set -x                                # enable trace logging
            : "${cmd[@]}"                         # run : with our array as arguments
            { set +x; } 2>/dev/null               # silently disable tracing
            ;;
    
          [1-4].*) # pre-5.0 bash does not support ${var@Q}; these logs are uglier
            { printf '%q ' "${cmd[@]}"; printf \n; } >&2 ;;
    
          *) # modern bash; shortest syntax, prettiest output
            printf '%s\n' "${cmd[*]@Q}" >&2;;
        esac
        "${cmd[@]}"                               # execute our array
    }
    

    Note that in current shell releases printf %q will use backslashes rather than quotes for escaping, so it would change ggtest "some string" to have some\ string in the logs; not the worst thing in the word, but it's less pretty than ${array[*]@Q}'s representation