1

I am new to Bash scripting, I've used Visual Basic in Windows before and am looking for a bash equivalent string function for VB's inStr().

example in VB :

strMain = "abcABC123ABCabc"
searchStartPos = 6   'Start searching at 6th char.
subStr = "AB"        'Look for "AB".

pos = inStr(searchStartPos, strMain, subStr)

print pos

10                   'A match is found at 10th char.

I would really appreciate if someone could tell me how to do this in Bash.

1
  • I created a solution for you. Depending on what you want to do next, there may be better solutions. E.g. if you want to replace or delete the substring, you don't need to know its position explicitly. Compare XY problem. Now when the question is answered please do not change its scope. If you tell me (in a comment) what you want to do next, I may be able to guide you further. Nov 28, 2019 at 8:19

3 Answers 3

2

I'm not aware of any existing equivalent. Some external tools may provide something out of the box, but not Bash. Bash is able to do this, but you need to tell it how to do this; i.e. you need to write some shell code.

The following shell function is an example of such code:

inStr () {
    local start string substring len pos
    start="$1"
    string="$2"
    substring="$3"

    len="${#string}"

    # strip start-1 characters
    string="${string:start-1}"

    # strip the first substring and everything beyond
    string="${string%%"$substring"*}"

    # the position is calculated
    pos=$((start+${#string}))

    # if it's more than the original length, it means "not found"
    if [ "$pos" -gt "$len" ]; then
       # not found, adjust the behavior to your needs
       # e.g. you can print -1
       return 1
    else
       printf "%s\n" "$pos"
       return 0
    fi
}

Usage:

inStr 6 "abcABC123ABCabc" "AB"

Notes:

  • In the line

    string="${string%%"$substring"*}"
    

    $substring is double-quoted. Whatever you pass as substring will always be treated literally. E.g. A*a will only match literal A*a. If the line was like this:

    string="${string%%$substring*}"
    

    then you could pass a pattern to match. In this case A*a could match the ABCa fragment. Note you need to quote such substring while invoking the function, otherwise A*a may get expanded to (possibly multiple) names of matching files (this term includes directories) in the current directory (globbing).

  • The function does not validate its input; in particular it produces garbage when a nonpositive starting position is used. Implement some tests if needed.
  • The behavior for edge cases (empty string and/or substring) may not be as you expect. Implement some logic if needed.
4
  • Thanks for your solution Kamil, I've tested it and it works. Regarding your compare X,Y problem question, guilty as charged, I can do text manipulation with VB string functions but being new to bash, I find it's text manipulation features are more cryptic and difficult to understand. By getting the same VB functions in bash I can use them to solve these problems at least while I'm getting up to speed with pure bash.
    – plogwyn
    Nov 29, 2019 at 18:07
  • ... also the code and comments are informative and help me to understand whats happening. I need to extract two substrings from Bluetooth string generated by lsusb like so : Bus 001 Device 004: ID 105b:e065 Foxconn International, Inc. BCM43142A0 Bluetooth module The substrings are BCM43142A0 and 105b:e065,
    – plogwyn
    Nov 29, 2019 at 18:17
  • @plogwyn Learn grep, awk, sed, cut,… Bash itself is not the best tool for string manipulation. Example: lsusb | grep Bluetooth | grep -o 'ID ....:.... ' | cut -d ' ' -f 2. Nov 29, 2019 at 18:54
  • Kamil, I'm looking into these tools now. Many thanks for your advice.
    – plogwyn
    Nov 30, 2019 at 18:33
1

In pure bash(1), how about ...

# Returns start position or empty string if not found - removes the
# substring + everything thereafter, thus the value of the length of the
# remaining string is the actual index of the start of the substring
instr() {
  pos = ${1/$2*}
  case ${#pos} in ${#1}) echo ;; *) echo ${#pos} ;
}

strMain="abcABC123ABCabc"
subStr="AB"        # Look for "AB".

instr "$strMain" "$subStr" # 3

0

There's a similar function strpos(haystack, needle, pos) in PHP which has been ported to bash and other shells as well. Search for "bash strpos" and you will easily find it, for example here.

The VB function is then quite easily implemented:

inStr() { strpos $2 $3 $1; }

The pos parameter is optional in strpos.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .