Rocket
Rocket

Reputation: 1093

BASH - read lines from section of file

I have a file formatted like this:

[SITE1]
north
west

[MOTOR]
west
south
north

[AREA]
west
east
north

[CLEAR]

What I need to be able to do is read all values from a specific section.

Eg: read AREA and be returned:

west
east
north

The examples I've found online are for ini files, which have key value pairs. Can anyone help advise how I can do this ?

Thanks

Upvotes: 0

Views: 1766

Answers (4)

Reino
Reino

Reputation: 3423

Though in the first place an XML/HTML/JSON parser, you can perfectly use for raw text as well:

$ xidel -s --input-format=text "input.txt" -e '
  for $section in tokenize($raw,"\n\n")
  let $value:=x:lines($section)
  where extract($value[1],"\[(.+)\]",1) = "AREA"
  return
  $value[position() gt 1]
'
west
east
north
  • Because the input starts with a "[", Xidel assumes JSON. --input-format=text to prevent that.
  • x:lines($section), a shorthand for tokenize($section,"\r\n?|\n") and turns $section into a sequence where every new line is another item.

Upvotes: 0

Aaron
Aaron

Reputation: 24802

Using sed :

category=MOTOR; sed -nE "/^\[$category\]$/{:l n;/^(\[.*\])?$/q;p;bl}" /path/to/your/file

It doesn't do anything until it matches a line that consists of your target category, at which point it enters a loop. In this loop, it consumes a line, exits if it's an empty line or another category (or the end of the file) and otherwise prints the line.

The sed commands used are the following :

  • /pattern/ executes the next command or group of commands when the current line matches the pattern
  • {commands} regroups commands, for instance to execute them conditionally.
  • :l defines a label named "l", to which you'll be able to jump to.
  • n asks sed to start working on the next line.
  • q exits
  • p prints the current line
  • bl jumps to the "l" label

You can try it here.

Upvotes: 3

dash-o
dash-o

Reputation: 14432

The alternative is to use an external program to filter the input. This MAY provide performance advantage if the input file is VERY large, or if additional logic is needed.

function filter_section {
    local id=$1
    awk -v ID="$id" '/^\[/ { p= ($0 == "[" ID "]" ); next } p && $0 { print }' < p.txt
}

function read_section {
    local id=$1
    local match
    input=()
    while read p ; do
        # Do something with '$p'
        input+=("$p")
        echo "Item $p"
    done <<< $(filter_section "$id")
    # Indicate section not found
    [ "${#input[*]}" -gt 0 ] && return 0
    return 1
}

if read_section "AREA" < p.txt ; then
        echo "Found Area" "${#input[$@]}"
else
        echo "Missing AREA"
fi

if read_section "FOO" < p.txt ; then
        echo "Found FOO"
else
        echo "Missing FOO"
fi

Upvotes: 0

dash-o
dash-o

Reputation: 14432

Two options in mind - use a filter (e.g., awk, sed) to extract the relevant section, or use bash to filter to the specific section.

With bash, using a function:

#! /bin/bash

function read_section {
    local id=$1
    local match
    input=()
    while read p ; do
        if [ "$p" = "[$id]" ] ; then
            # Read data here
            while read p ; do
                # Check for end of section - empty line
                if [ "$p" = "" ] ; then
                    break
                fi
                # Do something with '$p'
                input+=("$p")
                echo "Item $p"
            done
            # Indicate section was found
            return 0
        fi
    done
    # Indicate section not found
    return 1
}

if read_section "AREA" < p.txt ; then
        echo "Found Area" "${#input[$@]}"
else
        echo "Missing AREA"
fi

if read_section "FOO" < p.txt ; then
        echo "Found FOO"
else
        echo "Missing FOO"
fi

Output: (placing sample input into property file p.txt)

Item west
Item east
Item north
Found Area 4
Missing FOO

Notes

  1. that it's not clear if each section ends with empty line. Code assumes that this is the case. Otherwise, the section change can be modified to if [[ "$p" = \[* ]], or similar, with extra check to ignore empty line.
  2. The function return true/false to indicate if the section was found. The script can act on this information.
  3. The loaded items are placed into the input array, for further processing

Upvotes: 2

Related Questions