Find all occurence of a pattern in a string using bash

Viewed 216

I want to get all the character sequence which match a particular pattern in a string using bash.

Example 1:

Input:
Some random prefix "characters inside quotes".fixed() some random postfix  
Output:
characters inside quotes

Example 2:

Input:
Some random prefix "characters inside quotes first".fixed() some random postfix, some random post prefix "characters inside quotes second".fixed() some random post postfix.  
Output:  
characters inside quotes first  
characters inside quotes second

I'm using this script:

input='Some random prefix "characters inside quotes".fixed() some random postfix'
pattern=".* \"(.*)\".fixed()"
[[ $input =~ $pattern ]]
echo "${BASH_REMATCH[1]}"

This works perfectly for Example 1 but not for Example 2. What should i do to generate the correct output for Example 2?

2 Answers

Note that [[ $input =~ $pattern ]] only matches first occurrence in the string. You need to use grep or other tools to get multiple matches from a string.

For example, with pcregrep, you can use:

input='Some random prefix "characters inside quotes".fixed() some random postfix'
pcregrep -o1 '"([^"]*)"' <<< "$input"
## => characters inside quotes first
##    characters inside quotes second

Details:

  • -o1 - output mode on, print capturing group #1 value(s) if match found
  • "([^"]*)" - a regex that matches a ", then captures any zero or more chars other than " into Group 1 and then a " is matched.

With grep/sed:

grep -o '"[^"]*"' <<< "$input" | sed -E 's/^"|"$//g'
## => characters inside quotes first
##    characters inside quotes second

With "[^"]*" regex, you match all occurrences of the pattern, then with sed, you remove a double quotation mark at the start and end of each match.

Here is a pure Bash solution without external tool:

#!/usr/bin/env bash

input='Input: Some random prefix "characters inside quotes first".fixed() some random postfix, some random post prefix "characters inside quotes second".fixed() some random post postfix.'
pattern='(.*)\"(.*)\"\.fixed\(\)'

matches=()

while [[ $input =~ $pattern ]]; do
  matches=("${BASH_REMATCH[2]}" "${matches[@]}")
  input="${BASH_REMATCH[1]}"
done

for match in "${matches[@]}"; do
  printf %s\\n "$match"
done

Output:

characters inside quotes first
characters inside quotes second

Can also be done without reversing the matches with a different pattern for the captured text between quotes:

#!/usr/bin/env bash

input='Input: Some random prefix "characters inside quotes first".fixed() some random postfix, some random post prefix "characters inside quotes second".fixed() some random post postfix.'
pattern='\"([^"]*)\"\.fixed\(\)(.*)'

while [[ $input =~ $pattern ]]; do
  printf %s\\n "${BASH_REMATCH[1]}"
  input="${BASH_REMATCH[2]}"
done
Related