Об’єднайте кілька файлів з одним заголовком


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 '
    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(як у відповіді sputnik ).
Жил "ТАК - перестань бути злим"

Немає рядків заголовків подібні до всіх файлів, і вони знаходяться лише у верхній частині кожного файлу. Так, греп був швидшим. Дякую обом
Jana

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) мають бути об'єднані. Чи слід вводити кожне ім’я файлу чи є якісь інші способи це зробити?
Яна

Дивіться моє відредаговане повідомлення з технікою нарізки
Жил Квенот

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

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

@Gilles: Я помітив вашу відповідь через довгий час, але вона була дуже корисною
Jana

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; хвіст -n +2 -q $ {масив [@]: 0} >> all.txt

Якщо припустити, що ви використовуєте папку з .txt файлами з тим самим заголовком, які потрібно об'єднати / об'єднати, цей код об'єднав би всі файли txt у all.txt лише з одним заголовком. перший рядок (рядки, розділені крапками з комою) збирає всі текстові файли для об'єднання, другий рядок виводить заголовок з першого файлу txt у all.txt , а останній рядок об'єднує всі текстові файли, зібрані без заголовка (запускаючи конкатенація від другого рядка далі) і додає її до all.txt .


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