这看起来像一个简单的任务,但是使用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列,依此类推。
答案 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 包含统一的每行五行,使用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 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
)。