Reputation: 1093
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
Reputation: 3423
Though in the first place an XML/HTML/JSON parser, you can perfectly use xidel 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
--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
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
exitsp
prints the current linebl
jumps to the "l" labelYou can try it here.
Upvotes: 3
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
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
if [[ "$p" = \[* ]]
, or similar, with extra check to ignore empty line.Upvotes: 2