Bash arguments: Difference between revisions

From wikinotes
 
(7 intermediate revisions by the same user not shown)
Line 3: Line 3:


= Argument Variables =
= Argument Variables =
<blockquote>
<source lang="bash">
<source lang="bash">
$1  # first argument
$1  # first argument
Line 8: Line 9:
# ... etc.
# ... etc.


$?        # last command exit code
$$        # pid
$#        # number of arguments
$#        # number of arguments
$@        # array of all arguments
$@        # array of all arguments
${@[$#]}  # last argument
${@[$#]}  # last argument
 
${@:2}    # skip 1st argument, but all arguments afterwards
</source>
</source>
 
</blockquote><!-- Argument Variables -->


= Parsing Arguments =
= Parsing Arguments =
Line 76: Line 79:
== Combined Single Letter Params ==
== Combined Single Letter Params ==
<blockquote>
<blockquote>
Bash doesn't support regex, so we're limited to manually repeating character ranges.
Bash doesn't support regex, so we're limited to manually repeating character ranges.<br>


Say for example you wanted to accept the following calls (similar to ls).
<syntaxhighlight lang="bash">
<syntaxhighlight lang="bash">
# these are all available single-letter params
foo -abc          # same as `foo -a -b -c`
# ex: foo -a -b -c
foo -a -bc        # same as `foo -a -b -c`
avail_letters="abc"
foo -a --other     # same as `foo -a -b -c --other`
 
foo -abc -depth 2 # same as `foo -a -b -c -depth 2`
# accept as many as avail_letters,
</syntaxhighlight>
# only in combinations of avail_letters
case_value=$(
     for ((i=1; i<=${#avail_letters}; i++)); do
        echo -n "|-$(printf "[${avail_letters}]%.0s" {1..$i})"
    done
)
 
# strip 1st '|'
case_value="${case_value:1}" # "-[abc]|-[abc][abc]|-[abc][abc][abc]"


<syntaxhighlight lang="bash">
# now use your param case statement
# now use your param case statement
case $1 in
case $1 in
     $case_value)
     -h|--help)
        case a)
            echo "a received"
            ;;
        case b)
            echo "b received"
            ;;
        case c)
            echo "c received"
            ;;
    --help)
         echo "help me obi-wan kenobi"
         echo "help me obi-wan kenobi"
        return 0
         ;;
         ;;
  --other)
    --other)
         echo "other"
         echo "other"
         ;;
         ;;
     esac
     -*)
        param="$1"
        if echo "$param" | grep -E '^-[abc]+$' 2>&1 > /dev/null ; then
            for ((i=2; i<=${#param}; i++)); do  # each letter, after '-'
                case ${param[$i]} in
                    a)
                        echo "a received"
                        ;;
                    b)
                        echo "b received"
                        ;;
                    c)
                        echo "c received"
                        ;;
                esac
            done
            shift
        elif [[ "$1" == "-depth" ]] ; then
            local DEPTH=$1
            shift 2
        fi
        ;;
esac
esac
</syntaxhighlight>
</syntaxhighlight>

Latest revision as of 00:44, 10 November 2021

Arguments are handled the same way for functions and modules.


Argument Variables

$1   # first argument
$2   # second argument
# ... etc.

$?         # last command exit code
$$         # pid
$#         # number of arguments
$@         # array of all arguments
${@[$#]}   # last argument
${@:2}     # skip 1st argument, but all arguments afterwards

Parsing Arguments

Regular Params

parsing arguments in this way, it does not matter the order that parameters are passed in, or if they have are keyword arguments with a value.

# PARSE ALL ARGUMENTS

while [ $# -gt 0 ] ; do
    case $1 in
        -f|--file)
            filepath=$2
            shift 2        # jump forwards twice (once for '--file', once for 'filepath')
            ;;
        -d|--debug)
            debug=1
            shift
            ;;
        *)
            echo "default behaviour"
            ;;
    esac
done

Here is another variation

function parse_args() {
    args=($@)
    for ((i=0; i <= ${#args[@]}; i++))
    do
        case "${args[$i]}" in
            -h|--help)
                print_help
                exit 0
                ;;
            -L)
                LOCAL_PORTS+=( ${args[$i+1]} )
                ((i++))
                ;;
            -R)
                REMOTE_PORTS+=( ${args[$i+1]} )
                ((i++))
                ;;
            -t|--test)
                TEST=1
                ;;
            *)
                SSH_PARAMS+=" ${args[$i]}"
                ;;
        esac
    done
}

Combined Single Letter Params

Bash doesn't support regex, so we're limited to manually repeating character ranges.

Say for example you wanted to accept the following calls (similar to ls).

foo -abc           # same as `foo -a -b -c`
foo -a -bc         # same as `foo -a -b -c`
foo -a --other     # same as `foo -a -b -c --other`
foo -abc -depth 2  # same as `foo -a -b -c -depth 2`
# now use your param case statement
case $1 in
    -h|--help)
        echo "help me obi-wan kenobi"
        return 0
        ;;
    --other)
        echo "other"
        ;;
    -*)
        param="$1"
        if echo "$param" | grep -E '^-[abc]+$' 2>&1 > /dev/null ; then
            for ((i=2; i<=${#param}; i++)); do  # each letter, after '-'
                case ${param[$i]} in
                    a)
                        echo "a received"
                        ;;
                    b)
                        echo "b received"
                        ;;
                    c)
                        echo "c received"
                        ;;
                esac
            done
            shift
        elif [[ "$1" == "-depth" ]] ; then
            local DEPTH=$1
            shift 2
        fi
        ;;
esac

Counting Arguments

if [ $# -gt 2 ]; then
    echo "if there are more than 2 arguments"
fi
# see also
-eq   # equal
-ne   # not equal

-gt   # greater than
-ge   # greater or equal
-lt   # less than
-le   # less or equal

Iterating Arguments

for arg in "${args[@]}"; do
   echo $arg
done


for (( i=1; i<=(($#-1)); i++ )) ; do
	echo ${@[$i]};
done