3

I am trying to compare strings against a list of other strings read from a file.

However some of the strings in the file contain wildcard characters (both ? and *) which need to be taken into account when matching.

I am probably missing something but I am unable to see how to do it

Eg.

I have strings from file in an array which could be anything alphanumeric (and include commas and full stops) with wildcards : (a?cd, xy, q?hz, j,h-??)

and I have another string I wish to compare with each item in the list in turn. Any of the strings may contain spaces.

so what I want is something like

teststring="abcdx.rubb ish,y"

matchstrings=("a?cd" "*x*y" "q?h*z" "j*,h-??")

for i in "${matchstrings[@]}" ; do

    if [[ "$i" == "$teststring" ]]; then # this test here is the problem
        <do something>
    else
        <do something else>
    fi
done

This should match on the second "matchstring" but not any others

Any help appreciated

Peter McN
  • 31
  • 2
  • I don't think there's a way to do this using globs, but you could use [`=~` for regex matching](https://stackoverflow.com/questions/17420994/how-can-i-match-a-string-with-a-regex-in-bash). – Sagar Mar 05 '22 at 17:36
  • Looked at the discussion you linked to but I don't see how it helps. What am I missing? – Peter McN Mar 05 '22 at 22:03

2 Answers2

5

Yes; you just have the two operands to == reversed; the glob goes on the right (and must not be quoted):

if [[ $teststring == $i ]]; then

Example:

$ i=f*
$ [[ foo == $i ]] && echo pattern match
pattern match

If you quote the parameter expansion, the operation is treated as a literal string comparison, not a pattern match.

$ [[ foo == "$i" ]] || echo "foo != f*"
foo != f*

Spaces in the pattern are not a problem:

$ i="foo    b*"
$ [[ "foo    bar" == $i ]] && echo pattern match
pattern match
chepner
  • 497,756
  • 71
  • 530
  • 681
  • Thank you chepner. I think I see how this would work but I don't think it will for me if the strings contain spaces (which they may). Sorry I should have mentioned that in my initial question but didn't think it would matter - now revised. – Peter McN Mar 05 '22 at 22:56
  • Parameter expansions don't undergo word-splitting inside `[[ ... ]]`. The quotes are there purely to indicate whether meta characters like `*` are intended as literal characters or as pattern wildcards. – chepner Mar 05 '22 at 23:01
  • If `i` has the value `foo bar`, `$i` is the single word `foo bar`, not two words `foo` and `bar` as with parameter expansion in a simple command. – chepner Mar 05 '22 at 23:04
  • Then this is exactly what I need. Thank you! As you may gather I am still a relative newby at writing bash scripts – Peter McN Mar 06 '22 at 18:44
  • Thank you so much. Just what I need - brilliant – Peter McN Mar 06 '22 at 22:43
3

You can do this even completely within POSIX, since case alternatives undergo parameter substitution:

#!/bin/sh

teststring="abcdx.rubbish,y"

while IFS= read -r matchstring; do
  case $teststring in
  ($matchstring) echo "$matchstring";;
  esac
done << "EOF"
a?cd
*x*y
q?h*z
j*,h-??
EOF

This outputs only *x*y as desired.

Jens
  • 69,818
  • 15
  • 125
  • 179
  • Good answer, but I would use `IFS= read -r matchstring`, to ensure lines are passed verbatim. Also `"EOF"` (quoted here-doc delimiter) for the same reason. Although OP said patterns will ultimately come from a file anyway. – dan Mar 07 '22 at 01:04
  • @dan Thanks for the suggestion; I edited the code as you suggested. I used the here-doc so the OP can easily replace it with `< file` (and is not tempted to use a *Useless Use Of Cat*). – Jens Mar 07 '22 at 07:16