Объединить несколько файлов с одинаковым заголовком


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, но мне это нужно в оболочке?

Ответы:


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 продолжает читать другую строку (пропуская текущую), пока текущая соответствует регулярному выражению ^<header>. Вторая строка скрипта awk печатает все, кроме строк, которые были пропущены ранее.


Спасибо, Жиль. Каждый из моих файлов в ГБ. R не будет эффективным делать это. Вот почему я спросил.
Яна

@Jana Есть ли строки, которые выглядят как заголовки, но не в верхней части файла? Если нет, то самый быстрый способ - использовать grep(как в ответе спутника ).
Жиль "ТАК - перестань быть злым"

Ни одна строка заголовка не похожа на все файлы и они находятся только вверху каждого файла. Да, Греп был быстрее. Спасибо вам обоим
Яна

1
@Jana Кстати, если все ваши файлы имеют одинаковое количество строк заголовка, вот другой способ (который я ожидаю, что будет еще быстрее): head -n 10 file1.txt >output.txt && tail -q -n +11 file*.txt >>output.txt(если у вас есть 10 строк заголовка). Кроме того, если ваши файлы имеют номера в именах, будьте осторожны, если они file9.txtотсортированы между file89.txtи file90.txt. Если ваши файлы номера нравится file001.txt, ..., files009.txt, files010.txt..., то files*.txtперечислим их в правильном порядке.
Жиль "ТАК - перестань быть злым"

Лучшее решение (от stackoverflow.com/a/16890695/310441 ), которое не требует сопоставления регулярному выражению: awk 'FNR==1 && NR!=1{next;}{print}' *.csv
Owen

42

Другое решение, аналогичное " cat+grep" сверху, с использованием tailи head:

  1. Запишите заголовок первого файла в вывод:

    head -2 file1.txt > all.txt

    - head -2получает 2 первые строки файла.

  2. Добавьте содержимое всех файлов:

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

    - -n +3делает tailлинию печати с 3 - го до конца, -qговорит ему , чтобы не печатать заголовок с именем файла ( для чтения 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), которые нужно объединить. Должен ли я вводить каждое имя файла или есть ли другие способы сделать это?
Яна

Смотрите мой отредактированный пост с техникой нарезки
Gilles Quenot

Это удаляет <header>строки в любом месте файлов, а не только в начале. Это может не быть проблемой здесь, в зависимости от данных.
Жиль "ТАК - перестань быть злым"

1
Проще:grep '^<header>' file1.txt >output.txt && grep -v '^<header>' file*.txt >>output.txt
Жиль "ТАК - перестань быть злым"

@ Жиль: я заметил твой ответ через долгое время, но он был очень полезен
Яна

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то, как показал спутник , это более простой подход.


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 .


малюсенькое объяснение будет иметь большое значение для помощи будущим пользователям
Джефф Шаллер
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.