Об’єднайте два файли по черзі з символом потрійної труби відмежувача "|||"


14

У мене є два паралельних файли з однаковою кількістю рядків на двох мовах і планую об'єднати ці два файли по черзі з роздільником |||. Наприклад, два файли наступні:

Файл A:

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

Файл B:

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" = Я цертрі, "Ich bin neu" = Я новий ... не обов'язково означає, що ти дурний. ... про всяк випадок, коли ви насправді
вивчаєте

@ StéphaneChazelas Thx, але мій висновок все ще містить лише одну трубу ...
Frown

@dave_alcarin Данк sehr!
Хмурився

Відповіді:


19

З пастою POSIX :

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

pasteз'єднає відповідні рядки всіх вхідних файлів. Тут ми маємо шість файлів fileA,, чотири фіктивні файли від стандартних в -і fileB.

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

Для першої лінії шість файлів, fileAбуде поєднана з першим фіктивним файлом (який не що інше, спасибі до Ні-оп: оператор), виробляє line1-fileA<space>.

Перший макетний файл буде з'єднаний з другим трубою, продукувати line1-fileA |, потім другий файл манекена з третім файлом манекена, виробляти line1-fileA ||, третій файл манекена з четвертим файлом манекена, виробляти line1-fileA |||.

І четвертий фіктивний файл з fileB, виробляти line1-fileA ||| line1-fileB.

Цей крок буде повторюватися для всіх рядків, і дасть очікуваний результат.


Використання :|призначене для менш типізованого використання, і в основному використання в інтерактивній оболонці. У сценарії ви повинні використовувати:

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

щоб запобігти породженню передплавки.


1
+1 для :|. розумна альтернатива</dev/null
cas

4
... і +1 для розумного використання 4 фіктивних файлів зі стандартного вводу - - - -, але наступного разу ви навіть можете написати пару рядків для пояснення :)
Hastur

Thx, але я все одно отримую вихід однією трубою ...
Хмурився

@hui, ти виконав команду точно так, як задано, включаючи всі тире та пробіли? Яка ваша операційна система?
Стефан Шазелас

:|paste -d '|' fileA - - fileBдає більш правильну версію без роздільника місця.
Pål 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, щоб перетворити його, але це було б видом помилок, якщо символ уже з'явився у вхідному файлі.


2
Ваше рішення не буде працювати, якщо рядок містить символ зворотної косої риси або починається з тире. Ви хочете використовувати IFS=перед кожним read. Ви легко можете це зробити paste. Дивіться мою відповідь , а також цю, щоб дізнатися, чому слід уникати використання whileциклу в скрипті оболонки.
cuonglm

Це працює для мого файлу. Багато Thx !!!
Хмурився

5

Версія awk (GNU)

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

За допомогою getlineкоманди " Вхід" awkви можете встановити $0(всі змінні для стовпців) з наступного запису введення, якщо getline < "filename"встановити наступний $0з вказаного файлу.

getline <"файл" Встановіть 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'

Щоб цього уникнути, оскільки ви замінюєте кожен шаблон |новим |||, але ви повинні припустити, що символ pipe ( |) відсутній у ваших даних , інакше вам доведеться розібратися з особливими випадками і скласти більш складний код, щоб уникнути побічних ефектів.


Варіант із конструкцією Here String [ 1 ]<<<

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

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


Випробувано на GNU Awk 4.0.1, вставити (GNU coreutils) 8.21 та sed (GNU sed) 4.2.2


Thx, команда awk працює!
Хмурився

1
На здоров'я. Оновіть відповідь, додавши sedприклад, щоб уникнути (:-)) та більше коментарів.
Гастур

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.

Мені це подобається через простоту. Я вважаю, ти маєш на увазі "додавання", а не "додавання". Отримайте відповідь на відповідь Hastur для версія awk цього.
Wildcard

Ви повинні змінити процес заміни на трубу, щоб у вас не було обмеження для кількості оболонок, які його підтримують.
cuonglm

@Wildcard так, додайте, але я перепишу його, щоб додати до файли. Я думаю, що awk є дещо зайвим для цього.
snth

@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.
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.