[英]Print a file in multiple columns based on delimiter
这似乎是一个简单的任务,但使用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中有两列布局的方式的人也获得了一些信息。 我尝试了column
和pr
,两者都没有按预期工作。 (我在评论中对此进行了详细阐述)
编辑:要清楚,我正在寻找一个通用的方法。 浏览文件,在分隔符之间获取数据,将其放到A列,将下一个数据放到B列,依此类推。
这个问题被标记为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
-----------------------------------
此脚本获取当前终端的最大宽度并将其拆分为2,然后打印由RS =“\\ n \\ n”分隔符拆分的记录,打印找到的第一个并将光标放在其第一行/最后一列以写入下一个记录。
#!/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
----------------------------------- -----------------------------------
假设文件包含每行五行的统一块,使用paste
, sed
和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"
OP规范的问题
OP报告块长度可能会有所不同 ,并且应该用固定数量的行分隔。 偶数编号的块位于A 列,B列编号为奇数编号的块。
那就产生了一个tail -f
问题。 假设源输入的块长度以1000行开始,然后是一行,1000,1,1000,1 等 。 因此, 列A获取所有1000个行块,而列B获取所有一个行块。 假设输出中的块每个分隔1行。 因此, A列中的一个块与B列中的 500个块对齐 。 因此对于具有滚动输出的终端,这意味着在我们可以输出A列中的第一个块之前,我们必须等待1000个输入块 。 要输出A列中的第三个块(在第一个块下面),我们必须等待2000个输入块 。
如果块相对较慢地添加到输入文件中,块之间有一秒钟的延迟,那么块3将在输入文件中出现需要3秒钟,但块3需要33分钟才能显示输出文件。
好吧,既然没有干净的方法可以做到这一点,我提出了自己的解决方案。 它有点乱,需要安装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
)。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.