HarshvardhanSharma
HarshvardhanSharma

Reputation: 786

Bash: Find and replace using Parameter Expansion only

I want to replace the input,

input_string: @include circle-progress(38px, 30px, #4eb630)

and output,

Output_string: @include circle-progress(38px, 30px)

using ${input_string//pattern/replacement_string} where pattern is , #[A-Fa-f0-9]{3,6} I supply.

This pattern matches the portion I want to replace when I test it in regex101.com, but the code

replace_pat=", \#[A-Fa-f0-9]{3,6}"
echo "${input_string//"$replace_pat"/}"

outputs exactly the same input. I don't want to use anything else like sed. Don't know where I am going wrong.

Upvotes: 1

Views: 1403

Answers (4)

Charles Duffy
Charles Duffy

Reputation: 295291

The below was originally a self-answer added to the question by the OP.

SOLVED. Thank You Gordon.

The solution with glob patterns works perfectly, but now the code below still prints exactly the same input i.e the line matching pattern, when I read lines of code from a file.

pattern="@include circle-progress\(([0-9]{1,3}px, ){2}#[A-Fa-f0-9]{3,6}\)"
replace_glob=', #[A-Fa-f0-9][A-Fa-f0-9][A-Fa-f0-9]?([A-Fa-f0-9]?([A-Fa-f0-9]?([A-Fa-f0-9])))'
while IFS='' read -r line || [[ -n "$line" ]]; do
    if [[ $line =~ $pattern ]] 
    then
        echo "${line//$replace_glob/}" 
    fi
done < "$1"

Upvotes: 0

Gordon Davisson
Gordon Davisson

Reputation: 125708

The pattern replacement you're using (${var//pattern/replacement}) uses glob patterns, not regular expressions. Glob patterns are significantly less powerful than regular expressions (and have very different syntax), so you can't entirely do what you're trying to. Well, unless you're using bash (not a generic POSIX shell) and enable extended glob patterns. Their syntax is very different from regular expressions, but they're actually just as powerful (but sometimes inelegant). Here's how to do this in bash:

shopt -s extglob    # Enable extended glob pattern syntax
replace_glob=', #[A-Fa-f0-9][A-Fa-f0-9][A-Fa-f0-9]?([A-Fa-f0-9]?([A-Fa-f0-9]?([A-Fa-f0-9])))'
input_string='@include circle-progress(38px, 30px, #4eb630)'
echo "${input_string//$replace_glob/}"    # prints "@include circle-progress(38px, 30px)"

Did I mention that extended globs can be inelegant? They're especially bad at bounded repetitions (the {3,6} in the regular expression, meaning between 3 and 6 of the preceding item). The glob above starts with ", #" (obvious), then three "[A-Fa-f0-9]"'s, then an optional (indicated by ?( )) pattern that includes another hex digit and an optional string that... well, you get the idea. If you want to simplify it and just allow any number of hex digits, then it gets much simpler:

replace_glob=', #*([A-Fa-f0-9])'

(BTW, also note that I didn't double-quote the pattern variable when using it -- that's because the shell treats quoted patterns (or parts of patterns) as literal strings, where the glob syntax is ignored. This is one of the few cases in the shell where you shouldn't double-quote variable references.)

Upvotes: 3

oliv
oliv

Reputation: 13239

You could use:

str='input_string: @include circle-progress(38px, 30px, #4eb630)'; echo ${str/, #[A-Fa-f0-9]*/)}

or with sed:

echo 'input_string: @include circle-progress(38px, 30px, #4eb630)' | sed 's/, #[A-Fa-f0-9]\{3,6\}//'

Upvotes: 0

sahaquiel
sahaquiel

Reputation: 1838

[root@XXX ~]# a="@include circle-progress(38px, 30px**, #4eb630**)"; echo $a | sed 's/\*\*,.*/)/g'
@include circle-progress(38px, 30px)

Upvotes: 0

Related Questions