Search code examples
bashshellposix

Remove pattern from beginning of string using only Bash pattern matching


Is it possible to remove a pattern from only the beginning of a string using Bash's built-in pattern matching and parameter substitution? (Bash 5.0.18)

Given the following array:

myparams=( --some-param -h --15 -q 100 )

I would like to loop through the elements and output the following (strip all -s from the front without affecting any that might come after). Desired output:

some-param
h
15
q
100

I am aware of Bash Hackers Wiki - Substring removal but I didn't see any way to do this.

Here is one failed attempt, Almost works, but some- gets clobbered:

$ for p in "${myparams[@]}"; do echo "$p ==> ${p##*-}" ; done
--some-param ==> param
-h ==> h
--15 ==> 15
-q ==> q
100 ==> 100

Another failed attempt, this time some-param becomes someparam:

$ for p in "${myparams[@]}"; do echo "$p ==> ${p//-}" ; done
--some-param ==> someparam
-h ==> h
--15 ==> 15
-q ==> q
100 ==> 100

Here's a working solution, but it relies on sed (tested with sed from macOS 10.15.7):

$ for p in "${myparams[@]}"; do echo "$p ==> $(sed 's/^-*//'<<<"$p")" ; done
--some-param ==> some-param
-h ==> h
--15 ==> 15
-q ==> q
100 ==> 100

And finally, here's a working method that uses BASH_REMATCH — is this the only builtin way?:

$ for p in "${myparams[@]}"; do [[ $p =~ ^(-*)?(.*) ]]; echo "$p ==> ${BASH_REMATCH[2]}" ; done
--some-param ==> some-param
-h ==> h
--15 ==> 15
-q ==> q
100 ==> 100

Solution

  • Use an extended pattern and parameter expansion:

    shopt -s extglob
    printf '%s\n' "${myparams[@]##+(-)}"
    
    • +(-) matches one or more dashes
    • # removes the string only at the beginning of the value
    • ## matches the longest string possible
    • Using an array as the parameter removes the prefixes from all its members