In Bash how to get the position of a substring, within a string, starting the search at character position n

0

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.

plogwyn

Posted 2019-11-28T00:13:50.107

Reputation: 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.

– Kamil Maciorowski – 2019-11-28T08:19:47.720

Answers

0

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.

Kamil Maciorowski

Posted 2019-11-28T00:13:50.107

Reputation: 38 429

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 – 2019-11-29T18:07:58.913

... 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 – 2019-11-29T18:17:58.457

@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. – Kamil Maciorowski – 2019-11-29T18:54:23.277

Kamil, I'm looking into these tools now. Many thanks for your advice. – plogwyn – 2019-11-30T18:33:56.310