Home > other >  Shell, compare two script with wildcard
Shell, compare two script with wildcard

Time:01-13

I am trying to compare two "almost" identical strings in a shell script. They are similar except for one character (A-Z). In the example below I want the code to return true for all cases of the string from 1-2-3-A-5-6- to 1-2-3-Z-5-6-

Currently my code returns false.

#!/bin/bash
TEMPLATE='1-2-3-*-5-6-'
CASE='1-2-3-A-5-6-'
if [[ "$TEMPLATE" == "$CASE" ]]; then
  echo "It's there."
else
  echo "NO, improve!"
fi

CodePudding user response:

The pattern 1-2-3-*-5-6- is a glob.

This pattern language is described in the Pattern Matching section of the bash manual, and available to both the [[ == ]] test you're trying to use (manual quoted below), and the case version mentioned in a comment.

[[ expression ]]

... When the == and != operators are used, the string to the right of the operator is considered a pattern and matched according to the rules described below under Pattern Matching, as if the extglob shell option were enabled. The = operator is equivalent to ==. If the nocase‐ match shell option is enabled, the match is performed without regard to the case of alphabetic characters.

... Any part of the pattern may be quoted to force the quoted portion to be matched as a string.

There are only two constraints:

  1. you can't quote the pattern if you want it to be matched as a pattern rather than a literal string

    (see the last quoted paragraph)

  2. the pattern must go on the right hand side of the operator

    (first paragraph)

So your code will work fine as

#!/bin/bash
TEMPLATE='1-2-3-*-5-6-'
CASE='1-2-3-A-5-6-'
if [[ $CASE == $TEMPLATE ]]; then
  echo "It's there."
else
  echo "NO, improve!"
fi


NB. Part of the reason for using the built-in [[ ]] conditions instead of the old [ ] ones is specifically that

Word splitting and pathname expansion are not performed on the words between the [[ and ]];

so you don't generally need to quote variables (and in this particular case, must not quote at least one of them).

CodePudding user response:

Something like this.

#!/usr/bin/env bash

TEMPLATE='1-2-3-*-5-6-'
CASE='1-2-3-A-5-6-'
if [[ "$CASE" == ${TEMPLATE/\*/[A-Z]} ]]; then
  echo "It's there."
else
  echo "NO, improve!"
fi

Or you could add the glob/pattern [A-Z] directly at the assignment instead of the glob *.

#!/usr/bin/env bash

TEMPLATE='1-2-3-[A-Z]-5-6-'
CASE='1-2-3-E-5-6-'
if [[ "$CASE" == $TEMPLATE ]]; then
  echo "It's there."
else
  echo "NO, improve!"
fi

The accepted answer already explained the immediate error/problem with the OP's code. Just want to add a small snippet from set -x to support as to why you don't need to quote the rhs Right Hand Side of the = or == if the intention is to do a pattern matching.

  TEMPLATE='1-2-3-[A-Z]-5-6-'
  CASE=1-2-3-T-5-6-
  [[ 1-2-3-T-5-6- == \1\-\2\-\3\-\[\A\-\Z\]\-\5\-\6\- ]]
  echo 'NO, improve!'
NO, improve!

Quoting the RHS of the == or = or even =~ operator will make it a literal string not a pattern/glob/regex, since every string has a leading \, and \ is a form of quoting/escaping from the shell. What ever special meaning of a sting from the shell is now gone after it was escaped/quoted.

  • Related