Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I remove positional parameters in bash?

I have a script that takes parameters and I want to remove given named parameters

e.g. script.sh a b c and I want to remove 'b' so that

    $1 = a
    $2 = c

This also must work for any given set of args such that e.g.

    script.sh "a 1" b "c 2"
    $1 = "a 1"
    $2 = "c 2"

A previous answer said

    for var in "$@"; do
        case $var in
            "a") echo -n $var ;;
            "b") ;;
        esac
    done

The generic version of the previous answer should have been (in order to remove b)

    for var in "$@"; do
        case $var in
            "b") ;;
            *) echo -n $var ;;
        esac
    done

But this fails for multiple parameters e.g.

    script.sh a b c
    Output: ac

We wanted a b and we wanted them in positional parameter $1=a and $2=b

So a better answer is needed.

like image 660
Marshall Jobe Avatar asked Dec 06 '25 07:12

Marshall Jobe


2 Answers

In the special case (commonly occurring) that you want to remove some initial positional parameters, you use shift. By default that deletes $1; with a numeric argument, it will delete n parameters.

Now, in your specific case, we can use a combination of shift and set --.

local a=$1
shift 2  # delete $1 and $2
set -- "$a" "$@"   # insert $1 before original $3 ... $n

Using Bash arrays is another possibility. If we translate the "$@" arguments into a bash array.

local args=("$@")    # convert $1 $2 ... into ${args[0]} ${args[1]} ...
unset args[1]        # delete args[1]; note: elements DO NOT SHIFT DOWN!
set -- "${args[@]}"  # convert array back to positional params

When we convert the array back to positional params with the "${args[@]}" expansion syntax, the unset args[1] element is absent, and so the original $2 disappears.

Of course, if we need those arguments in order to pass them to a function or script, we don't have to convert the array back to positional parameters. Just:

local args=("$@")    # convert $1 $2 ... into ${args[0]} ${args[1]} 
unset args[1]        # delete second arg
script "${args[@]}"  # call script with second argument deleted

Likewise, in my very first example, we could just call whatever we need to call rather than using set to further edit the positionals:

local a=$1         # preserve $1 in temporary variable
shift 2            # delete $1 and $2
script "$a" "$@"   # call target script with original $1 $3 $4 ...
like image 97
Kaz Avatar answered Dec 08 '25 21:12

Kaz


Try this Shellcheck-clean Bash code:

#! /bin/bash -p

args=()
for a in "$@"; do
    [[ $a == b ]] || args+=( "$a" )
done
set -- "${args[@]}"

(( $# > 0 )) && printf '%s\n' "$@"
  • When run with arguments 'a 1' b 'c 2' it produces output

    a 1
    c 2
    
  • When run with arguments -x "a'1" b 'c 2' "'\$(echo REBOOTING >&2)'" it produces output

    -x
    a'1
    c 2
    '$(echo REBOOTING >&2)'
    
  • The -- argument to set prevents subsequent arguments (e.g. -x) being treated as options.

like image 20
pjh Avatar answered Dec 08 '25 23:12

pjh



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!