按行分割文件,并保留第一个字符串作为输出文件的标题 sed awk 纯bash

问题描述

我有如下文件

t_#_3_0 v_0_17  v_1_20  v_2_78  u_0_1   u_0_2   u_1_2
t_#_3_1 v_0_144 v_1_17  v_2_20  u_0_1   u_0_2   u_1_2
t_#_3_2 v_0_143 v_1_233 v_2_238 u_0_1   u_0_2   u_1_2
t_#_3_3 v_0_20  v_1_253 v_2_275 u_0_1   u_0_2   u_1_2
t_#_3_4 v_0_144 v_1_209 v_2_90  u_0_1   u_0_2   u_1_2
t_#_3_5 v_0_144 v_1_209 v_2_30  u_0_1   u_0_2   u_1_2
t_#_3_6 v_0_19  v_1_20  v_2_78  u_0_1   u_0_2   u_1_2
t_#_3_7 v_0_20  v_1_7   v_2_78  u_0_1   u_0_2   u_1_2
t_#_3_8 v_0_16  v_1_20  v_2_78  u_0_1   u_0_2   u_1_2
t_#_3_9 v_0_15  v_1_20  v_2_78  u_0_1   u_0_2   u_1_2

我想逐行拆分它,并将第一个字符串保留为新文件标题。我想要的输出应该像这样。

file 1: t_#_3_0.txt (inside located line - t_#_3_0 v_0_17 v_1_20 v_2_78 u_0_1 u_0_2 u_1_2)
file 2: t_#_3_1.txt
file 3: t_#_3_2.txt

我尝试了split命令,但它会导致输出文件的数字索引。

感谢您的建议!

谢谢!

Olha

解决方法

使用GNU awk:

awk '{name=$1 ".txt"; print >name; close(name)}' file

变量name包含第一列后缀.txt的内容。 print >name将完整的当前行写入文件name

,

我对要求的理解:

  • 源文件中的每一行都将被复制到一个新文件中
  • 新文件以字段(第1列)的内容命名

一个awk解决方案(假设原始数据位于文件file.all中)

$ awk '{ fn=$1".txt" ; print > fn ; close (fn) } ' file.all
$ for f in t*#*txt
do
echo "+++++++++++++ $f"
cat $f
done

+++++++++++++ t_#_3_0.txt
t_#_3_0 v_0_17  v_1_20  v_2_78  u_0_1   u_0_2   u_1_2
+++++++++++++ t_#_3_1.txt
t_#_3_1 v_0_144 v_1_17  v_2_20  u_0_1   u_0_2   u_1_2
+++++++++++++ t_#_3_2.txt
t_#_3_2 v_0_143 v_1_233 v_2_238 u_0_1   u_0_2   u_1_2
+++++++++++++ t_#_3_3.txt
t_#_3_3 v_0_20  v_1_253 v_2_275 u_0_1   u_0_2   u_1_2
+++++++++++++ t_#_3_4.txt
t_#_3_4 v_0_144 v_1_209 v_2_90  u_0_1   u_0_2   u_1_2
+++++++++++++ t_#_3_5.txt
t_#_3_5 v_0_144 v_1_209 v_2_30  u_0_1   u_0_2   u_1_2
+++++++++++++ t_#_3_6.txt
t_#_3_6 v_0_19  v_1_20  v_2_78  u_0_1   u_0_2   u_1_2
+++++++++++++ t_#_3_7.txt
t_#_3_7 v_0_20  v_1_7   v_2_78  u_0_1   u_0_2   u_1_2
+++++++++++++ t_#_3_8.txt
t_#_3_8 v_0_16  v_1_20  v_2_78  u_0_1   u_0_2   u_1_2
+++++++++++++ t_#_3_9.txt
t_#_3_9 v_0_15  v_1_20  v_2_78  u_0_1   u_0_2   u_1_2
,

这些结果让我感到惊讶。 :)

sed

只是从工具箱中拉出一些奇怪的东西,作为为什么您应该不时评估方法的示例...

$: time sed -En 's/^([^ ]+)( .*)$/printf "%s%s\n" "\1" "\2" > \1.txt/e' file
real    0m0.859s
user    0m0.183s
sys     0m0.480s

我认为这样做会很慢,但是为了以防万一,在工具箱中安装它还是很好的。不要用钳子钉指甲。

awk

$: time awk '{ fn=$1".txt" ; print > fn ; close (fn) } ' file
real    0m0.141s
user    0m0.031s
sys     0m0.077s

出乎意料的更快,对于较大的文件可能会很多。

但是让我感到惊讶的人-

bash

$: time while read line; do echo "$line" > "${line%%[   ]*}"; done < file
real    0m0.015s
user    0m0.000s
sys     0m0.016s

注意-编辑以使用空格和/或制表符

"${line%%[ ]*}"在方括号[ ]之间有一个空格和一个制表符作为字段定界符。

我认为这里节省的时间是文件管理的开销。众所周知read的运行速度很慢,但是我想操作系统可以很好地处理自己的I / O。

也许有人可以进行更深入的分析?

将测试详细说明为1万条记录的样本,大大缩小了bashawk之间的差距-

$: for x in {0..9999}; do echo "t_${x}_3_0 v_0_17  v_1_20  v_2_78  u_0_1   u_0_2   u_1_2"; done >| file

$: time while read line; do echo "$line" > "${line%% *}"; done < file
real    0m24.022s
user    0m2.360s
sys     0m11.938s

$: time awk '{ fn=$1".txt" ; print > fn ; close (fn) } ' file
real    0m27.284s
user    0m1.312s
sys     0m12.656s

$: $: time sed -En 's/^([^ ]+)( .*)$/printf "%s%s\n" "\1" "\2" > \1.txt/e' file
real    13m28.503s
user    1m48.374s
sys     8m22.970s

我怀疑与较小的数据集有很多不同之处,也许是awk的编译时间?

测试100k以确认-

$: wc -c file # >5.5MB
5788890 file

bash

real    8m42.666s
user    0m28.671s
sys     2m34.781s

awk

real    8m15.096s
user    0m15.546s
sys     2m35.421s

我真的很惊讶它们之间的差别很小。
我认为是因为大多数时候 是文件I / O操作。