Slurp-режим in awk?


16

Такі інструменти , як sed, awkабо perl -nобробити їх введення однієї записи в той час, записи бути рядки за умовчанням.

Деякі з них , як і awkз RS, ГНУ sedз -zабо perlз -0oooможе змінити тип запису, вибравши інший роздільник записів.

perl -nможе зробити весь вхід (кожен окремий файл, коли передано декілька файлів) один запис з -0777можливістю (або -0слідом за будь-яким восьмигранним числом більше 0377, 777 - канонічним). Саме так вони називаються режимом сліз .

Чи можна щось подібне зробити за допомогою механізму awks RSчи іншого? Де awkобробляється кожен вміст файлу в цілому, на відміну від кожного рядка кожного файлу?

Відповіді:


15

Ви можете скористатися різними підходами залежно від того, чи awkтрактуєте їх RSяк єдиний символ (як awkце робиться у традиційних реалізаціях) або як регулярний вираз (як gawkабо mawkробити). Порожні файли також складно розглядати як awkтенденцію до їх пропуску.

gawk, mawkАбо інші awkреалізації , де RSможуть бути регулярним виразом.

У цих реалізаціях (бо mawk, майте на увазі, що деякі ОС на зразок Debian надсилають дуже стару версію замість сучасної, підтримувану @ThomasDickey ), якщо вона RSмістить один символ, роздільником записів є цей символ, або awkпереходить у режим абзацу, коли RSпорожній, або трактує RSяк регулярний вираз інакше.

Рішення в тому, щоб використовувати регулярний вираз, який неможливо зіставити. Деякі приходять на думку, як x^або $x( xдо початку, або після закінчення). Однак деякі (особливо з gawk) коштують дорожче, ніж інші. Поки що я виявив, що ^$це найефективніший. Він може збігатися лише на порожньому вході, але тоді не було б нічого протиставити.

Тож ми можемо зробити:

awk -v RS='^$' '{printf "%s: <%s>\n", FILENAME, $0}' file1 file2...

Одним із застережень є те, що він пропускає порожні файли (всупереч perl -0777 -n). Це можна вирішити з GNU awk, поставивши код ENDFILEзамість цього у заяві. Але нам також потрібно скинути $0оператор BEGINFILE, оскільки він інакше не буде скинутий після обробки порожнього файлу:

gawk -v RS='^$' '
   BEGINFILE{$0 = ""}
   ENDFILE{printf "%s: <%s>\n", FILENAME, $0}' file1 file2...

традиційні awkреалізації, POSIXawk

У них RSлише один символ, у них немає BEGINFILE/ ENDFILE, вони не мають RTзмінної, вони також, як правило, не можуть обробити символ NUL.

Ви можете подумати, що використання RS='\0'може працювати тоді, оскільки все одно вони не можуть обробити вхід, що містить байт NUL, але ні, що RS='\0'в традиційних реалізаціях трактується як RS=, що є режимом абзацу.

Одним з варіантів може бути використання символу, який навряд чи знайдеться у вхідному вигляді \1. У мультибайтових локальних символах ви навіть можете зробити його послідовностями байтів, які малоймовірні, оскільки вони утворюють символи, які не присвоєні або не символи, як $'\U10FFFE'у мовах UTF-8. Насправді не дурно захищений, але у вас є проблема і з порожніми файлами.

Іншим рішенням може бути збереження всього введення в змінну та обробка цього в операторі END в кінці. Це означає, що ви можете одночасно обробляти лише один файл:

awk '{content = content $0 RS}
     END{$0 = content
       printf "%s: <%s>\n", FILENAME, $0
     }' file

Це еквівалент sed's:

sed '
  :1
  $!{
   N;b1
  }
  ...' file1

Ще одна проблема з цим підходом є те , що , якщо файл не закінчується символом переведення рядка (а не порожній), один з - як і раніше довільно додають $0в кінці (з gawk, ви б працювати навколо цього, використовуючи RTзамість того , щоб RSв код вище). Однією з переваг є те, що у вас є запис кількості рядків у файлі в NR/ FNR.


що стосується останньої частини ("якщо файл не закінчувався символом нового рядка (і не був порожнім), він все одно довільно додається в $ 0 наприкінці"): для текстових файлів вони повинні мати закінчення новий рядок vi додає один, наприклад, і таким чином змінює файл під час його збереження. Немає закінчуючого нового рядка змушує деякі команди відкинути останній "рядок" (наприклад: wc), а інші все ще "бачать" останній рядок ... ymmv. Тому ваше рішення є дійсним, імо, якщо вам належить обробляти текстові файли (це, мабуть, так, як awk корисний для обробки тексту, але не такий хороший для двійкових файлів ^))
Олів'є Дулак

1
намагаючись пробурхати все в, може вдатися до деяких обмежень ... традиційний awk, мабуть, мав (маєте?) обмеження в 99 полів на лінії ... тож вам може знадобитися використовувати інший FS, щоб уникнути цього обмеження, але ви можете також є обмеження щодо того, якою може бути загальна довжина рядка (або всієї речі, якщо вам вдасться це зробити на одному рядку)?
Олів'є Дулак

нарешті: хакінг (дурний ...) міг би 1-й розібрати весь файл і шукати таблицю, якої немає там, потім tr '\n' 'thatchar' файл перед відправкою в awk, і tr 'thatchar' \n'вихід? (можливо, вам доведеться все-таки додати новий рядок, щоб переконатися, що, як я вже зазначав вище, у вашому вхідному файлі є новий завершальний рядок: { tr '\n' 'missingchar' < thefile ; printf "\n" ;} | awk ..... | { tr 'missingchar' '\n' }(але в кінці цього слова додається "\ n", можливо, вам потрібно буде позбутися ... можливо додавання sed перед остаточним tr? якщо цей tr приймає файли без припинення нових рядків ...)
Олів'є Дулак

@OlivierDulac, обмеження кількості полів буде досягнуто лише в тому випадку, якщо ми отримаємо доступ до NF або будь-якого поля. awkне робить розщеплення, якщо ми цього не зробимо. Сказавши це, навіть у /bin/awkSolaris 9 (на основі 1970 року) не було такого обмеження, тому я не впевнений, що ми можемо знайти таке (все-таки можливе, оскільки у ходу SVR4 було обмежено 99, а nawk 199, так що це ймовірно, зняття цієї межі додано Sun і може бути не знайдене в інших базі SVR4, ви можете протестувати на AIX?).
Стефан Шазелас
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.