Reading quoted/escaped arguments correctly from a string

前端 未结 4 1597
难免孤独
难免孤独 2020-11-22 03:48

I\'m encountering an issue passing an argument to a command in a Bash script.

poc.sh:

#!/bin/bash

ARGS=\'\"hi there\" test\'
./swap ${ARGS}
<         


        
4条回答
  •  粉色の甜心
    2020-11-22 04:24

    Ugly Idea Alert: Pure Bash Function

    Here's a quoted-string parser written in pure bash (what terrible fun)!

    Caveat: just like the xargs example above, this errors in the case of an escaped quote. This could be fixed... but much better to do in an actual programming language.

    Example Usage

    MY_ARGS="foo 'bar baz' qux * "'$(dangerous)'" sudo ls -lah"
    
    # Create array from multi-line string
    IFS=$'\r\n' GLOBIGNORE='*' args=($(parseargs "$MY_ARGS"))
    
    # Show each of the arguments array
    for arg in "${args[@]}"; do
        echo "$arg"
    done
    

    Example Output

    foo
    bar baz
    qux
    *
    

    Parse Argument Function

    This literally goes character-by-character and either adds to the current string or the current array.

    set -u
    set -e
    
    # ParseArgs will parse a string that contains quoted strings the same as bash does
    # (same as most other *nix shells do). This is secure in the sense that it doesn't do any
    # executing or interpreting. However, it also doesn't do any escaping, so you shouldn't pass
    # these strings to shells without escaping them.
    parseargs() {
        notquote="-"
        str=$1
        declare -a args=()
        s=""
    
        # Strip leading space, then trailing space, then end with space.
        str="${str## }"
        str="${str%% }"
        str+=" "
    
        last_quote="${notquote}"
        is_space=""
        n=$(( ${#str} - 1 ))
    
        for ((i=0;i<=$n;i+=1)); do
            c="${str:$i:1}"
    
            # If we're ending a quote, break out and skip this character
            if [ "$c" == "$last_quote" ]; then
                last_quote=$notquote
                continue
            fi
    
            # If we're in a quote, count this character
            if [ "$last_quote" != "$notquote" ]; then
                s+=$c
                continue
            fi
    
            # If we encounter a quote, enter it and skip this character
            if [ "$c" == "'" ] || [ "$c" == '"' ]; then
                is_space=""
                last_quote=$c
                continue
            fi
    
            # If it's a space, store the string
            re="[[:space:]]+" # must be used as a var, not a literal
            if [[ $c =~ $re ]]; then
                if [ "0" == "$i" ] || [ -n "$is_space" ]; then
                    echo continue $i $is_space
                    continue
                fi
                is_space="true"
                args+=("$s")
                s=""
                continue
            fi
    
            is_space=""
            s+="$c"
        done
    
        if [ "$last_quote" != "$notquote" ]; then
            >&2 echo "error: quote not terminated"
            return 1
        fi
    
        for arg in "${args[@]}"; do
            echo "$arg"
        done
        return 0
    }
    

    I may or may not keep this updated at:

    • https://git.coolaj86.com/coolaj86/git-scripts/src/branch/master/git-proxy

    Seems like a rather stupid thing to do... but I had the itch... oh well.

提交回复
热议问题