Home > other >  How to use a 2D array as a result from a function in bash script
How to use a 2D array as a result from a function in bash script

Time:12-29

I have the following function:

function regex_match_2d_list {
  local regex=$1
  local text=$2
  local groups=()
  while [[ $text =~ $regex ]]; do
    local group=()
    for (( i=0; i<${#BASH_REMATCH[@]}; i   )); do
      group =("${BASH_REMATCH[$i]}")
    done
    groups =("${group[@]}")
    text=${text#*"${BASH_REMATCH[0]}"}
  done
  echo "${groups[@]}"
}

what I am trying to do is to have a function that is going to receive a regex and a text and it will output a 2d list where each element of that list is composed of all the groups from regex:

pattern="^([a-z] )\s([a-z] )"
text="hello world
foo bar"

res=$(regex_match_2d_list "$pattern" "$text")
echo "${res[0,1]}"

if I did understand it correctly, by doing res[0,2] I should receive "world" because is the second group of the first element.
enter image description here

but instead if am not getting anything. Could anyone explain to me what I am doing wrong here?

CodePudding user response:

Note: you can debug those problem yourself by adding 'declare -p BASH_REMATCH text` immediately after the match. Short version below:

The line: text=${text#*"${BASH_REMATCH[0]}"} is expected to remove the match. However, the match does NOT include the trailing separators after the second token (new line between "hello world" and "foo bar"). You want to change the match to remove any trailing new line.

One possible solution - add the new line to the end of the pattern. To handle the last line which is not terminated with new line - I made it optional (with '?'). Possible to append new line to the text as alternative.

pattern="^([a-z] )\s([a-z] )"$'\n?'

Second issue is the transfer of BASH_REMATCH to groups (via intermediate group variable). Current code also copy element 0 of RE_BATCH, which will result in duplicating entries in groups. Consider using groups =(${BASH_REMATCH[@]} instead.

Last problem is the assignment to 'res'. Current code will 'flatten' the array into single string. You probably want to get res as an array. One possible (but not always safe) to use array assignment res=($(regex_match ...)).

Final code:

function regex_2 {
  local regex=$1
  local text=$2
  local groups=()
  while [[ $text =~ $regex ]]; do
    groups =("${BASH_REMATCH[@]:1}")
    text=${text#*"${BASH_REMATCH[0]}"}
  done
  echo "${groups[@]}"
}

pattern="^([a-z] )\s([a-z] )"$'\n?'
text="hello world
foo bar"

res=($(regex_2 "$pattern" "$text"))
declare -p res
  • Related