Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

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) ?

share|improve this question
5  
Note that cat file | grep foo is pointless. It's known as a "useless use of cat". You can always do grep foo file instead. –  terdon Aug 22 at 11:47
    
Saying ${file} instead of just plain $file is occasionally useful, but it’s much more useful to get into the habit of saying "$file". Saying ${file} will not protect you against having space or wildcard characters in $file. –  G-Man Aug 22 at 15:36

6 Answers 6

up vote 8 down vote accepted

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
share|improve this answer

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.

share|improve this answer
    
awk is just an amazing tool –  Networker Aug 22 at 11:54
2  
@Networker yeah. It's a full programming language, there are some very very complex programs written in awk. One of the first gene prediction algorithms for example. –  terdon Aug 22 at 11:57
    
The question says cut -d'=' -f2, which yields the second =-delimited field. Your answer yields the last space-delimited field. They are the same for the sample data in the question, but it is sample data. I wonder what the real data look like. –  G-Man Aug 22 at 15:37
    
@G-Man true. I had originally written awk -F= '{print $2}' which does the same thing but I felt that $NF was simpler in this case. –  terdon Aug 22 at 22:36
    
@terdon: Gene prediction?? That's a little bit... awk –  Mehrdad Aug 22 at 23:22

and use this:

sed -n -e '/\[part1\]/,/\[part2\]/p' FILE |sed -e '1d;$d'| awk -F "=" '{print $2}'

OUTPUT is:

 a
 val1
 a
 val1
share|improve this answer

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].

share|improve this answer
    
Your last thing does not mimic the behavior of cut. If cut splits on equals and prints only the second field of the line, and - as I think you're trying to suggest - there are more than matching fields then yours old handles the problem of the last three matching fields. And this is how your command does not mimic cut. –  mikeserv Aug 22 at 19:48
    
@mikeserv: Huh? What? –  G-Man Aug 25 at 21:47
    
just what i said - It does not mimic cut because you do s/.*... Doing that means it will only get the last possible occurrence of Val[^=]*=\([^=]*\).*/\1. Seems like you worked it up a lot just for that, maybe. –  mikeserv Aug 26 at 0:24
    
@mikeserv: “just what i [sic] said”? Seriously? “… there are more than matching fields then yours old …” is one step up from gibberish – I hope there’s a typo in there, but I can’t guess what you mean. “handles the problem of the last three matching fields” is only slightly more intelligible. Do you mean that my command will fail for lines that contain “Val…=…Val…=”? If so, I guess you’re right; I didn’t consider that case. But there is nothing in your first comment suggesting that that’s what you’re talking about, and your second comment is still far from clear. –  G-Man Aug 26 at 15:23
    
@mikeserv: Remember, I said, “Note that my approach assumes that the data look at least generally like the illustration in the question.” If you’re going to claim that my solution fails on input that looks radically different from the illustration in the question, it behooves you to at least give an example of input for which my command fails. –  G-Man Aug 26 at 15:24

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}'
share|improve this answer
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 quit 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 print. 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'
share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.