根据分隔符在多列中打印文件

时间:2018-04-03 00:49:22

标签: linux bash perl file unix

这看起来像一个简单的任务,但是使用duckduckgo我无法找到正确做我正在尝试的方法。

主要问题是:如何使用分隔符将linux或bash中的命令输出拆分为多个列?

我有一个看起来像这样的文件:(这只是一个简化的例子)

-----------------------------------
Some data
that varies in line length
-----------------------------------

-----------------------------------
More data that is seperated
by a new line and dashes
-----------------------------------

等等。每次将数据写入文件时,它都会包含在一行破折号中,并由最后一个块的空行分隔。数据的行长度各不相同。我想要的基本上是使用bash将文件拆分成多个列的工具或方式:

-----------------------------------        -----------------------------------
Some data                                  More data that is seperated
that varies in line length                 by a new line and dashes
-----------------------------------        -----------------------------------

每列应占据屏幕的50%,不需要居中(如对齐)。文件 要拆分每块。在中间拆分文件或类似的东西是行不通的。我基本上希望块1转到左列,块2转到右边,3转到左边,4转到右边,依此类推。文件不断更新,应立即将更新写入屏幕。 (目前我正在使用tail -f

因为这听起来像是一个相当普遍的问题,我会欢迎一般的方法,而不是一个只适用于我的情况的特定答案,所以来自搜索引擎寻找方法在bash中使用两列布局的人得到一些信息也是。我尝试了columnpr,两者都无法正常工作。 (我在评论中对此进行了详细阐述)

编辑:要明确,我正在寻找一般方法。浏览文件,在分隔符之间获取数据,将其放到A列,将下一个数据放到B列,依此类推。

4 个答案:

答案 0 :(得分:2)

这个问题被标记为Perl,所以这里有一个可能的Perl答案:

#!/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
-----------------------------------

输出:

-----------------------------------     -----------------------------------
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
-----------------------------------

答案 1 :(得分:1)

此脚本获取当前终端的最大宽度并将其拆分为2,然后打印由RS =&#34; \ n \ n&#34;分隔符,打印找到的第一个并将光标放在它的第一行/最后一列以写入下一条记录。

#!/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
    }
}'

这是一个更简单的版本

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

输出

-----------------------------------      -----------------------------------     
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                
-----------------------------------      ----------------------------------- 

答案 2 :(得分:0)

假设 file 包含统一的每行五行,使用pastesedprintf

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"

OP规范问题

OP reports that the block lengths may vary,并且应该用固定数量的行分隔。偶数编号的块进入 A列 B列中的奇数编号块。

然后会产生tail -f问题。假设源输入的块长度以1000行开始,然后是一行,1000,1,1000,1,。因此列A 获取所有1000个行块,列B 获取所有一个行块。假设输出中的块每个分隔1行。因此, A列中的一个块与 B列中的500个块对齐。因此,对于具有滚动输出的终端,这意味着在我们可以在 A列中输出第一个块之前,我们必须等待 1000个输入块。要在 A列中输出第三个​​块(在第一个块的正下方),我们必须等待 2000个输入块

如果块相对较慢地添加到输入文件中,块之间有一秒钟的延迟,那么块3将在输入文件中出现需要3秒钟,但块3需要33分钟才能显示显示在输出文件中。

答案 3 :(得分:0)

好吧,因为看起来没有干净的方法,我想出了自己的解决方案。它有点乱,需要安装GNU screen,但它可以工作。块内或块周围的任何数量的线,50%的屏幕自动调整大小,每列独立打印,它们之间有固定数量的换行符。每x秒自动更新一次。 (在我的例子中为120)

#!/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

首先,使用screen -S testscr开始一个屏幕会话,然后在会话内或会话外执行上面的脚本。这将使用每列50%垂直分割屏幕并在两列上执行tail -f,之后它将通过输入文件并逐块写入每个tmp。以所需的方式提交文件。由于它处于无限循环中,它基本上每x秒自动更新所显示的输出(此处为120)。