From: John W. Krahn on
Ed Morton wrote:
> On 2/25/2010 3:36 PM, John W. Krahn wrote:
>> Harry wrote:
>>> I have about 30 text files containing some MQ object definitions.
>>> I want to locate some CHANNEL definitions with a pattern
>>> "SSLCAUTH(REQUIRED)".
>>
>> ..." | perl -ne'$_ .= <> and redo if s/\+$//; print grep /DEFINE
>> CHANNEL|SSLCAUTH\(REQUIRED\)|SSLCIPH/, /.*\n/g'
>> DEFINE CHANNEL ('AAA.SVRCONN') CHLTYPE(SVRCONN)
>> SSLCAUTH(REQUIRED)
>> SSLCIPH(' ')
>
> Please don't take this as a knock against perl as it's not intended that
> way, I'm just genuinely curious. Maybe it's just me but I find the
> syntax of:
>
> perl -ne'$_ .= <> and redo if s/\+$//; print grep /DEFINE
> CHANNEL|SSLCAUTH\(REQUIRED\)|SSLCIPH/, /.*\n/g'
>
> very unintuitive compared to this awk syntax to produce the same output:
>
> awk 'BEGIN{RS="";FS="\n"}
> /SSLCAUTH\(REQUIRED\)/{
> for (i=1;i<=NF;i++)
> if ($i ~ /DEFINE CHANNEL|SSLCAUTH|SSLCIPH/)
> print $i
> }'

The perl version of that would be:

perl -F'\n' -00 -ane'
if ( /SSLCAUTH\(REQUIRED\)/ ) {
for ( @F ) {
if ( /DEFINE CHANNEL|SSLCAUTH|SSLCIPH/ ) { print "$_\n" }
}
}'


> Is the perl syntax you used above just a choice you made for the sake of
> brevity

Sort of. The algorithm is different. The perl script above
concatenates together all the lines that end with a '+' and then loops
through that one string using grep() to pick out the correct "lines".


> and in reality it's possible to do the job in perl using a
> syntax that's similar to the awk one? If so, what would that look like?
>
> I suppose I could do something similar to the perl syntax above in GNU
> awk like:
>
> gawk -v RS= '$0!=($0=gensub(/(DEFINE
> CHANNEL[^\n]*).*(SSLCAUTH\(REQUIRED\)).*(SSLCIPH[^\n]*).*/,"\\1\n\\2\n\\3\n",""))'
>
>
> but in reality I wouldn't for the sake of clarity and portability to
> other awks.



John
--
The programmer is fighting against the two most
destructive forces in the universe: entropy and
human stupidity. -- Damian Conway
From: Harry on
On Feb 26, 5:49 am, Ed Morton <mortons...(a)gmail.com> wrote:
> On 2/25/2010 10:04 PM, Harry331 wrote:
>
> > Harry wrote...
> <snip>
> >> How can I display the file names as well ?
> <snip>
> > Here is my latest revision.
>
> > find . -type f -exec echo \; -exec echo {} \; -exec
> > awk 'BEGIN{RS="";FS="\n"} /SSLCAUTH\(REQUIRED\)/ { for
> > (i=1;i<=NF;i++) if ($i ~ /DEFINE|SSLCAUTH|SSLCIPH/) print
> > $i}' {} \;
>
> In awk the "FILENAME" variable contains (surprise!) the file name so depending
> on how you want your output to be formatted you could do something like either
> of these:
>
> awk 'BEGIN{RS="";FS="\n"}
>      /SSLCAUTH\(REQUIRED\)/ {
>          for (i=1;i<=NF;i++)
>              if ($i ~ /DEFINE|SSLCAUTH|SSLCIPH/)
>                  print FILENAME,$i
>          print ""
>      }'
>
> awk 'BEGIN{RS="";FS="\n"}
>      /SSLCAUTH\(REQUIRED\)/ {
>          print FILENAME
>          for (i=1;i<=NF;i++)
>              if ($i ~ /DEFINE|SSLCAUTH|SSLCIPH/)
>                  print $i
>          print ""
>      }'
>
> I also threw in an extra print "" to give you a blank line between records in
> case that's useful for separating them.
>
> Regards,
>
>         Ed.

Perfect!

Thanks a lot, Ed.

From: Ed Morton on
On 2/26/2010 10:50 AM, John W. Krahn wrote:
> Ed Morton wrote:
>> On 2/25/2010 3:36 PM, John W. Krahn wrote:
>>> Harry wrote:
>>>> I have about 30 text files containing some MQ object definitions.
>>>> I want to locate some CHANNEL definitions with a pattern
>>>> "SSLCAUTH(REQUIRED)".
>>>
>>> ..." | perl -ne'$_ .= <> and redo if s/\+$//; print grep /DEFINE
>>> CHANNEL|SSLCAUTH\(REQUIRED\)|SSLCIPH/, /.*\n/g'
>>> DEFINE CHANNEL ('AAA.SVRCONN') CHLTYPE(SVRCONN)
>>> SSLCAUTH(REQUIRED)
>>> SSLCIPH(' ')
>>
>> Please don't take this as a knock against perl as it's not intended
>> that way, I'm just genuinely curious. Maybe it's just me but I find
>> the syntax of:
>>
>> perl -ne'$_ .= <> and redo if s/\+$//; print grep /DEFINE
>> CHANNEL|SSLCAUTH\(REQUIRED\)|SSLCIPH/, /.*\n/g'
>>
>> very unintuitive compared to this awk syntax to produce the same output:
>>
>> awk 'BEGIN{RS="";FS="\n"}
>> /SSLCAUTH\(REQUIRED\)/{
>> for (i=1;i<=NF;i++)
>> if ($i ~ /DEFINE CHANNEL|SSLCAUTH|SSLCIPH/)
>> print $i
>> }'
>
> The perl version of that would be:
>
> perl -F'\n' -00 -ane'
> if ( /SSLCAUTH\(REQUIRED\)/ ) {
> for ( @F ) {
> if ( /DEFINE CHANNEL|SSLCAUTH|SSLCIPH/ ) { print "$_\n" }
> }
> }'

Got it. Thanks!

>
>> Is the perl syntax you used above just a choice you made for the sake
>> of brevity
>
> Sort of. The algorithm is different. The perl script above concatenates
> together all the lines that end with a '+' and then loops through that
> one string using grep() to pick out the correct "lines".

OK, I get that concept, but I don't understand it knows when it finds a "DEFINE
CHANNEL" line in the concatenated string that it will later find a
"SSLCAUTH\(REQUIRED\)" string on a subsequent line so it's OK to print that
first line.

Ed.
>
>
>> and in reality it's possible to do the job in perl using a syntax
>> that's similar to the awk one? If so, what would that look like?
>>
>> I suppose I could do something similar to the perl syntax above in GNU
>> awk like:
>>
>> gawk -v RS= '$0!=($0=gensub(/(DEFINE
>> CHANNEL[^\n]*).*(SSLCAUTH\(REQUIRED\)).*(SSLCIPH[^\n]*).*/,"\\1\n\\2\n\\3\n",""))'
>>
>>
>> but in reality I wouldn't for the sake of clarity and portability to
>> other awks.
>
>
>
> John

From: pk on
Ed Morton wrote:

>> Sort of. The algorithm is different. The perl script above concatenates
>> together all the lines that end with a '+' and then loops through that
>> one string using grep() to pick out the correct "lines".
>
> OK, I get that concept, but I don't understand it knows when it finds a
> "DEFINE CHANNEL" line in the concatenated string that it will later find a
> "SSLCAUTH\(REQUIRED\)" string on a subsequent line so it's OK to print
> that first line.

I'm sure he'll be able to explain much better...anyway (slightly
reformatted):

-------------------
perl -ne

This (-n) enables an implicit loop over the input, ie the same that happens
in awk automatically. By default, records end at \n, like in awk. "$_" is
like awk's "$0", ie it contains the record (plus a ton of other things, but
here that's what it does). One difference is that the trailing \n is NOT
removed so it's part of "$_".
-------------------
$_ .= <> and redo if s/\+$//;

(Note the "quasi natural language" syntax of this Perl code)
This concatenates successive input lines as long as the resulting string
ends in "+". <> is (VERY roughly) like awk's getline here. "." is the
concatenation operator, s/// is like sed's substitution and by default it
operates on "$_" (and the result is true if the replacement is performed),
so the above code is similar (not completely equivalent) to this awk code:

while(sub(/\+$/,"")) { getline a; $0=$0 "\n" a; }; $0=$0 "\n"

Note that, even if trailing newline is preserved in Perl, /\+$/ matches what
one expects.
-------------------

print grep /DEFINE CHANNEL|SSLCAUTH\(REQUIRED\)|SSLCIPH/, /.*\n/g'

This is a very idiomatic construct. We should start from the end.

/.*\n/g

/pattern/ is Perl's match operator, and by default it matches against "$_".
The /g modifier performs a global match, ie match as many times as possible.
Believe it or not, due to Perl rules the result of the above "/.*\n/g" is an
array containing all the "lines" in "$_". That would be like awk's

n = split($0, lines, /\n/)

except again each array element does end in "\n" in the Perl version.

The Perl array is unnamed, and "grep" operates on that array.
The syntax used for grep here is

grep EXPRESSION,LIST

where the unnamed array is the LIST part in the above synopsis.
What grep does is to apply the EXPRESSION to each element of LIST, and
return the elements for which EXPRESSION is true.

EXPRESSION here is

/DEFINE CHANNEL|SSLCAUTH\(REQUIRED\)|SSLCIPH/

so it is a match against the enclosed pattern. So, each line in the unnamed
array is matched against that pattern, and if it matches grep returns it.

In awk it would be something like:

count=0
for(i=1;i<=n;i++){
if(lines[i] ~ /DEFINE CHANNEL|SSLCAUTH\(REQUIRED\)|SSLCIPH/) {
count++
matchinglines[count] = lines[i]
}
}

with again the difference that in awk you have to explicitly name the
arrays.

The result of grep is again a (possibly smaller) unnamed array, which is fed
to "print" to be printed. Since each element already has its original
trailing newline, it will be printed in a separate line. To complete, in awk
that would be

for(i=1;i<=count;i++){
print matchinglines[i]
}

So the complete awk code would be (untested!)

awk '{
while(sub(/\+$/,"")) { getline a; $0=$0 "\n" a; }; $0=$0 "\n"
n = split($0, lines, /\n/)
count=0
for(i=1;i<=n;i++){
if(lines[i] ~ /DEFINE CHANNEL|SSLCAUTH\(REQUIRED\)|SSLCIPH/) {
count++
matchinglines[count] = lines[i]
}
}
for(i=1;i<=count;i++){
print matchinglines[i]
}
}'

From: pk on
pk wrote:

> perl -ne
>[cut]
> The Perl array is unnamed

I guess that for true Perl correctness, all those object I called "unnamed
arrays" are better called "lists". Sorry for the imprecision.