This seems like a simple task, but using duckduckgo I wasn\'t able to find a way to properly do what I\'m trying to.
The main question is: How do I split the output of a
The question is tagged as Perl so here is a possible Perl answer:
#!/usr/bin/env perl
use strict;
use warnings;
my $is_col1 = 1;
my $in_block = 0;
my @col1;
while (<DATA>) {
chomp;
if (/^\s*-+\s*$/ ... /^\s*-+\s*$/) {
$in_block = 1;
if ($is_col1) {
push @col1, $_;
}
else {
printf "%-40s%-40s\n", shift @col1 // '', $_;
}
}
else {
if ($in_block) {
$in_block = ! $in_block;
$is_col1 = ! $is_col1;
print "\n" if $is_col1; # line separating blocks
}
}
}
print join("\n", @col1), "\n\n" if @col1;
__DATA__
-----------------------------------
Some data
that varies in line length
-----------------------------------
-----------------------------------
More data that is seperated
by a new line and dashes
with a longer column2
-----------------------------------
-----------------------------------
The odd last column
-----------------------------------
Output:
----------------------------------- -----------------------------------
Some data More data that is seperated
that varies in line length by a new line and dashes
----------------------------------- with a longer column2
-----------------------------------
-----------------------------------
The odd last column
-----------------------------------
Alright, since apprently there is no clean way to do this I came up with my own solution. It's a bit messy and requires GNU screen
to be installed, but it works. Any amount of lines within or around the blocks, 50% of the screen automatically resizing and each column prints independantly from each other with a fixed amount of newlines between them. Also automatic updates every x seconds. (120 in my example)
#!/bin/bash
screen -S testscr -X layout save default
screen -S testscr -X split -v
screen -S testscr -X screen tail -f /tmp/testscr1.txt
screen -S testscr -X focus
screen -S testscr -X screen tail -f /tmp/testscr2.txt
while : ; do
echo "" > /tmp/testscr1.txt
echo "" > /tmp/testscr2.txt
cfile=1 # current column
ctype=0 # start or end of block
while read; do
if [[ $REPLY == "------------------------------------------------------------" ]]; then
if [[ $ctype -eq 0 ]]; then
ctype=1
else
if [[ $cfile -eq 1 ]]; then
echo "${REPLY}" >> /tmp/testscr1.txt
echo "" >> /tmp/testscr1.txt
echo "" >> /tmp/testscr1.txt
cfile=2
else
echo "${REPLY}" >> /tmp/testscr2.txt
echo "" >> /tmp/testscr2.txt
echo "" >> /tmp/testscr2.txt
cfile=1
fi
ctype=0
fi
fi
if [[ $ctype -eq 1 ]]; then
if [[ $cfile -eq 1 ]]; then
echo "${REPLY}" >> /tmp/testscr1.txt
else
echo "${REPLY}" >> /tmp/testscr2.txt
fi
fi
done < "$1"
sleep 120
done
First, start a screen session with screen -S testscr
then, either within or outside the session, execute the script above. This will split the screen vertically using 50% per column and execute tail -f
on both columns, afterwards it will go through the input file and write block by block to each tmp. file in the desired way. Since it's in an infinite while loop it's essentially automatically updating the shown output every x seconds (here 120
).
Assuming file contains uniform blocks of five lines each, using paste
, sed
, and printf
:
c=$((COLUMNS/2))
paste -d'#' <(sed -n 'p;n;p;n;p;n;p;n;p;n;n;n;n;n' file) \
<(sed -n 'n;n;n;n;n;p;n;p;n;p;n;p;n;p' file) |
sed 's/.*/"&"/;s/#/" "/' |
xargs -L 1 printf "%-${c}s %-${c}s\n"
Problem with OP spec
The OP reports that the block lengths may vary, and should be separated by a fixed number of lines. Even numbered blocks go in Column A, odd numbered blocks in Column B.
That creates a tail -f
problem then. Suppose the block lengths of the source input begin with 1000 lines, then one line, 1000, 1, 1000, 1, etc. So Column A gets all the 1000 line blocks, and Column B gets all the one line blocks. Let's say the blocks in the output are separated by 1 line each. So one block in Column A lines up with 500 blocks in Column B. So for a terminal with scrolling output, that means before we can output the first block in Column A, we have to wait for 1000 blocks of input. To output the third block in Column A, (just below the first block), we have to wait for 2000 blocks of input.
If the blocks are added to the input file relatively slowly, with a one second delay between blocks, then it will take three seconds for the block 3 to appear in the input file, but it will take 33 minutes for block 3 to be displayed in the output file.
This script is getting max width of current terminal and splitting it in 2, then printing records split by RS="\n\n" separator, print the first found and placing the cursor at the first line/last column of it to write the next record.
#!/bin/bash
tput clear
# get half current terminal width
twidth=$(($(tput cols)/2))
tail -n 100 -f test.txt | stdbuf -i0 -o0 gawk -v twidth=$twidth 'BEGIN{ RS="\n\n"; FS=OFS="\n"; oldNF=0 } {
sep="-----------------------------------"
pad=" "
printf "%-" twidth "s", $0
getline
for(i = 1; i <= NF; i++){
# move cursor to first line, last column of previous record
print "\033[" oldNF ";" twidth "f" $i
oldNF+=1
}
}'
Here's a simpler version
gawk 'BEGIN{ RS="[-]+\n\n"; FS="\n" } {
sep="-----------------------------------"
le=$2
lo=$3
getline
printf "%-40s %-40s\n", sep,sep
printf "%-40s %-40s\n", le,$2
printf "%-40s %-40s\n", lo,$3
printf "%-40s %-40s\n\n", sep,sep
}' test.txt
Output
----------------------------------- -----------------------------------
Some data More data that is seperated
that varies in line length by a new line and dashes
----------------------------------- -----------------------------------
----------------------------------- -----------------------------------
Some data More data that is seperated
that varies in line length by a new line and dashes
----------------------------------- -----------------------------------