Bash Script - split string using regex delimiter

bash, regex, sh, split, string

Solution

The example provided in the OP (as well as the two answers provided by @GlennJackman and @devnull) assume that the actual question could have been:

In bash, how do I replace the match for a regular expression in a string with a newline.

That's not actually the same as "split a string using a regular expression", unless you add the constraint that the string does not contain any newline characters. And even then, it's not actually "splitting" the string; the presumption is that some other process will use a newline to split the result.

Once the question has been reformulated, the solution is not challenging. You could use any tool which supports regular expressions, such as `sed`:

sed 's/ *ONCE\[[^]]*] */\n/g' <<<"$variable"

(Remove the `g` if you only want to replace the first sequence; you may need to adjust the regular expression, since it wasn't quite clear what the desired constraints are.)

`bash` itself does not provide a `replace all` primitive using regular expressions, although it does have "patterns" and, if the option `extglob` is set (which is the default on some distributions), the patterns are sufficiently powerful to express the pattern, so you could use:

echo "${variable//*( )ONCE\[*([^]])]*( )/$'\n'}"

Again, you can make the substitution only happen once by changing `//` to `/` and you may need to change the pattern to meet your precise needs.

That leaves open the question of how to actually split a bash variable using a delimiter specified by a regular expression, for some definition of "split". One possible definition is "call a function with the parts of the string as arguments"; that's the one which we use here:

# Usage:
# call_with_split <pattern> <string> <cmd> <args>...
# Splits string according to regular expression pattern and then invokes
# cmd args string-pieces
call_with_split () { 
  if [[ $2 =~ ($1).* ]]; then
    call_with_split "$1" \
                    "${2:$((${#2} - ${#BASH_REMATCH[0]} + ${#BASH_REMATCH[1]}))}" \
                    "${@:3}" \
                    "${2:0:$((${#2} - ${#BASH_REMATCH[0]}))}"
  else
    "${@:3}" "$2"
  fi
}

Example:

$ var="substring1 substring2 ONCE[0,10s] substring3"
$ call_with_split " ONCE\[[^]]*] " "$var" printf "%s\n"
substring1 substring2
substring3

Problem

I want to split string something like 'substring1 substring2 ONCE[0,10s] substring3'. The expected result should be (with delimiter 'ONCE[0,10s]'): ``` substring1 substring2 substring3 ``` The problem is that the number in delimiter is variable such as 'ONCE[0,1s]' or 'ONCE[0,3m]' or 'ONCE[0,10d]' and so on. How can I do this in bash script ? Any idea ? Thank you

Original source