How can I split a long text into array based on empty lines?

Viewed 71

I have a text file with following content:

...
LogLevelMax=-1
Id=keyboard-setup.service

LogLevelMax=-1
Id= networkd-dispatcher.service

LogLevelMax=-1
Id=systemd-remote-fs.service

LogLevelMax=-1
Id=systemd-journal-flush.service

LogLevelMax=-1
Id=some-other.service
...

I want to save them into an associative array, being key 'Id', value 'LogLevelMax'. Between each "entity" there are exactly 2 new lines. Between LogLevelMax and Id there is exactly one new line.

First, I try to replace 2 empty lines with a character '#': cat file.txt | tr "\n\n" "#". But it replaces all new lines with '#', not only exactly 2 new lines.

How can I do it in bash with sed, awk, regex or bash functions? Thanks.

3 Answers

With awk:

parse.awk

BEGIN {
  RS=""
  FS=" *[\n=] *"
}

# Copy references into the h associative array
{ h[$4] = $2 }

# Print collected key/value pairs
END { 
  for (k in h) 
    print k " -> " h[k]
}

Run it e.g. like this:

awk -f parse.awk infile | column -t

Output:

networkd-dispatcher.service    ->  -1
keyboard-setup.service         ->  -1
systemd-remote-fs.service      ->  -1
systemd-journal-flush.service  ->  -1
some-other.service             ->  -1

With bash:

declare -A array
while IFS='=' read -r a b; do
  if [[ "$a" == "Id" ]]; then
    array+=(["$b"]="$c")
  fi
  c="$b"
done < file

And then:

$ for k in "${!array[@]}"; do printf '%s : %s\n' "$k" "${array[$k]}"; done
systemd-journal-flush.service : -1
keyboard-setup.service : -1
systemd-remote-fs.service : -1
networkd-dispatcher.service : -1
some-other.service : -1

I would use awk and Bash this way:

declare -A aarr

while read -r key val; do
    aarr["$key"]="$val"
done < <(awk '{print $4, $2}' RS='\n\n' FS="[[:space:]]*[=\n][[:space:]]*" file)

Result:

$ declare -p aarr
declare -A aarr=([systemd-journal-flush.service]="-1" [keyboard-setup.service]="-1" [systemd-remote-fs.service]="-1" [networkd-dispatcher.service]="-1" [some-other.service]="-1" )

If it is possible that there are white spaces in the fields, you can do this instead:

while IFS=# read -r key val; do
    aarr["$key"]="$val"
done < <(awk '{print $4 "#" $2}' RS= FS="[[:space:]]*[=\n][[:space:]]*" file)

Where # is a delimiter that is not in your fields.

Related