Oscar Hierro
Oscar Hierro

Reputation: 1107

Bash variable substitution with a regex not working as expected

Given a bash variable holding the following string:

INPUT="Cookie: cf_clearance=foo; __cfduid=bar;"

Why is the substitution ${INPUT/cf_clearance=[^;]*;/} producing the output: Cookie: instead of what I'd expect: Cookie: __cfduid=bar;

Testing the same regex in online regex validators confirms that cf_clearance=[^;]*; should match cf_clearance=foo; only, and not the rest of the string.

What am I doing wrong here?

Upvotes: 5

Views: 12562

Answers (3)

tripleee
tripleee

Reputation: 189387

Like you have been told in comments, bash parameter substitution only supports glob patterns, not regular expressions. So the problem is really with your expectation, not with your code per se.

If you know that the expression can be anchored to the beginning of the string, you can use the ${INPUT#prefix} parameter substitution to grab the shortest possible match, and add back the Cookie: in front:

echo "Cookie: ${INPUT#Cookie: cf_clearance=*;}"

If you don't have this guarantee, something very similar can be approximated with a pair of parameter substitutions. Find which part precedes cf_clearance, find which part follows after the semicolon after cf_clearance; glue them together.

head=${INPUT%cf_clearance=*}
tail=${INPUT#*cf_clearance=*;}
echo "$head$tail"

(If you are not scared of complex substitutions, the temporary variables aren't really necessary or useful.

echo "${INPUT%cf_clearance=*}${INPUT#*cf_clearance=*;}"

This is a little dense even for my sophisticated taste, though.)

Upvotes: 1

chepner
chepner

Reputation: 531165

Use the actual regular-expression matching features instead of parameter expansion, which works with patterns.

[[ $INPUT =~ (.*)(cf_clearance=[^;]*;)(.*) ]]
ans=${BASH_REMATCH[1]}${BASH_REMATCH[3]}

You can also use an extended pattern, which is equivalent to a regular expression in power:

shopt -s extglob
$ echo "${INPUT/cf_clearance=*([^;]);/}"

Upvotes: 12

iBug
iBug

Reputation: 37227

Use sed:

INPUT=$(sed 's/cf_clearance=[^;]*;//' <<< "$INPUT")

Upvotes: 4

Related Questions