How to get arguments with flags in Bash

后端 未结 11 2043
余生分开走
余生分开走 2020-12-22 15:56

I know that I can easily get positioned parameters like this in bash:

$0 or $1

I want to be able to use flag options like this to s

相关标签:
11条回答
  • 2020-12-22 16:01

    I think this would serve as a simpler example of what you want to achieve. There is no need to use external tools. Bash built in tools can do the job for you.

    function DOSOMETHING {
    
       while test $# -gt 0; do
               case "$1" in
                    -first)
                        shift
                        first_argument=$1
                        shift
                        ;;
                    -last)
                        shift
                        last_argument=$1
                        shift
                        ;;
                    *)
                       echo "$1 is not a recognized flag!"
                       return 1;
                       ;;
              esac
      done  
    
      echo "First argument : $first_argument";
      echo "Last argument : $last_argument";
     }
    

    This will allow you to use flags so no matter which order you are passing the parameters you will get the proper behavior.

    Example :

     DOSOMETHING -last "Adios" -first "Hola"
    

    Output :

     First argument : Hola
     Last argument : Adios
    

    You can add this function to your profile or put it inside of a script.

    Thanks!

    Edit : Save this as a a file and then execute it as yourfile.sh -last "Adios" -first "Hola"

    #!/bin/bash
    while test $# -gt 0; do
               case "$1" in
                    -first)
                        shift
                        first_argument=$1
                        shift
                        ;;
                    -last)
                        shift
                        last_argument=$1
                        shift
                        ;;
                    *)
                       echo "$1 is not a recognized flag!"
                       return 1;
                       ;;
              esac
      done  
    
      echo "First argument : $first_argument";
      echo "Last argument : $last_argument";
    
    0 讨论(0)
  • 2020-12-22 16:02

    I like Robert McMahan's answer the best here as it seems the easiest to make into sharable include files for any of your scripts to use. But it seems to have a flaw with the line if [[ -n ${variables[$argument_label]} ]] throwing the message, "variables: bad array subscript". I don't have the rep to comment, and I doubt this is the proper 'fix,' but wrapping that if in if [[ -n $argument_label ]] ; then cleans it up.

    Here's the code I ended up with, if you know a better way please add a comment to Robert's answer.

    Include File "flags-declares.sh"

    # declaring a couple of associative arrays
    declare -A arguments=();
    declare -A variables=();
    
    # declaring an index integer
    declare -i index=1;
    

    Include File "flags-arguments.sh"

    # $@ here represents all arguments passed in
    for i in "$@"
    do
      arguments[$index]=$i;
      prev_index="$(expr $index - 1)";
    
      # this if block does something akin to "where $i contains ="
      # "%=*" here strips out everything from the = to the end of the argument leaving only the label
      if [[ $i == *"="* ]]
        then argument_label=${i%=*}
        else argument_label=${arguments[$prev_index]}
      fi
    
      if [[ -n $argument_label ]] ; then
        # this if block only evaluates to true if the argument label exists in the variables array
        if [[ -n ${variables[$argument_label]} ]] ; then
          # dynamically creating variables names using declare
          # "#$argument_label=" here strips out the label leaving only the value
          if [[ $i == *"="* ]]
            then declare ${variables[$argument_label]}=${i#$argument_label=} 
            else declare ${variables[$argument_label]}=${arguments[$index]}
          fi
        fi
      fi
    
      index=index+1;
    done;
    

    Your "script.sh"

    . bin/includes/flags-declares.sh
    
    # any variables you want to use here
    # on the left left side is argument label or key (entered at the command line along with it's value) 
    # on the right side is the variable name the value of these arguments should be mapped to.
    # (the examples above show how these are being passed into this script)
    variables["-gu"]="git_user";
    variables["--git-user"]="git_user";
    variables["-gb"]="git_branch";
    variables["--git-branch"]="git_branch";
    variables["-dbr"]="db_fqdn";
    variables["--db-redirect"]="db_fqdn";
    variables["-e"]="environment";
    variables["--environment"]="environment";
    
    . bin/includes/flags-arguments.sh
    
    # then you could simply use the variables like so:
    echo "$git_user";
    echo "$git_branch";
    echo "$db_fqdn";
    echo "$environment";
    
    0 讨论(0)
  • 2020-12-22 16:04

    getopt is your friend.. a simple example:

    function f () {
    TEMP=`getopt --long -o "u:h:" "$@"`
    eval set -- "$TEMP"
    while true ; do
        case "$1" in
            -u )
                user=$2
                shift 2
            ;;
            -h )
                host=$2
                shift 2
            ;;
            *)
                break
            ;;
        esac 
    done;
    
    echo "user = $user, host = $host"
    }
    
    f -u myself -h some_host
    

    There should be various examples in your /usr/bin directory.

    0 讨论(0)
  • 2020-12-22 16:07

    So here it is my solution. I wanted to be able to handle boolean flags without hyphen, with one hyphen, and with two hyphen as well as parameter/value assignment with one and two hyphens.

    # Handle multiple types of arguments and prints some variables
    #
    # Boolean flags
    # 1) No hyphen
    #    create   Assigns `true` to the variable `CREATE`.
    #             Default is `CREATE_DEFAULT`.
    #    delete   Assigns true to the variable `DELETE`.
    #             Default is `DELETE_DEFAULT`.
    # 2) One hyphen
    #      a      Assigns `true` to a. Default is `false`.
    #      b      Assigns `true` to b. Default is `false`.
    # 3) Two hyphens
    #    cats     Assigns `true` to `cats`. By default is not set.
    #    dogs     Assigns `true` to `cats`. By default is not set.
    #
    # Parameter - Value
    # 1) One hyphen
    #      c      Assign any value you want
    #      d      Assign any value you want
    #
    # 2) Two hyphens
    #   ... Anything really, whatever two-hyphen argument is given that is not
    #       defined as flag, will be defined with the next argument after it.
    #
    # Example:
    # ./parser_example.sh delete -a -c VA_1 --cats --dir /path/to/dir
    parser() {
        # Define arguments with one hyphen that are boolean flags
        HYPHEN_FLAGS="a b"
        # Define arguments with two hyphens that are boolean flags
        DHYPHEN_FLAGS="cats dogs"
    
        # Iterate over all the arguments
        while [ $# -gt 0 ]; do
            # Handle the arguments with no hyphen
            if [[ $1 != "-"* ]]; then
                echo "Argument with no hyphen!"
                echo $1
                # Assign true to argument $1
                declare $1=true
                # Shift arguments by one to the left
                shift
            # Handle the arguments with one hyphen
            elif [[ $1 == "-"[A-Za-z0-9]* ]]; then
                # Handle the flags
                if [[ $HYPHEN_FLAGS == *"${1/-/}"* ]]; then
                    echo "Argument with one hyphen flag!"
                    echo $1
                    # Remove the hyphen from $1
                    local param="${1/-/}"
                    # Assign true to $param
                    declare $param=true
                    # Shift by one
                    shift
                # Handle the parameter-value cases
                else
                    echo "Argument with one hyphen value!"
                    echo $1 $2
                    # Remove the hyphen from $1
                    local param="${1/-/}"
                    # Assign argument $2 to $param
                    declare $param="$2"
                    # Shift by two
                    shift 2
                fi
            # Handle the arguments with two hyphens
            elif [[ $1 == "--"[A-Za-z0-9]* ]]; then
                # NOTE: For double hyphen I am using `declare -g $param`.
                #   This is the case because I am assuming that's going to be
                #   the final name of the variable
                echo "Argument with two hypens!"
                # Handle the flags
                if [[ $DHYPHEN_FLAGS == *"${1/--/}"* ]]; then
                    echo $1 true
                    # Remove the hyphens from $1
                    local param="${1/--/}"
                    # Assign argument $2 to $param
                    declare -g $param=true
                    # Shift by two
                    shift
                # Handle the parameter-value cases
                else
                    echo $1 $2
                    # Remove the hyphens from $1
                    local param="${1/--/}"
                    # Assign argument $2 to $param
                    declare -g $param="$2"
                    # Shift by two
                    shift 2
                fi
            fi
    
        done
        # Default value for arguments with no hypheb
        CREATE=${create:-'CREATE_DEFAULT'}
        DELETE=${delete:-'DELETE_DEFAULT'}
        # Default value for arguments with one hypen flag
        VAR1=${a:-false}
        VAR2=${b:-false}
        # Default value for arguments with value
        # NOTE1: This is just for illustration in one line. We can well create
        #   another function to handle this. Here I am handling the cases where
        #   we have a full named argument and a contraction of it.
        #   For example `--arg1` can be also set with `-c`.
        # NOTE2: What we are doing here is to check if $arg is defined. If not,
        #   check if $c was defined. If not, assign the default value "VD_"
        VAR3=$(if [[ $arg1 ]]; then echo $arg1; else echo ${c:-"VD_1"}; fi)
        VAR4=$(if [[ $arg2 ]]; then echo $arg2; else echo ${d:-"VD_2"}; fi)
    }
    
    
    # Pass all the arguments given to the script to the parser function
    parser "$@"
    
    
    echo $CREATE $DELETE $VAR1 $VAR2 $VAR3 $VAR4 $cats $dir
    

    Some references

    • The main procedure was found here.
    • More about passing all the arguments to a function here.
    • More info regarding default values here.
    • More info about declare do $ bash -c "help declare".
    • More info about shift do $ bash -c "help shift".
    0 讨论(0)
  • 2020-12-22 16:10

    Another alternative would be to use something like the below example which would allow you to use long --image or short -i tags and also allow compiled -i="example.jpg" or separate -i example.jpg methods of passing in arguments.

    # declaring a couple of associative arrays
    declare -A arguments=();  
    declare -A variables=();
    
    # declaring an index integer
    declare -i index=1;
    
    # any variables you want to use here
    # on the left left side is argument label or key (entered at the command line along with it's value) 
    # on the right side is the variable name the value of these arguments should be mapped to.
    # (the examples above show how these are being passed into this script)
    variables["-gu"]="git_user";  
    variables["--git-user"]="git_user";  
    variables["-gb"]="git_branch";  
    variables["--git-branch"]="git_branch";  
    variables["-dbr"]="db_fqdn";  
    variables["--db-redirect"]="db_fqdn";  
    variables["-e"]="environment";  
    variables["--environment"]="environment";
    
    # $@ here represents all arguments passed in
    for i in "$@"  
    do  
      arguments[$index]=$i;
      prev_index="$(expr $index - 1)";
    
      # this if block does something akin to "where $i contains ="
      # "%=*" here strips out everything from the = to the end of the argument leaving only the label
      if [[ $i == *"="* ]]
        then argument_label=${i%=*} 
        else argument_label=${arguments[$prev_index]}
      fi
    
      # this if block only evaluates to true if the argument label exists in the variables array
      if [[ -n ${variables[$argument_label]} ]]
        then
            # dynamically creating variables names using declare
            # "#$argument_label=" here strips out the label leaving only the value
            if [[ $i == *"="* ]]
                then declare ${variables[$argument_label]}=${i#$argument_label=} 
                else declare ${variables[$argument_label]}=${arguments[$index]}
            fi
      fi
    
      index=index+1;
    done;
    
    # then you could simply use the variables like so:
    echo "$git_user";
    
    0 讨论(0)
  • 2020-12-22 16:15

    I had trouble using getopts with multiple flags, so I wrote this code. It uses a modal variable to detect flags, and to use those flags to assign arguments to variables.

    Note that, if a flag shouldn't have an argument, something other than setting CURRENTFLAG can be done.

        for MYFIELD in "$@"; do
    
            CHECKFIRST=`echo $MYFIELD | cut -c1`
    
            if [ "$CHECKFIRST" == "-" ]; then
                mode="flag"
            else
                mode="arg"
            fi
    
            if [ "$mode" == "flag" ]; then
                case $MYFIELD in
                    -a)
                        CURRENTFLAG="VARIABLE_A"
                        ;;
                    -b)
                        CURRENTFLAG="VARIABLE_B"
                        ;;
                    -c)
                        CURRENTFLAG="VARIABLE_C"
                        ;;
                esac
            elif [ "$mode" == "arg" ]; then
                case $CURRENTFLAG in
                    VARIABLE_A)
                        VARIABLE_A="$MYFIELD"
                        ;;
                    VARIABLE_B)
                        VARIABLE_B="$MYFIELD"
                        ;;
                    VARIABLE_C)
                        VARIABLE_C="$MYFIELD"
                        ;;
                esac
            fi
        done
    
    0 讨论(0)
提交回复
热议问题