Search code examples
bashshellunixargumentsspaces

BASH shell expand arguments with spaces from variable


Say I have a variable $ARGS which contains the following:

file1.txt "second file.txt" file3.txt

How can I pass the contents of $ARGS as arguments to a command (say cat $ARGS, for example), treating "second file.txt" as one argument and not splitting it into "second and file.txt"?

Ideally, I'd like to be able to pass arguments to any command exactly as they are stored in a variable (read from a text file, but I don't think that's pertinent).

Thanks!


Solution

  • It's possible to do this without either bash arrays or eval: This is one of the few places where the behavior of xargs without either -0 or -d extensions (a behavior which mostly creates bugs) is actually useful.

    # this will print each argument on a different line
    # ...note that it breaks with arguments containing literal newlines!
    xargs printf '%s\n' <<<"$ARGS"
    

    ...or...

    # this will emit arguments in a NUL-delimited stream
    xargs printf '%s\0' <<<"$ARGS"
    
    # in bash 4.4, you can read this into an array like so:
    readarray -t -d '' args < <(xargs printf '%s\0' <<<"$ARGS")
    yourprog "${args[@]}" # actually run your programs
    
    # in bash 3.x or newer, it's just a bit longer:
    args=( );
    while IFS= read -r -d '' arg; do
        args+=( "$arg" )
    done < <(xargs printf '%s\0' <<<"$ARGS")
    yourprog "${args[@]}" # actually run your program
    
    # in POSIX sh, you can't safely handle arguments with literal newlines
    # ...but, barring that, can do it like this:
    set --
    while IFS= read -r arg; do
        set -- "$@" "$arg"
    done < <(printf '%s\n' "$ARGS" | xargs printf '%s\n')
    yourprog "$@" # actually run your program
    

    ...or, letting xargs itself do the invocation:

    # this will call yourprog with ARGS given
    # ...but -- beware! -- will cause bugs if there are more arguments than will fit on one
    # ...command line invocation.
    printf '%s\n' "$ARGS" | xargs yourprog