1

Let x='abc.xyz' and y='abc:xyz' so that the following holds true (prints "matches" and "diff"):

[[ "${x}" =~ abc".xyz" ]] && echo "matches"
[[ "${y}" =~ abc".xyz" ]] || echo "diff"

Now, literal l=".xyz" can be extracted and tests still work (note double quotes around l refs):

[[ "${x}" =~ abc"${l}" ]] && echo "matches"
[[ "${y}" =~ abc"${l}" ]] || echo "diff"

And the problem: if we try further r="abc\"${l}\"" or r="abc${l}", the first test never prints "matches":

[[ "${x}" =~ ${r} ]] && echo "matches"
[[ "${y}" =~ ${r} ]] || echo "diff"

What should be the proper form of r to pass both tests?

3
  • It works fine for me Commented Jan 21, 2016 at 18:42
  • @anubhava No, it didn't. It is a delicate bash problem. Commented Jan 22, 2016 at 11:25
  • Of course it does, see this demo Commented Jan 22, 2016 at 15:20

1 Answer 1

1

The shell removes normally all unquoted " from the command line (they control only if arguments should be splitted or not), but there is special handling after =~. The quotes work here like escapes, everything between the quotes are handled as raw characters matching only itself (beside the variable substitution with $ that still work).

There is only one evaluation of the pattern, therefore quotes hidden in variables are considered as regular quotes, and do not trigger the special quote syntax.

You need to escape the . (or any other active) character in $l and the quote syntax does not work in variables.

If $l is always equal to .xyz, you can use r="abc\\${l}" to get the correct match. It is equal to r='abc\.xyz'.

Sign up to request clarification or add additional context in comments.

8 Comments

Putting \` into the r` assignment is very odd (since it assumes knowledge of the contents of l). That should go in l I would think.
@Etan Reisner - you are right but it is workaround, definitely not an elegant solution - one can quote all metachars in l. Something like ${l//./\.} would work given special case l=".xyz". In general case (for example l known only at runtime) parenthesis, brackets, and so on should be treated in the same way. It should be kind of function quote() ... and called from code.
@jofel - there is better way to do it than quoting all metachars in l. According to docs Any part of the pattern may be quoted to force the quoted portion to be matched as a string. It works for pattern abc".xyz", it works for abc"${l}", real pain is the introduction of r and subsequent dereference.
@j.l. Thanks, I extended/rewrote my answer. I do not think, that the pattern is re-evaluated, i.e. quotes in variables work.
@jofel Please note l can be pretty anything (even unknown if it comes from stdin or like). One solution (which you presented) is quoting all metachars in l, i.e. using ${l//...} instead of ${l}. Second, the elegant one, would be to treat whole ${l} as literal sequence. It is well supported in bash using double quotes on any pattern subregion. The hard part is variable expansion in the [[ ]] builtin context: neither r="abc\"${l}\"" or r="abc${l}" or even r="abc"${l}"" works.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.