Home > Enterprise >  How to set argument at specific index when running bash script
How to set argument at specific index when running bash script

Time:02-20

I have a bash script that takes multiple arguments. I would like to know if there is a way when running the script to somehow set an argument at a specific index:

script.sh

a=$1
b=$2

usage
I would like to set the second argument only , for example b with the value 44

./script.sh [index] [value] -> ./script.sh 2 44
Is it possible to send the index also besides the value of the argument ?

If i have a long list of arguments i do not want to set them all.I know you can have default values, but is there any way to set specific argument?

CodePudding user response:

If I'm understanding your requirement correctly, would you please try:

#!/bin/bash

vars=(a b c d)                          # array of variable names

printf -v "${vars[$1 - 1]}" "$2"        # indirect variable assignment via "printf -v"
echo "$b"

Then invoke it with:

./script.sh 2 44

Output:

44

the subtraction -1 in the array index is because the bash array starts with 0.

CodePudding user response:

It's not clear to me what your question means. I think it may mean something like this:

  • You use a program (e.g. 'script.sh') that you can't change and that takes many positional arguments. (Such a program should really be changed to use options (e.g. as supported by getopt).)
  • Many or all of the positional arguments are optional and are often not used. The program is often run with commands like ./script.sh '' 44 '' '' '' '' '' '' '' '5 5 5' (i.e. argument 2 is 44, argument 10 is '5 5 5', and other arguments are empty (arguments 1,3,4,5,6,7,8, and 9) or unused (possible arguments after argument 10).
  • You would like to be able to just specify the argument numbers that are really used when running the program. Maybe something like ./script.sh 2:44 '10:5 5 5'.

If that's not what you want, you can stop reading now.

The bad news is that there is no mechanism in Bash to run commands like that. All positional arguments, up to the maximum used one, must be specified explicitly.

The good news is that it is possible to define a Bash function that will run commands like that for you. The function defined in this Shellcheck-clean code does it:

#! /bin/bash -p

function run_with_indexed_args
{
    local -r cmd=$1

    local cmd_args=()

    # For each argument from 2 onwards, check it is of the form INDEX:VALUE
    # and set element INDEX of array 'cmd_args' to VALUE
    local i idx_val idx val
    for ((i=2; i<=$#; i  )); do
        idx_val=${*:i:1}
        if [[ $idx_val != *?:* ]]; then
            printf "%s: ERROR: arg. %d (%q) is not of form INDEX:VALUE\\n"  \
                "${FUNCNAME[0]}" "$i" "$idx_val" >&2
            return 1
        fi
        idx=${idx_val%%:*}
        val=${idx_val#*:}
        if [[ $idx == *[^[:digit:]]* ]]; then
            printf "%s: ERROR: arg. %d (%q) is not of form INDEX:VALUE\\n"  \
                "${FUNCNAME[0]}" "$i" "$idx_val" >&2
            return 1
        elif (( 10#$idx == 0 )); then
            printf "%s: ERROR: arg. %d (%q) has invalid index (zero)\\n"  \
                "${FUNCNAME[0]}" "$i" "$idx_val" >&2
            return 1
        fi
        cmd_args[10#$idx-1]=$val
    done

    # Put empty strings in all unset indexes (holes) less than the maximum
    # used index in the 'cmd_args' array
    local ci prev_ci=-1
    for ci in "${!cmd_args[@]}"; do
        for ((i=prev_ci 1; i<ci; i  )); do
            cmd_args[i]=''
        done
        prev_ci=$ci
    done

    declare -p cmd_args

    "$cmd" ${cmd_args[@] "${cmd_args[@]}"}
}

Example usage:

 run_with_indexed_args ./script.sh 2:44 '10:5 5 5'

That will construct a standard argument list and run

./script.sh '' 44 '' '' '' '' '' '' '' '5 5 5'

The code does just about everything that I'd want it to do, but it's only lightly tested and the documentation is inadequate. Let me know if it could be of use to you and I'll do some more work on it.

  • Related