coolaj86
coolaj86

Reputation: 77102

How to match a substring using Posix Shell?

What's an idiomatic way to test if a string contains a substring in a Posix Shell?

Basically this, but Posix:

[[ ${my_haystack} == *${my_needle}* ]]

Non-Posix Example

I'm looking for the equivalent of this, but that works in a Posix / Almquist / dash / ash shell:

#!/bin/bash

set -e
set -u

find_needle() {
    my_haystack="${1}"
    my_needle="${2}"

    if [[ ${my_haystack} == *${my_needle}* ]]; then
        echo "'${my_haystack}' contains '${my_needle}'"
    else
        echo "'${my_haystack}' does NOT contain '${my_needle}'"
    fi
}

find_needle "${1:-"haystack"}" "${2:-"a"}"

(that doesn't work in sh)

My ideal solution would be one that doesn't require the use of a subshell or pipe, and that doesn't exit on failure in strict mode.

Workaround

This works, but I'm wondering if there's another way to test a substring without echoing and piping to grep.

#!/bin/sh

set -e
set -u

find_needle() {
    my_haystack="${1}"
    my_needle="${2}"
    if echo "${my_haystack}" | grep -q "${my_needle}"; then
        echo "'${my_haystack}' contains '${my_needle}'"
    else
        echo "'${my_haystack}' does NOT contain '${my_needle}'"
    fi
}

find_needle "${1:-"haystack"}" "${2:-"a"}"

Or maybe this is the most idiomatic way?

Upvotes: 0

Views: 879

Answers (1)

coolaj86
coolaj86

Reputation: 77102

Substring match with case

As @dave_thompson_085 points out [1], you can use case:

case $haystack in
    *$needle*)
        return 0
        ;;
    *)
        return 1
        ;;
esac

See also:

Example Script

  • ✅ no subshell
  • ✅ no pipe
  • 🤷‍♂️ simplest, most idiomatic? maybe
#!/bin/sh

set -e
set -u

test_substring() {
    haystack="${1}"
    needle="${2}"

    case $haystack in
        *$needle*)
            return 0
            ;;
        *)
            return 1
            ;;
    esac
}

find_needle() {
    my_haystack="${1}"
    my_needle="${2}"
    if test_substring "${my_haystack}" "${my_needle}"; then
        echo "'${my_haystack}' contains '${my_needle}'"
    else
        echo "'${my_haystack}' does NOT contain '${my_needle}'"
    fi
}

find_needle haystack a
find_needle haystack x

Output:

'haystack' contains 'a'
'haystack' does NOT contain 'x'

Upvotes: 1

Related Questions