5

I'm trying to write a shell script, and in it I have to look for the presence of -32 in a string. Unfortunately I have to do it without using any bashisms as it has to run on computers running Dash.

I tried case ${1+"$@"} in *-32*);; but that will pick up something like -321. Is there a way to use a regex or something to look for just that flag? It can have other flags around it separated by spaces in the string.

I think the regex I would need looks for -32 with either a space or end of line after. -32(\s|$)

I'm pretty new to regexes so I'm not sure if that is the best way to do it. Any help would be appreciated!

Thanks!

Mason
  • 89
  • 5

2 Answers2

3

You can use grep with word boundary:

grep -E '-32\b' FILE

\b matches at a 'word boundary' = the location between an alphanumeric character and a non-alphanumeric character.

In the default locale, an 'alphanumeric character' (as the term is used above) means any character that is matched by the character class [a-zA-Z0-9_] (i.e. any number, any of the letters A–Z and underscore).

In an analogous way \B matches wherever there is not a word boundary (i.e. any location where the characters on either side of it are of the same type, either both alphanumeric characters, or both non-alphanumeric).

So if you wanted to make sure that the minus sign must be preceded by a non-alphanumeric character (e.g. space), as well, you would write the following:

grep -E '\B-32\b' FILE

This will match xx -32 xx (with a space before the minus) but not xx-32 xx (without the space). Since space and minus are both non-alphanumeric \B will match between them, but \b will not.

If you wanted to make sure that the minus sign is preceded by a alphanumeric character instead, you would write the following:

grep -E '\b-32\b' FILE

Which matches e.g. x-32 x (without the space), but not x -32 x (with a space before the minus).

zrajm
  • 1,361
  • 1
  • 12
  • 21
anubhava
  • 761,203
  • 64
  • 569
  • 643
1

All these examples use only builtin functions, and are completely compatible with dash.

Finding Substrings

If you're actually looking for a substring of a string you can use case or if.

Let me illustrate by writing two different match functions using each. If you're lazy you can cut/paste anyone of these functions into your script and use:

if match -32 "$STRING"; then
    echo "found '-32'!"
fi

E.g. if you wanted to search for $SUBSTR at both beginning and end, and separated from the rest of the string by spaces you could use something like this:

function match() {
    local SUBSTR="$1" STR="$2"                # get args
    case " $STR " in                          #   add extra spaces
        *" $SUBSTR "*) return 0 ;;            #   return true if match
    esac
    return 1                                  # no match: return false
}

(In the above function I add space around $STR so I don't have to explicitly look for matches at the beginning and end of the string.)

function match() {
    local SUBSTR=" $1 " STR=" $2 "            # get args + add extra spaces
    [ "${STR#*"$SUBSTR"}" != "$STR" ] \
        && return 0                           # match: return true
    return 1                                  # no match: return false
}

This if statement is a little trickier. The expression ${STR#*"$SUBSTR"} part mean, take $STR, and remove everything from the beginning of it, up to the first occurrence of $SUBSTR (if $SUBSTR couldn't be found, don't remove anything). Then we compare that with the original $STR and if they are not the same, we know the substring was found.

Finding an argument

Now, if you're actually looking for an argument, you could use any of the above functions, and then simply use match "-32" "$*", but would find all substrings, and not distinguish the arguments from each other (e.g. in the unlikely event that any argument, such as a filename, happened to contain the string ' -32 ' that would match).

Better to just look at the args in that case. Again I'll write a little function for you that you can call with argmatch "$ARG" "$@"

argmatch() {
    local FIND="$1" ARG; shift               # get $FIND arg, remove it from $@
    for ARG; do                              # loop $@
        [ "$ARG" = "$FIND" ] && return 0     #   found? then return true
    done
    return 1                                 # return false
}
zrajm
  • 1,361
  • 1
  • 12
  • 21