3

let's present my problem to explain better. I'm using cygwin, the installation is based on a setup.ini with the following format:

    @ package-name
    sdesc: "short description, on one line"
    ldesc: "long description of arbitrary length, commonly multiple lines"
    category: categories in which the packege belonges, one line
    requires: packages (libs etc) required by this package, one line

then comes the following package, & so forth.

what I need is, given a package name output all packages required by this package (without the 'requires' prefix, if possible).

I'm sure it's basic grep, but I'm new there. thanks.

3 Answers 3

5

I am not sure how will you do it with grep, but for such tasks I prefer awk. It gives more control over what I want to do. though I am not expert in awk and still learning but here is how I would have achieved this.

PKGNAM="package-name"; awk "/$PKGNAM\$/,/requires:/ { if ( \$0 ~ /requires:/ ) { sub( /^requires:.?/, \"\"  ); print } }"

UPDATE: updated the example awk command, now it uses the PKGNAM variable to match the pacakge name.

enter image description here

HTH.

4
  • good answer, I will vote you up when I will have enough reputation (or accept your answer if it's the best after a time). just to make it clear, you have to write /package-name$/ as there are many matches between packages (like GConf2 & GConf2-devel).
    – Philomath
    Commented Jul 11, 2011 at 14:16
  • one problem though, how can I replace package-name with a variable? (I need to use it in a script), var expansion doesn't work when surrounded with single quotes, and double quotes spoil the awk script.
    – Philomath
    Commented Jul 11, 2011 at 14:38
  • See I told you there must be better ways of doing it, and Gilles already told you 4 awesome different ways of doing this. I have updated my answer too to demonstrate the use of variable name. Commented Jul 11, 2011 at 19:48
  • thanks for all who answered, I just love the simplicity of this answer. KISS!
    – Philomath
    Commented Jul 17, 2011 at 0:26
3

Grep treats all lines independently, so it can't do the job on its own.

Awk is a general text processing tool. Keep track of what the current package is (in the variable p), and output a match if a requires: line is found in the right package (removing the requires: prefix).

<setup.ini awk -vpackage='NAME_OF_PACKAGE' '
    sub(/^@ */,"") {p=$0}
    p==package && sub(/^requires: */,"") {print}
'

Another awk approach is to process input delimited by newline-@ sequences rather than newlines. Or, since package sections have a blank line between them, process input by paragraph: pass an empty string as the record separator RS (which means that records are separated by one or more blank line). Then, for each line in the sought-after record, if the line begins with requires:, print it (minus the prefix).

<setup.ini awk -vpackage='NAME_OF_PACKAGE' -vRS= -vFS='\n' '
    sub(/^@ */,"") && $1==package {
        for (i=2; i<NF; i++) {if (sub(/^requires: */,"",$i)) print $i}
    }'

Another possibility is Perl's paragraph mode (-00). If the paragraph starts with the right header (/REGEXP/m means a multiline match, so that the $ anchor means end-of-line rather than end-of-string), and it contains a requires: line, then print that line (minus the prefix).

<setup.ini package=NAME_OF_PACKAGE perl -00 -ne '
    /\A@ *$ENV{package}$/m and /^requires: *(.*)$/m and print "$1\n"'

And here's one for the (GNU) sed lovers . (You are not expected to understand this.)

sed -ne '/^@/ { h; b; }; G; s/^requires: *\(.*\)\n@ *NAME_OF_PACKAGE$/\1/p'
3
  • wow! that's magic! I definitely have much to learn. just curios, which of them is expected to be the fastest?, we are speaking about thousands of packages.
    – Philomath
    Commented Jul 11, 2011 at 15:05
  • 1
    @Pilomath The difference in speed will be negligible. Thousands is not much for a computer. However, if you were going to do this for every package, it would be a lot faster to handle them all at once: put the reading and the logic inside the same program (Perl or Python would be more appropriate than the shell). Commented Jul 11, 2011 at 15:30
  • of course, but I'm Just Another non - {Perl, Python} Hacker.
    – Philomath
    Commented Jul 12, 2011 at 3:52
1

Here's a sed solution that can do it without a back reference.

# cf. "3.3. Addressing and address ranges",
# http://sed.sourceforge.net/sedfaq3.html#s3.3 (esp. (6) Relentless. ...)

PKGNAME="package-name"
sed -ne '/^ *@ *'"${PKGNAME}"'/{
:a
N
/\n *requires: /!ba
s/.*\n *requires: *//
p
}' setup.ini 

Gilles' (GNU) sed solutions works on Mac OS X 10.6.7 (using FreeBSD sed) if the b command is put on a separate line (or at least followed by a line break).

PKGNAME="package-name"
sed -ne '/^ *@ *'"${PKGNAME}"'/{h
b
};G;s/^ *requires: *\(.*\)\n *@ *'"${PKGNAME}"' *$/\1/p' setup.ini
1
  • Good answer, just replace ${PKGNAME} with ${PKGNAME}\$ to make sure GConf2 doesn't match GConf2-devel.
    – Philomath
    Commented Jul 13, 2011 at 0:26

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .