0

I'm currently refreshing my shell scripting and trying to write a function which checks if string arg1 starts with string arg2. Following code always prints the opposite result, but I can't find my mistake.


// Update:
Based on comments, here is the now working solution:

#!/usr/bin/env bash


## TRUE if arg1 starts with arg2, otherwise FALSE
## @param string targetPhrase
## @param string searchPhrase
str_starts_with() {
  case "${1}" in "${2}"*) return 0;; *) return 1;; esac;
}


## TEST CASES
a='foobar' ; b='foo'    # 1
if str_starts_with "$a" "$b"; then echo '1'; else echo '0'; fi

a='f' ; b='foo'         # 0
if str_starts_with "$a" "$b"; then echo '1'; else echo '0'; fi

a='bar' ; b='f'         # 0
if str_starts_with "$a" "$b"; then echo '1'; else echo '0'; fi

exit 0
1
0
0
stuck1a
  • 13
  • 6
  • Your code is using bashisms; it isn't compatible with POSIX. Why are you tagging this posix? (To be POSIX compliant you'd need to change from `==` to `=`, and you couldn't use the C-style `for` loop or `${var:index:len}`). – Charles Duffy Aug 12 '23 at 12:05
  • 1
    And, err, `[[ ${target} = "${prefix}"* ]]` is a lot terser, faster, and easier to read than what you're doing here. No reason to loop over characters yourself when the shell can compare the whole string for you. – Charles Duffy Aug 12 '23 at 12:06
  • You are using `test` (`[ ... ]`) but your expressions are `bash` conditionals (`==`). Try `[[ ... ]]`. And your`[ ! ${#1} ]` is wrong. Try `(( ${#1} == 0 ))`. – Renaud Pacalet Aug 12 '23 at 12:07
  • Also, remember that `return 0` is truthy and `return 1` is falsey (because 0 is the exit status for success, and everything else is an error code), so your `echo`s actually echo the **opposite** of your function's real return value. – Charles Duffy Aug 12 '23 at 12:08
  • I already listed the things that are nonstandard for you. `[ "$1" == "$2" ]` is nonstandard -- to be standard it would need to be `[ "$1" = "$2" ]`. And `for (( i=0; i<${#2}; i++ ))` is nonstandard; it would need to be something like `i=-1; while i=$((i + 1)); [ "$i" -lt ${#2} ]; do` to be POSIX-compliant. And POSIX sh doesn't have a way to retrieve a requested character offset or substring from a string using only shell builtin syntax at all. – Charles Duffy Aug 12 '23 at 12:11
  • ...see https://pubs.opengroup.org/onlinepubs/009604499/utilities/xcu_chap02.html#tag_02_06_02 for the list of parameter expansions that are required by POSIX; it's a short one, and `${var:index:len}` isn't in it. – Charles Duffy Aug 12 '23 at 12:12
  • Anyhow -- to get the output you expect, take out the `if` statements and just use `str_starts_with "$a" "$b"; echo "$?"` in your test cases. Or fix your function to correctly return `0` when you want the result to be true. – Charles Duffy Aug 12 '23 at 12:13
  • That said, there **is** a POSIX equivalent to `[[ $target = "$prefix"* ]]` available: `str_starts_with() { case $target in "$prefix"*) return 0;; *) return 1;; esac; }` – Charles Duffy Aug 12 '23 at 12:15
  • If updated the source based on your comments. Thanks a lot. Now it is pure POSIX, right? – stuck1a Aug 12 '23 at 12:22
  • No, because `[[` itself is a bashism. That's why I suggested `case` as a POSIX alternative. – Charles Duffy Aug 12 '23 at 12:25
  • Yes, _that's_ now POSIX-compliant code. Mind, it's bad form; `a && b || c` is not the same as `if a; then b; else c; fi` -- but it _is_ standard-compliant. – Charles Duffy Aug 12 '23 at 12:26
  • @Charles Duffy I see. I've updated again with your suggestion. Thanks again! – stuck1a Aug 12 '23 at 12:26
  • (see https://mywiki.wooledge.org/BashPitfalls#cmd1_.26.26_cmd2_.7C.7C_cmd3 for a longer explanation re: `a && b || c`) – Charles Duffy Aug 12 '23 at 12:27

0 Answers0