Чи може sed замінити нові символи рядка?


43

Чи є проблема з sed і новим символом рядка?
У мене є файл test.txt із наступним вмістом

aaaaa  
bbbbb  
ccccc  
ddddd  

Наступне не працює:
sed -r -i 's/\n/,/g' test.txt

Я знаю, що я можу використовувати trдля цього, але моє питання, чому це видається неможливим з sed.

Якщо це побічний ефект обробки файлу рядок за рядком, мені було б цікаво, чому це відбувається. Я думаю, grepвидаляє нові рядки. Сед робить те саме?


1
У цьому випадку sed може виявитися не найкращим інструментом для використання (наприклад, "tr"). Є інструменти, які більш інтуїтивно зрозумілі, їх легше читати / підтримувати, краще виконувати функції (особливо на великих даних) і т.д. ... Не використовуйте молоток, щоб вставити гвинти (навіть якщо це працює). Порівняння можна знайти на веб-сайті: http://slash4.de/blog/python/sed-replace-newline-or-python-awk-tr-perl-xargs.html
omoser

2
trдодав би трейлінг ,і виводить незакінчену лінію. Краще використовувати pasteнатомість:paste -sd , test.txt
Стефан Шазелас

Відповіді:


49

Якщо GNU sedі надається POSIXLY_CORRECTне знаходиться в середовищі (для однорядного введення):

sed -i ':a;N;$!ba;s/\n/,/g' test.txt

З https://stackoverflow.com/questions/1251999/sed-how-can-i-replace-a-newline-n :

  1. створити мітку через :a
  2. додайте поточний та наступний рядок до простору шаблону через N
  3. якщо ми перебуваємо до останнього рядка, відгалужуємо до створеної мітки $!ba( $!означає не робити цього на останньому рядку (оскільки має бути один остаточний новий рядок)).
  4. нарешті, заміна замінює кожен новий рядок комою на просторі шаблону (який є всім файлом).

Це, мабуть, вказує на те, що проблема полягає в тому, що sed читає рядок за рядком. Але я не можу зрозуміти, чому це проблема. Він міг просто прочитати рядок і замінити новий символ рядка (або останнього символу) на,
Джим

1
@jim Схоже, це не в буфері, щоб відповідати, але я не вільно володію sed, можливо, хтось інший може пролити світло на це. Я думаю, ви повинні поширити свій Q з цією конкретною інформацією, так що люди з більшою ймовірністю читають його та сподіваємось відповісти.
Антон

Це призводить доba: Event not found
krb686

@ krb686 Що таке "Це", про яке ви посилаєтесь? Ви запустили вищевказану sedкоманду з цими точними параметрами? На якому test.txt файлі? З якою версією sed(спробувати sed --version)?
Антон

@Anthon Вибачте, я думаю, я мав на увазі сказати "той". Я прочитав ще одну публікацію SO, яка повідомила мені, що csh вимагає від мене втечі !. Цікаво, що це все ще не спрацювало для мене, і мені в кінцевому підсумку довелося подвоїти втечу з !мого .cshсценарію. Тож у мене зараз немає проблем, але ви знаєте, чому це могло бути? Що для мене працювалоsed :a;N;$\\!ba;s/\n/ /g'
krb686

17

Це працює з GNU sed:

sed -z 's/\n/,/g' 

-z включено з 4.2.2

NB. -zзмінює роздільник на нульові символи ( \0). Якщо ваш вхід не містить нульових символів, весь вхід розглядається як один рядок. Це може мати свої обмеження .

Щоб уникнути заміни нового рядка останнього рядка, ви можете змінити його назад:

sed -z 's/\n/,/g;s/,$/\n/'

(Що таке sedсинтаксис GNU знову, але це не має значення, оскільки вся справа лише в GNU)


3
Це також замінить останній новий рядок, який може бути не тим, чого хоче ОП ... порівняйте результат з рішенням mikeserv .
don_crissti

7

З веб-сайту Oracle:

Утиліта sed працює, послідовно читаючи файл, рядок за рядком, в пам'ять. Потім він виконує всі дії, вказані для рядка, і повертає лінію назад в пам'ять, щоб скинутись на термінал із запрошеними змінами. Після того, як всі дії будуть виконані в цьому одному рядку, він читає наступний рядок файлу і повторює процес, поки не закінчиться з файлом.

В основному це означає, що оскільки sed читає рядок за рядком, символ нового рядка не відповідає.

Рішення https://stackoverflow.com/questions/1251999/sed-how-can-i-replace-a-newline-n таке:

sed ':a;N;$!ba;s/\n/,/g'

або, у портативній версії (без ;збивання ярликів після стрибка)

sed -e ':a' -e 'N;$!ba' -e 's/\n/,/g'

На цій сторінці подано пояснення того, як це працює.


Я використовував модифіковану форму цього для розбору журналів VPN і розміщував у цьому ж рядку інформацію про "автентифікацію" та інформацію про часові позначки. Ура!
користувач208145

Зауважте, що цей синтаксис є специфічним для GNU, і навіть для GNU sed, якщо POSIXLY_CORRECT знаходиться в середовищі, а вхід має лише один рядок, виходу не буде.
Стефан Шазелас

5

sedзавжди видаляє \nкінцеву лінію ewline перед тим, як заповнити простір шаблону, а потім додає його перед тим, як виписати результати свого сценарію. \nEwline можна було в модельному просторі різними способами - але ніколи , якщо це не є результат редагування. Це важливо - \nлінії ewlines в sedпросторі картини завжди відображають зміни, і ніколи не трапляються у вхідному потоці. \newlines - єдиний роздільник, на який держ sedможе розраховувати при невідомому введенні.

Якщо ви хочете замінити всі \newlines комами, а ваш файл не дуже великий, тоді ви можете зробити:

sed 'H;1h;$!d;x;y/\n/,/'

Це додає кожен рядок вводу до hстарого простору - за винятком першого, який замість замінює hстарий пробіл - після \nсимволу ewline. Потім він dвибирає кожний рядок, не $!останню з результатів. На останньому рядку Hстарі та пробіли шаблону xзмінюються, і всі \nсимволи ewline y///переводяться на коми.

Для великих файлів така річ неодмінно спричиняє проблеми - sedбуфер на межі рядків, який може бути легко переповнений діями такого роду.


2

Крім того, ви можете використовувати трохи простіший синтаксис:

sed ':a;N;s/\n/,/g;ba'

... лише зміна порядку послідовності.


3
Але виконує sкоманду для кожного рядка вводу на просторі шаблону, який стає все більшим.
Стефан Шазелас

1

Тут є дуже приємна сед магія. І кілька хороших моментів щодо переповнення простору візерунком. Я люблю використовувати sed навіть тоді, коли це не найпростіший спосіб, тому що він такий компактний і потужний. Однак він має свої обмеження, і для великих обсягів даних простір шаблону повинен був бути махоосивним.

GNU говорить про це:

Тим, хто хоче написати портативні сценарії sed, пам’ятайте, що, як відомо, деякі реалізації обмежують довжину рядків (для пробілів шаблону та місця утримувати) не більше 4000 байт. Стандарт posix визначає, що відповідні реалізації sed повинні підтримувати щонайменше 8192 байт довжини рядка. GNU sed не має вбудованого обмеження по довжині лінії; до тих пір, поки це може заштрихувати () більше (віртуальну) пам'ять, ви можете живити або будувати лінії, скільки завгодно.
Однак рекурсія використовується для обробки підпрограм та невизначеного повторення. Це означає, що наявний простір стека може обмежувати розмір буфера, який може бути оброблений певними шаблонами.

Мені нема чого додати, але я хотів би вказати вам на моє керівництво для sed . Це чудово. http://www.grymoire.com/Unix/Sed.html

і ось моє рішення:

for i in $(cat test.txt); do echo -n $i','; done; echo '' >> somewhere

добре це працює



-1

Скажімо, ви хочете замінити нові рядки на \n. Я хотів це зробити, тому ось що я зробив:

(echo foo; echo bar; echo baz) | sed -r '$!s/$/\\n/' | tr -d '\n' 
# Output: foo\nbar\nbaz

Ось що це робить: для всіх рядків, крім останнього , додайте \n. Потім видаліть нові рядки за допомогою tr.


-rдоступний лише в GNU sed, а не в BSD.
kenorb
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.