Объедините два файла построчно с символом тройки разделителя «|||»

14

У меня есть два параллельных файла с одинаковым количеством строк на двух языках, и я планирую объединить эти два файла построчно с разделителем |||. Например, два файла следующие:

Файл А:

1Mo 1,1 I love you.
1Mo 1,2 I like you.
Hi 1,3 I am hungry.
Hi 1,4 I am foolish.

Файл Б:

1Mo 1,1 Ich liebe dich.
1Mo 1,2 Ich mag dich.
Hi 1,3 Ich habe Durst.
Hi 1,4 Ich bin neu.

Ожидаемый результат выглядит так:

1Mo 1,1 I love you. ||| 1Mo 1,1 Ich liebe dich.
1Mo 1,2 I like you. ||| 1Mo 1,2 Ich mag dich.
Hi 1,3 I am hungry. ||| Hi 1,3 Ich habe Durst.
Hi 1,4 I am foolish. ||| Hi 1,4 Ich bin neu.

Я попробовал pasteкоманду, такую ​​как:

paste -d "|||" fileA fileB

Но возвращенный вывод содержит только одну трубу, такую ​​как:

1Mo 1,1 I love you. |1Mo 1,1 Ich liebe dich.
1Mo 1,2 I like you. |1Mo 1,2 Ich mag dich.

Есть ли способ разделить каждую пару линий по трубе |||?

нахмуриться
источник
8
paste -d '|||' fileA - - fileB < /dev/null
Стефан Шазелас
5
оффтоп, но ваши переводы не верны;) "Ich habe Durst" = я thisrty, "Ich bin neu" = я новичок ... не обязательно означает, что вы глупы. ... на тот случай, если вы действительно изучаете немецкий язык ...
dave_alcarin
@ StéphaneChazelas Спасибо, но мой вывод все еще содержит только одну трубу ...
Хмуриться
@dave_alcarin Данк сэр!
Хмуриться

Ответы:

20

С пастой POSIX :

:|paste -d ' ||| ' fileA - - - - fileB

pasteобъединит соответствующие строки всех входных файлов. Здесь у нас есть шесть файлов, fileAчетыре фиктивных файла из стандартного файла -, и fileB.

Список разделителей включает пробел, три трубы и пробел в том порядке, который будет использоваться по pasteкругу.

Для первой строки из шести файлов, fileAбудут объединены с первым фиктивным файлом (что ничего, спасибо оператору no-op :), производим line1-fileA<space>.

Первый фиктивный файл будет соединен со вторым с помощью канала, а line1-fileA |затем второй фиктивный файл с третьим фиктивным файлом. line1-fileA ||Третий фиктивный файл с четвертым фиктивным файлом line1-fileA |||.

И четвертый пустышка fileB, производят line1-fileA ||| line1-fileB.

Эти шаги будут повторяться для всех строк, чтобы получить ожидаемый результат.


Используется :|для меньшего набора текста и в основном используется в интерактивной оболочке. В скрипте вы должны использовать:

</dev/null paste -d ' ||| ' fileA - - - - fileB

чтобы предотвратить появление подоболочки.

cuonglm
источник
1
+1 за :|. умная альтернатива</dev/null
cas
4
... и +1 за умное использование 4 фиктивных файлов из стандартного ввода с помощью - - - -, но в следующий раз вы даже можете написать пару строк для объяснения :)
Hastur
Спасибо, но я все еще получаю вывод с одной трубой ...
Хмуриться
@ Хуэй, ты выполнил команду точно так, как указано, включая все тире и пробелы? Какая у вас операционная система?
Стефан Шазелас
:|paste -d '|' fileA - - fileBдает более правильную версию без разделителя пробелов.
Пол GD
7

Ну, это не использует sed, awk или grep, но вы можете сделать это довольно легко в bash. Команда:

(while IFS= read -r a <&3 && IFS= read -r b <&4; do echo "$a ||| $b"; done) 3<fileA 4<fileB

Проблема с пастой заключается в том, что разделитель представляет собой один символ. Вы также можете вставить один символ и использовать sed для его преобразования, но это может привести к ошибкам, если этот символ уже появился во входном файле.

user3188445
источник
2
Ваше решение не будет работать, если строка содержит какой-либо символ обратной косой черты или начинается с тире. Вы хотите использовать IFS=перед каждым read. Вы можете легко сделать это с paste. Смотрите мой ответ , а также этот, чтобы понять, почему следует избегать использования whileцикла в сценарии оболочки.
Cuonglm
Это работает для моего файла. Многие, спасибо!
хмуриться
5

Версия awk (GNU)

awk '{printf ("%s ||| ", $0); getline < "fileB"; print $0 }' fileA

С помощью getlineкоманды in awkвы можете установить $0(все переменные для столбцов) из следующей входной записи, если getline < "filename"вы установите следующую $0из указанного файла.

getline <"file" Установить $ 0 из следующей записи файла; установить NF.


Почему ваша попытка не сработала так, как вы ожидаете? Из man pasteмы можем прочитать

-d, --delimiters=LIST
     reuse characters from LIST instead of TABs

но он использует разделители по одному для каждого столбца .

Таким образом, команда
paste -d '|*|*' fileA fileB fileA fileBдает мне строки как

Hi 1,3 I am hungry.|Hi 1,3 Ich habe Durst.*Hi 1,3 I am hungry.|Hi 1,3 Ich...
Hi 1,4 I am foolish.|Hi 1,4 Ich bin neu.*Hi 1,4 I am foolish.|Hi 1,4 Ich...


sedРешение , которое я предлагаю , чтобы избежать даже если близко к первоначальной попытке, потому что она исправляет полученное поведение к первоначальной цели:

 paste -d '|' fileA fileB | sed 's/|/|||/g'

Чтобы избежать, потому что вы заменяете каждый шаблон |новым |||, но вы должны предположить, что символ канала ( |) не присутствует в ваших данных , иначе вам придется иметь дело с особыми случаями и сделать более сложный код, чтобы избежать побочных эффектов.


Вариант с конструкцией Here String [ 1 ]<<<

 paste -d ' ||| ' fileA - - - - fileB  <<< ''

Вы устанавливаете 5 разделителей с помощью -d ' ||| '(пробел, |, |, |, пробел) и 4 фиктивных файлов ( - - - -), которые будут принимать данные из пустой строки ''.


Протестировано на GNU Awk 4.0.1, paste (GNU coreutils) 8.21 и sed (GNU sed) 4.2.2

Hastur
источник
Спасибо, команда awk работает!
Хмуриться
1
Пожалуйста. Обновил ответ, добавивsed пример, чтобы избежать (:-)) и больше комментариев.
Hastur
4

Если вы хотите избежать магии и драматизма круговых разделителей и фиктивных файлов, вы можете просто добавить свой разделитель в один файл перед вставкой:

paste <(sed 's/$/ |||/' filea) fileb

дает

1Mo 1,1 I love you. ||| 1Mo 1,1 Ich liebe dich.
1Mo 1,2 I like you. ||| 1Mo 1,2 Ich mag dich.
Hi 1,3 I am hungry. ||| Hi 1,3 Ich habe Durst.
Hi 1,4 I am foolish. |||    Hi 1,4 Ich bin neu.
snth
источник
Мне это нравится для простоты. Я полагаю, что вы имеете в виду «prepend», а не «append». Оформить заказ Hastur's awk за эту версию awk.
Подстановочный
Вы должны изменить процесс замены на канал, чтобы у вас не было ограничения на количество поддерживаемых оболочек.
Cuonglm
@Wildcard да, готов, но я перепишу его, чтобы добавить в filea. Я думаю, что awk немного излишне для этого.
2015 г.
@cuonglm правда, но я хотел избежать труб для ясности. Я почувствовал , как труба будет сделать его начать выглядеть как фиктивные файлы, но вы правильно
snth
0

Вы можете сделать это в Python тоже таким образом.

lines1 = [ line.rstrip() for line in open("file1") ]
lines2 = [ line.rstrip() for line in open("file2") ]
for i in xrange((len(lines1))): print lines1[i] + " ||| " + lines2[i]
... 
1Mo 1,1 I love you. ||| 1Mo 1,1 Ich liebe dich.
1Mo 1,2 I like you. ||| 1Mo 1,2 Ich mag dich.
Hi 1,3 I am hungry. ||| Hi 1,3 Ich habe Durst.
Hi 1,4 I am foolish. ||| Hi 1,4 Ich bin neu.
c4f4t0r
источник