Obtain values of a section of a file
I have a configuration file in the following format.
<Title>
[part1]
A.File = a
A.Val = val1
B.File = a
B.Val = val1
[part2]
A.File = a1
A.Val = val2
B.File = a
B.Val = val1
I want to extract values from first part only.
#!/bin/sh
getCalibDate()
{
file="/path/of/config/file"
value=`cat ${file} | grep Val | cut -d'=' -f2`
for v in $value
do
echo $v
done
}
getCalibDate
Above script will return all the values.
How can I get values from only first part (part1) ?
If you have only 4 lines after [part1]
you can use -A4
option with grep
:
cat ${file} | grep -A4 "part1" | cut -d'=' -f2`
For general case (more than 4 lines after [part1]) use sed
to get the text between two parts:
cat ${file} | sed -n "/part1/,/part2/p" | head -n-1
head
is to delete additional part2
at the end
As terdon said you don’t have to use cat
, you can do the following instead:
grep -A4 "part1" ${file} | cut -d'=' -f2`
OR:
sed -n "/part1/,/part2/p" ${file} | head -n-1
You need to use a more sophisticated tool to parse the file. For example, awk
:
#!/bin/sh
getCalibDate()
{
file="${1}"
value=$(awk '/[part/{a++}(a<2 && /Val/){print $NF}' ${file})
for v in $value
do
echo $v
done
}
getCalibDate ${1}
Here, the variable a
is incremented each time a line matches [part
. Then, the last field ($NF
) is printed when a line matches Val
but only if a
is less than 2, only if we’re in the 1st section.
and use this:
sed -n -e '/[part1]/,/[part2]/p' FILE |sed -e '1d;$d'| awk -F "=" '{print $2}'
OUTPUT is:
a
val1
a
val1
sed -n '/part2/q;s/[^=]*=//p'
<<DATA
<Title>
[part1]
A.File = a
A.Val = val1
B.File = a
B.Val = val1
[part2]
A.File = a1
A.Val = val2
B.File = a
B.Val = val1
DATA
OUTPUT
a
val1
a
val1
That should do the trick. It will immediately q
uit the input file the first time it encounters the string part2 anywhere in the input. This means that it never even attempts to read through the parts of the file you don’t want – which should make it very efficient.
The -n
disables auto printing so sed
only prints what it is definitely told to p
rint. The only time it is ever definitely told to print is when it can successfully remove a sequence of 0 or more characters that are not =
and one character that is an =
.
If you wanted to print the whole matched line instead you could do:
sed -n '/part2/q;/=/p'
To get the whole lines from the first part:
awk '$1 ~ /^[/ {n++;next} n==1'
To just print the right hand side of the first =
:
awk '$1 ~ /^[/ {n++;next} n==1 {sub(/^[^=]*=[[:blank:]]*/,""); print}'
There are some good answers here,
but I see only one that incorporates the Val
part of the problem,
and it’s ambiguous whether that is correct.
I agree that awk
is “an amazing tool”, but it’s not necessary here;
I believe that this sed
command:
sed -n '/[part1]/,/[part2]/s/.*Val.*=//p' "$file"
probably does what’s desired.
Like the other sed -e '/[part1]/,/[part2]/p'
solutions
(Networker
and Babyy),
this is trivially adaptable to select any section.
(You do, of course, need to know its name; if you know only its ordinal number,
you can adapt terdon’s answer
or glenn jackman’s answer,
both of which count sections rather than looking for a specific name.)
If you don’t know the name of section following, you can do
sed -n '/[part42]/,/[part/s…' "$file"
for example.
My only meta-question is regarding the cut -d'=' -f2
part of the question.
If an input line that we’re extracting data from contains multiple =
characters
after Val
(i.e., the field value contains =
character(s)), e.g.,
Einstein.Val = E=mc^2
then the above cut
command will extract only the text between the first and second =
(i.e., the field value, up to (but not including) the first =
), e.g., E
.
The sed
command I have presented above will extract only the text
after the last =
(e.g., mc^2
).
To get everything after the first =
(e.g., E=mc^2
), use
sed -n '/[part1]/,/[part2]/s/.*Val[^=]*=//p' "$file"
To mimic the behavior of the cut
(e.g., E
), use
sed -n '/[part1]/,/[part2]/s/.*Val[^=]*=([^=]*).*/1/p' "$file"
Note that my approach assumes that the data look
at least generally like the illustration in the question;
i.e., at least one =
appears somewhere to the right of the Val
string.
Accordingly, all of my solutions will ignore input like
Girl.Name = Valerie
Valerie Bertinelli
even if it falls between [part1]
and [part2]
.