2016-11-22 109 views
2

我秀例如什么,我需要做的:如何分割文件根据第一列awk的

输入:

name value1 value2 value3 
john xxxxx yyyyy qqqqqq 
john xxxxx ddddd vvvvvv 
john mmmmm jjjjj llllll 
paul xxxxx yyyyy qqqqqq 
paul ccccc ccccc dddddd 

,我需要根据相同名称,以保持头和拆分后的文件在第一列。 我需要根据第一列来命名我的输出文件。

输出:

FILE1:john.tsv

name value1 value2 value3 
john xxxxx yyyyy qqqqqq 
john xxxxx ddddd vvvvvv 
john mmmmm jjjjj llllll 

FILE2:paul.tsv

name value1 value2 value3 
paul xxxxx yyyyy qqqqqq 
paul ccccc ccccc dddddd 

输入和输出文件是标签分开。标题始终相同。

我的解决方案是非常复杂和缓慢:

head -1 INPUT > header 

awk 'NR>1{print $1}' | sort | uniq > names 

while read line 

do grep $line INPUT | cat header - > $line.tsv 

< names 

done 
+0

是他们排序?例如:所有'john'条目总是彼此相邻? – Sundeep

+0

Yeas总是按名称排序。 – Paul

+0

更好地编辑与排序要求的问题,也改变样本输入和预期的输出,以反映排序 – Sundeep

回答

3

数据部分的排序的所有迄今公布的答案有问题,这将使他们的脆弱和/或非便携式的(例如使用getline而不检查它的结果,输出重定向的右侧未使用,使用gawk特有的功能,以及在完成输出文件时不关闭)和/或不必要的复杂。

由第一2列进行排序输入文件,同时保留标题是:

$ awk -v OFS='\t' '{print (NR>1), $0}' file | sort | cut -f2- 
name value1 value2 value3 
john mmmmm jjjjj llllll 
john xxxxx ddddd vvvvvv 
john xxxxx yyyyy qqqqqq 
paul ccccc ccccc dddddd 
paul xxxxx yyyyy qqqqqq 

和强劲,便携,高效打印您的输入,包括标题行到单独的文件命名基于第一列是:

$ cat tst.awk 
NR==1 { hdr=$0; next } 
$1 != prev { 
    close(out) 
    out = $1 ".tsv" 
    print hdr > out 
    prev = $1 
} 
{ print > out } 

所以把它放在一起会是:

awk -v OFS='\t' '{print (NR>1), $0}' file | sort | cut -f2- | awk -f tst.awk 
+0

埃德谢谢你的好解决方案。将有可能请使用您的awk代码,如终端中的一个班轮?我尝试使用:awk'NR == 1 {hdr = $ 0;下一步} $ 1!= prev {close(out)out = $ 1“.csv”print hdr> out prev = $ 1} {print> out}'infile。但它不起作用。 – Geroge

+1

当然,只需用';'替换每一个换行符,除了紧跟在'{'之后的那个换行符。 –

+1

伟大的是现在的作品。非常感谢! – Geroge

5

使用awk我们可以写一些像

$ awk 'NR == 1{header = $0; next} 
    !($1 in filename){ print header > ($1".tsv") } 
    NR > 1 { print $0 > ($1".tsv"); filename[$1] }' file 

它能做什么?

  • NR == 1{header = $0}如果记录的读取数量为1,这是标题,将其保存在header供以后使用。

  • NR > 1 { print $0 > ($1".tsv"); filename[$1] }如果我们已经阅读了多条记录,请将该行的内容打印到文件名$1即第一列。

    • filename[$1]我们将文件名保存在由文件名索引的关联数组中。该数组用于打印出标题。
  • ($1 in filename){ print header > ($1".tsv") }如果我们在filename数组中找不到当前文件名,那么这是第一次出现。所以我们打印出文件头。


编辑

如果你想在第二列中的文件进行排序,那么我们可以先进行排序,然后将它们管awk一样,

$ sort -n -k2 file | awk .... 
  • -n数字排序。
  • -k2按第二个键排序。

这个可能不起作用,如果头也是数字。

+3

建议'awk'NR == 1 {header = $ 0;下一个}!($ 1的文件名){print header> $ 1“.tsv”} {print $ 0> $ 1“.tsv”;文件名[$ 1]}'',以避免为头文件创建以及匹配文件扩展名预计由OP – Sundeep

+0

@Sundeep oops,我错过了。感谢您指出。 – nu11p01n73R

+0

您复制了我尚未发布的答案! –

2

类似@ nu11p01n73R的回答,添加脚本

$ awk 'NR==1{h=$0; next} 
    !p[$1]++{print h > $1} 
      {print | "sort -k2 >> " $1}' file 

$ head paul john 

==> paul <== 
name value1 value2 value3 
paul ccccc ccccc dddddd 
paul xxxxx yyyyy qqqqqq 

==> john <== 
name value1 value2 value3 
john mmmmm jjjjj llllll 
john xxxxx ddddd vvvvvv 
john xxxxx yyyyy qqqqqq 
+0

谢谢你的帮助。我有这个错误信息:NR == 1 {h = $ 0;下一页}!p [$ 1] ++ {print h> $ 1} {print | “sort -k2 >>”>>> $ <<< 1} awk:源代码行1的非法声明 - 任何想法? – Paul

+1

是你的脚本,像我发布的单引号? – karakfa

+0

未定义的输出重定向是每个POSIX未定义的行为,因此可能是导致OP语法错误的原因。 $ 1可能包含空格,因此您需要引用它。切换输出文件时,可能需要关闭()管道。 –