文本处理行块的行到列 Awk

文本处理行块的行到列 Awk

我有如下输入文件。我需要按照 o/p 提到的那样重新格式化文本。我使用从该论坛获得的 awk,但它是根据列表 A 列数显示的。请帮忙

awk 'BEGIN{ max=0 }
 /^List/{ if(k && k>max) { max=k; idx=c } ++c; k=0 }
 NF{ a[c][++k]=$0 }
 END{ 
     for(i=1;i<=max;i++) 
         for(j=1;j<=c;j++) printf "%s%s",a[j][i],(j==c)?ORS:"\t" 
 }' filename | column -ts$'\t'  

输入:-

List A
Hello
how are you
fine

List B
good: Fine_health
hello: world_free
some: unkon_text
some: unkon_text1
some: unkon_text2

预期产出

List A       List B
Hello        good: Fine_health
how are you  hello: world_free
fine         some: unkon_text
             some: unkon_text1
             some: unkon_text2

答案1

使用(以前称为 Perl_6)

~$ raku -e 'my @a = slurp.split("\n\n"); @a.=map(*.split("\n", :skip-empty));  \
            my $length = @a>>.elems.max;  for ^$length -> $i {  \
               print ($_[$i] // q[ ]) ~ q[|] for @a; "".say;    \
               };'  filename.txt  |  column -ts'|'

上面的答案是用 Raku 编码的。行被slurp编入(一次阅读)和split段落(\n\n)。段落被分配给@a数组。在下一个语句中,每个段落元素都被分成\n几行。计算数组max的元素长度。elems@a

@a然后将数组的每个位置(读:段落)print导出到完整的 max $length,并且每个位置的子元素(读:行)未定义(使用//“定义或”运算符),一个q[ ]空格已插入(“占位符”列)。列~与尾随|栏连接,然后每行以换行符终止(使用"".say;)。

为了给出 OP 所需的输出,unix 实用程序column用于在插入的|条分隔符上拆分列。对于仅限 Raku 的解决方案,替换~ q[|]~ qb[\t]会返回制表符分隔的输出。

输入示例:

List A
Hello
how are you
fine

List B
good: Fine_health
hello: world_free
some: unkon_text
some: unkon_text1
some: unkon_text2

示例输出:

List A        List B
Hello         good: Fine_health
how are you   hello: world_free
fine          some: unkon_text
              some: unkon_text1
              some: unkon_text2

https://raku.org
https://rakudo.org

答案2

您的输入和输出示例意味着您只想将两个列表连接起来。您不需要 awk 来完成此任务。将输入文件分成两个:lista 和 listb,然后使用粘贴命令将它们连接起来:

paste lista listb

您可以自动将输入文件分解为两个文件(假设每个部分由一两个空行分隔),然后使用 awk 和 RS 选项作为空白:

awk -v RS= '{print > ("list" NR ".txt")}' listall

这会生成文件list1.txt和list2.txt

然后:

paste list1.txt list2.txt

答案3

发布的脚本确实不是工作。它只输出前 4 行——列表 B 中的最后两行被省略。

问题是 的值k计算每个列表中的行数。但它仅存储max开始每个列表的长度,因此不考虑第二个列表的长度。

解决方法是if(k && k>max) { max=k; }在读取最后一个列表后,将 重复作为 END 块中的第一行。

这揭示了另一个错误。最后两行没有按列列出 - 它们出现在第 1 列中。问题似乎是column无法识别零长度的第一列:如果我.在每个值的开头强制使用 a,它会正确对列表 B 进行列列。

就我个人而言,我会在 awk 中进行列化——保存每列中任何条目的最大长度,并用%-*s宽度说明符将它们隔开。这可能就是未使用的变量的idx用途。

编辑:是的,绝对是专栏中的错误。该选项卡针对 进行操作Four,但针对 忽略Three

有一个-n选项支持前导分隔符和重复分隔符,但被记录为 Debian 扩展(并且也适用于我的 Mint (Ubuntu) 发行版)。如果没有-n,空值将被丢弃(即行首和行尾的分隔符将被忽略),并且多个相邻的分隔符将被合并。

$ cat -vet foo
One$
 Two$
^IThree$
q^IFour$
$ column -t -s $'\t' foo | cat -vet
One$
 Two$
Three$
q      Four$

编辑二:此版本具有更多功能。

(a) 通过在内部进行制表来避免命令中的错误column(还避免了额外的过程以及将 awk 和列将整个数据集存储在内存中的内存开销)。

(b) 接受多个文件参数(默认情况下为标准输入,因此它在管道中工作)。

(c) 适用于任意数量的输出列,而不仅仅是两个。

(d) 修复了最初发布的错误(最右列的长度被忽略)。

#! /bin/bash

Awk='
BEGIN { Gap = 2; }
/^List/ { ++col; row=0; }
NF { X[++row, col] = $0;
    if (mxrow < row) mxrow = row;
    if (len[col] < length($0)) len[col] = length($0);
}
function Column (Local, r, c) {
    for (r = 1; r <= mxrow; ++r) {
        for (c = 1; c < col; ++c) 
            printf ("%-*s", Gap + len[c], X[r,c]);
        printf ("%-s\n", X[r,c]);
    }
}
END { Column( ); }
'
    awk "${Awk}" "${@:-}" 
    

相关内容