What is the correct onliner for a column filter expression using Perl?

Viewed 121

I try to filter expression from a configuration file under Linux mixing bash command line tools and Perl commands in a pipe.

I have a configuration file (see section configuration) and can filter the relevant lines by using `

grep PG.DATABASE database.conf | \
sed -r -e 's/^\s*//;s/\s+/ /'  | \
cut -d ' ' -f2 | \
sort

And get the result as expected:

DB.GRM.CON.LOCAL.V01
DB.GRM.CON.LOCAL.V02
DB.GRM.CON.LOCAL.V03

Now I want to switch to Perl

grep PG.DATABASE database.conf | \
perl -lpe 's/^\s*//; @m = split /\s+/; print $m[1]'  

but I get strange results with a duplication of the input line.

DB.GRM.CON.LOCAL.V01
PG.DATABASE: DB.GRM.CON.LOCAL.V01 BEGIN
DB.GRM.CON.LOCAL.V02
PG.DATABASE: DB.GRM.CON.LOCAL.V02 BEGIN
DB.GRM.CON.LOCAL.V03
PG.DATABASE: DB.GRM.CON.LOCAL.V03 BEGIN

Question

Why is the duplication there and is the correct Perl oneliner for result achieved with the command line tools?

Configuration File

PG.CONFIG: DEFAULT BEGIN

   # Green Rebel Database via PG.SERVICE ------------------------
   PG.DATABASE: DB.GRM.CON.LOCAL.V01 BEGIN
     SERVICE:     'LOC-GRM-V0'
   END.DB.GRM.CON.LOCAL.V01

   # Green Rebel Database via DBI Driver ------------------------
   PG.DATABASE: DB.GRM.CON.LOCAL.V02 BEGIN
     DBI.PG: "dbi:Pg:dbname=grm;host=localhost;port=5432"
     AUTO.COMMIT: FALSE
     RAISE.ERROR: TRUE
     PRINT.ERROR: FALSE
   END.DB.GRM.CON.LOCAL.V02

   # Green Rebel Database via Host, Db, User, Pass --------------
   PG.DATABASE: DB.GRM.CON.LOCAL.V03 BEGIN
     SERVER:     'localhost'
     PORT:        5432
     DATABASE:    'grm'
     USER:        ${/SYSTEM/USER}
     SSL.MODE:    allow
     AUTO.COMMIT: FALSE
     RAISE.ERROR: TRUE
     PRINT.ERROR: FALSE
   END.DB.GRM.CON.LOCAL.V03
END.DEFAULT

3 Answers

I suggest changing the -p option (which makes it automatically print every line) to -n. You can also skip the grep and let perl do it:

perl -lne 'if(/PG\.DATABASE/) {chomp; s/^\s*//; @m = split /\s+/; print $m[1]}' database.conf

A simplification could be:

perl -lne 'print $1 if(/PG\.DATABASE:\s(\S+)/)'  database.conf

You can also let perl do the splitting into columns automatically, that might be the correct way to filter columns in perl:

perl -lane 'print $F[1] if $F[0] eq "PG.DATABASE:"' database.conf
DB.GRM.CON.LOCAL.V01
DB.GRM.CON.LOCAL.V02
DB.GRM.CON.LOCAL.V03

But your format isn't really column oriented so I would go with a regex capture solution like in the other answers:

perl -lne 'print $1 if(/^\s*PG\.DATABASE:\s*(\S+)/)' database.conf

I would do:

perl -nE 'say $1 if /^\s+PG\.DATABASE:\s+(\S+)/' file

Which also works in GNU grep:

grep -oP '^\s+PG\.DATABASE:\s+\K\S+' file

And GNU sed:

sed -nE 's/^\s+PG\.DATABASE:\s+(\S+).*/\1/p' file

Or POSIX sed:

sed -nE 's/^[[:blank:]]*PG\.DATABASE:[[:blank:]]*([^[:blank:]]*).*/\1/p' file
Related