dylanaraps / pure-sh-bible

📖 A collection of pure POSIX sh alternatives to external processes.
MIT License
6.45k stars 281 forks source link

Add function to find substring at the beginning of a string #51

Open tricantivu opened 1 year ago

tricantivu commented 1 year ago

printf precision and %s conversion specifications can be used to limit the bytes/characters written from a string. To take advantage of this we can use the if compound command, test builtin command, command substitution, and parameter expansion to compare the beginning of a string and a substring.

if [ "$(printf "%.${#substring}s" "${string}")" = "${substring}" ]; then
    printf 'Starts with\n'
else
    printf 'Does not start with\n'
fi

It does not really matter if a parameter is empty or unset, because its length will be zero and identical strings will be compared.

...a null digit string is treated as zero.

From: POSIX

# Both are empty
string=''
substring=''

# '' and '' are compared
if [ "$(printf "%.${#substring}s" "${string}")" = "${substring}" ]; then
    printf 'Starts with\n'
else
    printf 'Does not start with\n'
fi

I have executed the test shell script without errors and tested the code against many strings.

#!/usr/bin/env dash

startswith() {

    if [ "$(printf "%.${#1}s" "$2")" = "$1" ]; then
        return 0
    else
        return 1
    fi
}

startswith "$1" "$2"

Requires zsh >= 5.9:

for i in {1..10175}; do char="$(printf "\U$(( [##16]i ))")"; ./startswith "${char}" "${char}"; done
for i in {1..10175}; do char="$(printf "\U$(( [##16]i ))")"; ./startswith "${char}" "${char}oooooooooo"; done

The same approach (although more convoluted) can used to find substrings at the end:

string=foo
substring=oo

if [ "${string#$(printf "%.$(( "${#string}" - "${#substring}" ))s" "${str}")}" = "${substr}" ]; then
    printf 'Ends with\n'
else
    printf 'Does not end with\n'
fi