如何将多行转换为固定列长

How to convert multiple lines into fixed column lengths

要将行转换为制表符分隔,很容易

cat input.txt | tr "\n" " "

但我有一个包含 84046468 行的长文件。我希望将其转换为包含 1910147 行和 44 个制表符分隔列的文件。第一列是文本字符串,例如 chrXX_12345_+,其他 43 列是数字字符串。有没有办法执行此转换?

存在 NA,所以我猜想 sed 如果前面的字符串是数字,则用“\n”代替“\t”是行不通的。

样本input.txt

chr10_1000103_+
0.932203
0.956522
1
0.972973
1
0.941176
1
0.923077
1
1
0.909091
0.9
1
0.916667
0.8
1
1
0.941176
0.904762
1
1
1
0.979592
0.93617
0.934783
1
0.941176
1
1
0.928571
NA
1
1
1
0.941176
1
0.875
0.972973
1
1
NA
0.823529
0.51366
chr10_1000104_-
0.952381
1
1
0.973684

样本output.txt

chr10_1000103_+   0.932203 (numbers all tab-delimited)
chr10_1000104_-   etc
(sorry alot of numbers to type manually)

不是最佳解决方案,但应该可行:

line="nonempty"; while [ ! -z "$line" ]; do for i in $(seq 44); do read line; echo -n "$line "; done; echo; done < input.txt 

如果文件中有空行,它将终止。对于更永久的解决方案,我会尝试 perl。


编辑:

如果你关心效率,就用awk。

awk '{ printf "%s\t",  } NR%44==0{ print "" }' < input.txt

您可能想用 | sed 's/\t$//' 去除尾随制表符或使 awk 脚本更复杂。

sed '
# use a delimiter
s/^/M/
:Next
# put a counter
s/^/i/
# test counter
/^\(i\)\{44\}/ !{
   $ !{
# not 44 line or end of file, add the next line
      N
# loop    
      b Next
      }
    }

# remove marker and counter
s/^i*M//
# replace new line by tab
s/\n/     /g' YourFile

如果 sed 上的选项卡超过 255 个(所以 44 个就可以)

下面是使用 4 列而不是 44 列的正确方法:

$ cat file
chr10_1000103_+
0.932203
0.956522
1
chr10_1000104_-
0.952381
1
1

$ awk '{printf "%s%s", [=10=], (NR%4?"\t":"\n")}' file
chr10_1000103_+ 0.932203        0.956522        1
chr10_1000104_- 0.952381        1       1

只需将 4 更改为 44 即可作为您的实际输入。

如果您在输出中看到 control-Ms,那是因为它们存在于您的输入中,因此请在 运行 工具或使用 GNU awk 之前使用 dos2unix 或类似工具删除它们,您可以设置 -v RS='\n\r'.

发布问题时,请务必使其尽可能清晰、简单和简短,以便尽可能多的人有兴趣帮助您。

顺便说一句,cat input.txt | tr "\n" " " 是一个 UUOC,应该只是 tr "\n" " " < input.txt

这可能适合您 (GNU sed):

sed '/^chr/!{H;$!d};x;s/\n/\t/gp;d' file

如果一行不是以 chr 开头,则将其添加到保留 space 中,然后将其删除,除非它是最后一行。如果该行确实开始 chr 或者它是最后一行,则交换到保留 space 并用制表符替换所有换行符并打印出结果。

N.B。下一行的开头将在模式 space 中保持不变,成为新的保留 space.