csplit & 粘贴

csplit & 粘贴

我有一个文本文件列.txt有两列,如下所示:

  1 1.1
  2 4.0
  3 3.2
  start newset
  1 2.2
  2 6.1
  3 10.3
  4 2.1
  start newset
  1 18.2
  2 4.3

我需要将其转换为多列,以便在以下情况下创建一对新列开始新闻集到达了。因此,我想要的输出文本文件看起来像(我想丢弃带有字符串的行开始新闻集):

  1 1.1 1 2.2 1 18.2
  2 4.0 2 6.1 2 4.3
  3 3.2 3 10.3
        4 2.1

答案1

你可以试试这个 awk

awk '
    /^start newset/ {
        max = max>i ? max : i
        i = 0
        set++
        next
    }
    {
        ++i
        a[i][set] = $0
    }
    END {
        for( i=1 ; i<=max ; i++ ) { 
            for( j=1 ; j<=set ; j++ )
                b = b OFS a[i][j]
            sub( "\t" , "" , b )
            print b
            b=""
        }
    }
' set=1 OFS='\t' column.txt

答案2

csplit & 粘贴

用于csplit按一种模式将一个文件分成多个文件。然后使用paste将新文件连接在一起。

awk 'NF' column.txt | csplit --suppress-matched -s -z -f INTERIM -n 4 - '/start newset/' '{*}' ; paste INTERIM* | expand -t 6,13 ; rm -f INTERIM*

相同的代码,为了清晰起见重新格式化:

awk 'NF' column.txt | \
csplit --suppress-matched -s -z -f INTERIM -n 4 - '/start newset/' '{*}' ;

paste INTERIM* | \
expand -t 6,13 ;

rm -f INTERIM*

描述:

  • awk 'NF' column.txt
    删除空行。否则,输入文件中的空行会在输出中放置额外的列分隔符。
  • 分割
    • --suppress-matched
      不要在输出中包含包含分割模式的行。
    • -s
      不显示有关输出文件的摘要信息。
    • -z
      不要生成空的输出文件(即,当输入文件的两个相邻行包含分割模式时)。
    • -f INTERIM
      分割文件的文件名以此字符串开头。
    • -n 4
      分割文件的文件名以包含这么多数字的数字结尾。
    • -
      从 获取输入STDIN,因为我们首先通过 运行输入文件awk
    • '/start newset/'
      在包含此正则表达式的第一行分割输入文件。
    • '{*}'
      继续在包含该正则表达式的每个附加行上拆分输入文件。
  • paste INTERIM*
    加入临时文件。
  • expand -t 6,13
    调整连接文件之间的列间距(例如,从第 6 列开始第二个文件,从第 13 列开始第三个文件)。
  • rm -f INTERIM*
    删除临时文件。

输入文件示例column.txt

1 1.1
2 4.0
3 3.2
start newset
1 2.2
2 6.1
3 10.3
4 2.1
start newset
1 18.2
2 4.3

输出示例:

1 1.1 1 2.2  1 18.2
2 4.0 2 6.1  2 4.3
3 3.2 3 10.3 
      4 2.1  

如果输入文件的行和最终输出的行是缩进的,那就有点复杂了。

输入文件示例column.txt

  1 1.1
  2 4.0
  3 3.2
  start newset
  1 2.2
  2 6.1
  3 10.3
  4 2.1
  start newset
  1 18.2
  2 4.3
  • 更改awk 'NF'awk 'NF { sub(/^ +/,"",$0) ; print $0 }'以在进一步处理之前删除缩进。
  • 更改expand -t 6,13awk '{ print " " $0 }' | expand -t 8,15缩进输出。

输出示例:

  1 1.1 1 2.2  1 18.2
  2 4.0 2 6.1  2 4.3
  3 3.2 3 10.3 
        4 2.1  

答案3

通过一些临时文件进行路由:

$ awk 'BEGIN { n = 1 } /^start newset/ { n++; next } { name = sprintf("tmp-%04d", n); print >name }' file

这不会在终端中产生任何输出,但会创建名为的文件,tmp-n其中n是一个大于或等于 1 的由零填充的四位整数。每组数据都会有一个文件。

然后我们可以将这些临时文件粘贴在一起:

$ paste tmp-*
1 1.1   1 2.2   1 18.2
2 4.0   2 6.1   2 4.3
3 3.2   3 10.3
        4 2.1

或者,使用空格而不是制表符作为分隔符:

$ paste -d ' ' tmp-*
1 1.1 1 2.2 1 18.2
2 4.0 2 6.1 2 4.3
3 3.2 3 10.3
 4 2.1

如果有一个大的数据中的集合数,那么这有两个问题:

  1. 您可能会用完awk.这可以通过更改第二个awk代码块来防止

    { name = sprintf("tmp-%04d", n); print >name }
    

    { name = sprintf("tmp-%06d", n); print >>name; close(name) }
    

    (还要注意格式字符串的变化,以允许更大的数字)

  2. paste由于模式tmp-*扩展到太多文件,因此执行命令可能会出现问题。让我知道这是否是一个问题,然后我会解决它(将有一个 shell 循环通过添加文件中的列来构建结果tmp-*)。

答案4

$ awk '$1+0>=1{a[$1]=a[$1]" "$0}END{for (i in a)print a[i]}' file.txt 
 1 1.1 1 2.2 1 18.2
 2 4.0 2 6.1 2 4.3
 3 3.2 3 10.3
 4 2.1

上面的 awk 命令创建一个名为 a 的数组,并根据第一列存储/附加值。完全读取文件后,只需打印数组值即可。

step 1 : a[1] = "1 1.1"
step 2 : a[2] = "2 4.0"
step 3 : a[3] = "3 3.2"
step 4 : ignore the line # 4. because the first column is not numeric
step 5 : a[1] = "1 1.1 1 2.2"
step 6 : a[2] = "2 4.0 2 6.1".
....
...
once the file is fully procssed by awk, then just print the array values a[1],a[2],a[3]...a[n]

相关内容