我有一个文本文件列.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,13
为awk '{ 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
如果有一个大的数据中的集合数,那么这有两个问题:
您可能会用完
awk
.这可以通过更改第二个awk
代码块来防止{ name = sprintf("tmp-%04d", n); print >name }
到
{ name = sprintf("tmp-%06d", n); print >>name; close(name) }
(还要注意格式字符串的变化,以允许更大的数字)
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]