0

How can I count the number of occurrences of a substring in a string using Bash?

EXAMPLE:

I'd like to know how many times this substring...

Bluetooth
         Soft blocked: no
         Hard blocked: no

...occurs in this string...

0: asus-wlan: Wireless LAN
         Soft blocked: no
         Hard blocked: no
1: asus-bluetooth: Bluetooth
         Soft blocked: no
         Hard blocked: no
2: phy0: Wireless LAN
         Soft blocked: no
         Hard blocked: no
113: hci0: Bluetooth
         Soft blocked: no
         Hard blocked: no

NOTE I: I have tried several approaches with sed, grep, awk... Nothing seems to work when we have strings with spaces and multiple lines.

NOTE II: I'm a Linux user and I'm trying a solution that does not involve installing applications/tools outside those that are usually found in Linux distributions.


IMPORTANT:

I would like something like the hypothetical example below. In this case we use two Shell variables (Bash).

EXAMPLE:

STRING="0: asus-wlan: Wireless LAN
         Soft blocked: no
         Hard blocked: no
1: asus-bluetooth: Bluetooth
         Soft blocked: no
         Hard blocked: no
2: phy0: Wireless LAN
         Soft blocked: no
         Hard blocked: no
113: hci0: Bluetooth
         Soft blocked: no
         Hard blocked: no"

SUB_STRING="Bluetooth
         Soft blocked: no
         Hard blocked: no"

awk -v RS='\0' 'NR==FNR{str=$0; next} {print gsub(str,"")}' "$STRING" "$SUB_STRING"

NOTE: We are using awk just to illustrate!

  • You probably will find more help on Stack Overflow, the SE site for programmers. You could also possibly find more help on Unix & Linux SE. – juniorRubyist May 7 '18 at 6:06
2

I assume it can be done better with awk, but this is the best I can offer.

grep -zo "Bluetooth\s*Soft blocked: no\s*Hard blocked: no" file_name | grep -c "Bluetooth"

-z makes grep treat the whole file as one line.

-o only writes the output that matched the string and not the whole line.
(in our case with -z that whould mean the whole file)

\s matches blank characters and new lines.

Second instance of grep will search only for the word "Bluetooth" in the output of the first grep call.

-c makes grep write the count of matched regex, instead of the matches themself.

  • Many thanks @Iskustvo, but I think there is a problem in your answer. We have to escape the string which modifies the amount of spaces and, additionally, we would have to have some functionality to automate this process. Other than that I'd like to be able to use Shell variables (Bash) (see the modification and my question)! I'll try to explain better. For example, AFAIK if my entry is " Soft blocked: no" or "Soft blocked: no" with the value "\s*Soft blocked: no" match result becomes indifferent to the spaces of the original string entered. Thanks! =D – Eduardo Lucio May 7 '18 at 17:23
  • 2
    I understand what you are saying, well, not all of it, but most of it. However I don't think I'm able to provide better answer. Your request exceeds the grep and sed tools and something havier like awk, perl or python will have to be used and I can't help you with that. I hope some other answer will match all your criteria. – Iskustvo May 7 '18 at 22:30
  • Many thanks for your contribution! Sorry for my bad English (actually I speak Portuguese)! =D – Eduardo Lucio May 8 '18 at 1:14
0

It's a bit unclear how you'd like to match (you additional example in the comment above does not shed any light), however assume that you store your string block with the network information inside a file string and the substring block inside a file substring.

Using the following approach, you'd get what I understand you expected: 2 matches.

cat string | tr -s " " | tr '\n' '@' | grep -o "$(cat substring | tr -s " " | tr '\n' '@')" | wc -l

Essentially, both strings are condensed into one line, ignoring whitespaces or tabs, and converting newlines into @. Using the grep -o syntax, we print all the occurrences (-o) of the pattern found.

However, it's unclear if in your example you expect it to match 0 times (exact positional match) or 2 times (ignoring prepended text). This is a very similar solution to what Iskustvo posted here; could it be that we misunderstood your intention?

If you're trying to count matches of a 2-dimensional text fragment, you probably need fuzzy grep.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.