4

Anyone have idea how to turn this:

FX_AM140_EML AM140
Backend/aa.java
Backend/bb.java
Backend/cc.java
Backend/dd.java
Backend/ee.java

FX_AM172_EML AM172
Backend/aa.java
Backend/bb.java

Into this?

FX_AM140_EML AM140 Backend/aa.java
FX_AM140_EML AM140 Backend/bb.java
FX_AM140_EML AM140 Backend/cc.java
FX_AM140_EML AM140 Backend/dd.java
FX_AM140_EML AM140 Backend/ee.java
FX_AM172_EML AM172 Backend/aa.java
FX_AM172_EML AM172 Backend/bb.java

7 Answers 7

6

Something like withawk can do the work:

awk '/^FX/ {a=$0;next} /^Backend/ {print a,$0}' <input file>

Here I assume the head line start with FX and other start with Backend

6

Another awk approach which considers the first line in a group of lines delimited by empty lines as the prefix:

awk '
  $0 == ""     {prefix = ""; next}
  prefix == "" {prefix = $0; next}
               {print prefix, $0}'
3
  • The above answer can be as awk '!NF{getline;f=$0;next}{print f,$0}'. Is it correct? Dec 4, 2023 at 14:55
  • 3
    @PrabhjotSingh, !NF is not the same as $0 == "" as it also returns true for non-empty blank lines. Your approach wouldn't work correctly if there was an even number of blank lines between groups. Dec 4, 2023 at 15:57
  • @PrabhjotSingh no, f wouldn't be populated for the first block of lines (you'd need to add NR==1{f=$0} at the start or handle it otherwise) and getline itself comes with some baggage to handle, see awk.freeshell.org/AllAboutGetline.
    – Ed Morton
    Dec 6, 2023 at 0:40
3

A Perl suggestion. The driver for this variant is that groups of lines are separated by a blank line:

perl -ne '
    BEGIN {$/ = "\n\n"}
    ($p, @a) = split("\n");
    print "$p $_\n" foreach @a;
' datafile

It's more readable as several lines but it could be crunched into a single line (I've swapped the BEGIN clause for -00, which also switches on paragraph mode):

perl -00 -ne '($p, @a)=split("\n"); print "$p $_\n" foreach @a' datafile

The awk equivalent is this (which can also be crunched to a single line by simple concatenation):

awk -v RS= '
    {
        split($0, a, "\n");
        p=a[1]; delete a[1];
        for (f in a) {
            print p, a[f]
        }
    }
' datafile

Setting the record separator (RS) to an empty string switches on paragraph mode (POSIX, and search for "If RS is null")

Output

FX_AM140_EML AM140 Backend/aa.java
FX_AM140_EML AM140 Backend/bb.java
FX_AM140_EML AM140 Backend/cc.java
FX_AM140_EML AM140 Backend/dd.java
FX_AM140_EML AM140 Backend/ee.java
FX_AM172_EML AM172 Backend/aa.java
FX_AM172_EML AM172 Backend/bb.java
4
  • Nice one Chris. But I get an additional last line as FX_AM172_EML AM172 Dec 4, 2023 at 11:52
  • @elmo mmm, I don't see why awk should create a trailing blank line, but it does. It's fixed now - blank lines are ignored Dec 4, 2023 at 12:31
  • You can make the one-liner even shorter with perl -00 -ne '($p, @a) = split("\n"); print "$p $_\n" for @a;' file.
    – terdon
    Dec 4, 2023 at 16:10
  • @terdon not intentionally playing golf. Well, not much :-) Dec 4, 2023 at 16:23
2

You could try awk. Using your sample input as file q763021:

$ awk 'NF<1{next}/FX_AM/{a=$0;next}{print a,$0}' q763021 
FX_AM140_EML AM140 Backend/aa.java
FX_AM140_EML AM140 Backend/bb.java
FX_AM140_EML AM140 Backend/cc.java
FX_AM140_EML AM140 Backend/dd.java
FX_AM140_EML AM140 Backend/ee.java
FX_AM172_EML AM172 Backend/aa.java
FX_AM172_EML AM172 Backend/bb.java
2

A Perl approach which uses any line that does not contain / as the prefix for the others:

$ perl -lne 'if(/^(.[^\/]+)$/ || /^$/){ $k=$1; next} print "$k $_"' file
FX_AM140_EML AM140 Backend/aa.java
FX_AM140_EML AM140 Backend/bb.java
FX_AM140_EML AM140 Backend/cc.java
FX_AM140_EML AM140 Backend/dd.java
FX_AM140_EML AM140 Backend/ee.java
FX_AM172_EML AM172 Backend/aa.java
FX_AM172_EML AM172 Backend/bb.java
2

Using any awk:

$ awk '/\//{print h, $0; next} {h=$0}' file
FX_AM140_EML AM140 Backend/aa.java
FX_AM140_EML AM140 Backend/bb.java
FX_AM140_EML AM140 Backend/cc.java
FX_AM140_EML AM140 Backend/dd.java
FX_AM140_EML AM140 Backend/ee.java
FX_AM172_EML AM172 Backend/aa.java
FX_AM172_EML AM172 Backend/bb.java

or if the header lines sometimes have /s or the data lines sometimes don't:

$ awk -v RS= -F'\n' '{for (i=2; i<=NF; i++) print $1, $2}' file
FX_AM140_EML AM140 Backend/aa.java
FX_AM140_EML AM140 Backend/aa.java
FX_AM140_EML AM140 Backend/aa.java
FX_AM140_EML AM140 Backend/aa.java
FX_AM140_EML AM140 Backend/aa.java
FX_AM172_EML AM172 Backend/aa.java
FX_AM172_EML AM172 Backend/aa.java
0

Using Raku (formerly known as Perl_6)

~$ raku -e 'my @a = slurp.trim.split("\n\n").map: *.lines; for [email protected] {  \
                    put join " ", @a[$i][0], $_ for @a[$i][1..*] }'  file

OR

~$ raku -e 'my @a = slurp.trim.split("\n\n").map: *.lines;           \
                    for [email protected] { for @a[$i][1..*] -> $tail {    \
                    put join " ", @a[$i][0], $tail }}'  file

Above are comparatively verbose answers written in Raku, a member of the Perl-family of programming languages. The code can be used to take a record 'header' and use it as a prefix which gets added to following 'body' lines.

(The slurp function reads the file in all-at-once, so may not be appropriate for extremely large files. The trim function removes leading and trailing whitespace, so may be omitted for well-behaved logfiles).

Output:

FX_AM140_EML AM140 Backend/aa.java
FX_AM140_EML AM140 Backend/bb.java
FX_AM140_EML AM140 Backend/cc.java
FX_AM140_EML AM140 Backend/dd.java
FX_AM140_EML AM140 Backend/ee.java
FX_AM172_EML AM172 Backend/aa.java
FX_AM172_EML AM172 Backend/bb.java

https://raku.org

You must log in to answer this question.

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