连接具有相同标题的多个文件


26

我有多个文件,这些文件具有相同的标头,并且在其下具有不同的向量。我需要将所有这些都连接起来,但是我只希望连接第一个文件的标头,并且我不希望将其他头连接在一起,因为它们都是相同的。

例如:file1.txt

<header>INFO=<ID=DP,Number=1,Type=Integer>
<header>INFO=<ID=DP4,Number=4,Type=Integer>
A
B 
C

file2.txt

<header>INFO=<ID=DP,Number=1,Type=Integer>
<header>INFO=<ID=DP4,Number=4,Type=Integer>
D
E 
F

我需要输出

<header>INFO=<ID=DP,Number=1,Type=Integer>
<header>INFO=<ID=DP4,Number=4,Type=Integer>
A
B
C
D
E 
F

我可以在R中编写脚本,但是在shell中需要它吗?

Answers:


17

如果您知道如何在R中进行操作,那么一定要在R中进行操作。使用经典的Unix工具,这自然是在awk中完成的。

awk '
    FNR==1 && NR!=1 { while (/^<header>/) getline; }
    1 {print}
' file*.txt >all.txt

awk脚本的第一行与文件(FNR==1)的第一行匹配,除非它也是所有文件(NR==1)的第一行。当满足这些条件时,将while (/^<header>/) getline;执行表达式,这会使awk继续读取另一行(跳过当前行),只要当前行与regexp匹配即可^<header>。awk脚本的第二行将打印所有内容,但之前已跳过的行除外。


谢谢吉尔。我的每个文件都以GB为单位。R这样做效率不高。这就是为什么我问。
2013年

@Jana是否有看起来像标题但不在文件顶部的行?如果没有,最快的方法就是使用grep(例如sputnik的答案)。
吉尔斯(Gillles)“所以-别再邪恶了”

没有标题行与所有文件都相似,它们仅位于每个文件的顶部。是的,grep更快。谢谢你们
Jana 2013年

1
@Jana顺便说一句,如果所有文件的标题行数都相同,则这是另一种方式(我希望更快):(head -n 10 file1.txt >output.txt && tail -q -n +11 file*.txt >>output.txt如果您有10条标题行)。另外,如果文件名中带有数字,请注意file9.txtfile89.txt和之间排序file90.txt。如果您的文件已编号喜欢file001.txt,...... files009.txtfiles010.txt......,然后files*.txt会列出他们在正确的顺序。
吉尔(Gilles)“所以,别再邪恶了”

不需要正则表达式匹配 的更好的解决方案(来自stackoverflow.com/a/16890695/310441):–awk 'FNR==1 && NR!=1{next;}{print}' *.csv
Owen

42

另一个解决方案,类似于和cat+grep上面的“ ”,使用tailhead

  1. 将第一个文件的标头写入输出中:

    head -2 file1.txt > all.txt

    - head -2获取文件的前两行。

  2. 添加所有文件的内容:

    tail -n +3 -q file*.txt >> all.txt

    - -n +3使tail打印行从第3行到最后一行, -q告诉它不要打印文件名为(read man)的标题, >>将其添加到文件中,而不用覆盖它>

并且确保您可以将两个命令放在一行中:

head -2 file1.txt > all.txt; tail -n +3 -q file*.txt >> all.txt

或代替;放置&&在它们之间进行成功检查。


3
我建议将其进一步简化为:(head -2 file1.txt ; tail -n +3 -q file*.txt ) > all.txt(head -2 file1.txt && tail -n +3 -q file*.txt ) > all.txt
HongboZhu

4

尝试这样做:

$ cat file1.txt; grep -v "^<header" file2.txt
<header>INFO=<ID=DP,Number=1,Type=Integer>
<header>INFO=<ID=DP4,Number=4,Type=Integer>
A
B 
C
D
E 
F

注意

  • -v标志意味着反转的匹配
  • ^REGEX中,表示字符串的开头
  • 如果您有一堆文件,则可以

array=( files*.txt )
{ cat ${array[@]:0:1}; grep -v "^<header" ${array[@]:1}; } > new_file.txt

这是数组切片技术。


感谢sputnick,但是我有大约30个文件(file1.txt,file2.txt,file3.txt..filen.txt)要串联。我应该输入每个文件名还是其他方法?
Jana 2013年

请参阅我用切片技术编辑的帖子
Gilles Quenot 2013年

这将删除<header>文件中任何地方的行,而不仅仅是在开头。根据数据的不同,这里可能不是问题。
吉尔斯(Gillles)“所以-别再邪恶了”

1
更简单:grep '^<header>' file1.txt >output.txt && grep -v '^<header>' file*.txt >>output.txt
吉尔斯(Gilles)'所以别再邪恶了'

@吉尔斯:很长一段时间后,我注意到了您的回答,但这非常有用
2013年

1

tail命令(至少在GNU上)具有跳过给定数量的初始行的选项。要从第二行开始打印,即跳过单行标题,请执行以下操作:tail -n+2 myfile

因此,要在Bash中保留第一个文件的两行标题而不是第二个文件:

cat file1.txt <(tail -n+3 file2.txt) > combined.txt

或者,对于许多文件:

head -n1 file1.txt > combined.txt
for fname in *.txt
do
    tail -n+3 $fname >> combined.txt
done

grep -v如sputnik所示,如果已知某个字符串出现在所有标题行中,但在其余所有输入文件中都没有,则这是一种更简单的方法。


1

较短(不一定更快)sed

sed -e '3,${/^<header>/d' -e '}' file*.txt > all.txt

<header>...将从第3 行开始删除所有行,因此将保留第一个标头,并删除其他标头。如果标题中的行数不同,请相应地调整命令(例如,对于6行标题,请使用7代替3)。
如果标题中的行数未知,则可以这样尝试:

sed '1{
: again
n
/^<header>/b again
}
/^<header>/d
' file*.txt > all.txt

0

array =(* .txt); head -1 $ {array [0]}> all.txt; tail -n +2 -q $ {array [@]:0} >> all.txt

假设您使用的文件夹带有.txt文件,且具有相同的标头,需要合并/串联,那么此代码会将txt文件全部合并到all.txt中,而只有一个标头。第一行(用分号分隔的行)收集所有要串联的文本文件,第二行将第一个txt文件的标头输出到all.txt中,最后一行将不带标头的所有文本文件连接起来(通过启动从第2行开始并置),并将其附加到all.txt


解释一点点会去一个很大的帮助将来的用户
杰夫·夏勒
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.