Intereting Posts
Соединение не установлено снова после отсоединения-подключения Ethernet-кабеля Как я могу постоянно запускать скрипт bash, проверяя, изменилось ли значение? Иерархия файлов Linux – лучшее место для хранения файлов lockfiles? Как mount определяет параметры установки по умолчанию для вновь подключенных файловых систем? Нужно ли избавляться от процесса зомби? Редактирование последовательности полей в vim Инкремент оглядывается назад в файле конфигурации ежедневно +1 Какие единицы времени используют «сверху»? Потоки бит каретки и поток строки в двоичном файле во время загрузки TFTP в режиме ASCII Как выполнить xargs grep для вывода grep с пробелами? Быстрый способ удаления файлов с строками меньше x icmp ограничен во многих сетевых пространствах? выводить как stderr, так и stdout на консоль и хранить их в файле в одно и то же время Как создать несколько пользователей root-like и non_root-like linux Как маршрутизировать пакеты между частными сетями в Linux?

Заменить строку, содержащую новую строку в огромном файле

Кто-нибудь знает о нестрочном инструменте для «двоичного» поиска / замены строк в несколько эффективном для памяти режиме? См. Также этот вопрос .

У меня есть текстовый файл + 2 ГБ, который я бы хотел обработать, аналогично тому, как это выглядит:

sed -e 's/>\n/>/g' 

Это означает, что я хочу удалить все новые строки, которые появляются после a > , но не где-либо еще, чтобы исключить tr -d .

Эта команда (которую я получил от ответа на подобный вопрос ) терпит неудачу, так как couldn't re-allocate memory :

 sed --unbuffered ':a;N;$!ba;s/>\n/>/g' 

Итак, есть ли какие-либо другие методы, не прибегая к C? Я ненавижу perl, но я готов сделать исключение в этом случае 🙂

Я точно не знаю какого-либо символа, который не встречается в данных, поэтому временная замена \n другим символом – это то, что я хотел бы избежать, если это возможно.

Любые хорошие идеи, кто-нибудь?

Это действительно тривиально в Perl, вы не должны ненавидеть его!

 perl -i.bak -pe 's/>\n/>/' file 

объяснение

  • -i : отредактировать файл на месте и создать резервную копию оригинала с именем file.bak . Если вам не нужна резервная копия, просто используйте perl -i -pe .
  • -pe : прочитать файл ввода строки за строкой и распечатать каждую строку после применения сценария, указанного как -e .
  • s/>\n/>/ : подстановка, как и sed .

И вот подход awk :

 awk '{if(/>$/){printf "%s",$0}else{print}}' file2 

Решение perl :

 $ perl -pe 's/(?<=>)\n//' 

Explaination

  • s/// используется для замены строк.
  • (?<=>) <=> (?<=>) – это шаблон lookbehind.
  • \n соответствует новой строке.

Все значения шаблона удаляют всю новую строку, которая имеет > перед ней.

Как насчет этого:

 sed ':loop />$/ { N s/\n// b loop }' file 

Для GNU sed вы также можете попробовать добавить опцию -u ( --unbuffered ) в соответствии с вопросом. GNU sed также доволен этим как простой однострочный:

 sed ':loop />$/ { N; s/\n//; b loop }' file 

Вы должны иметь возможность использовать sed с помощью команды N , но трюк будет заключаться в том, чтобы удалить одну строку из пространства шаблонов каждый раз, когда вы добавляете другую (чтобы пространство шаблонов всегда содержало только две последовательные строки, вместо того, чтобы читать весь файл) – попробуйте

 sed ':a;$!N;s/>\n/>/;P;D;ba' 

РЕДАКТИРОВАТЬ: после перечитывания знаменитых одноразовых песен Питера Круминьса, я считаю, что лучшее решение sed будет

 sed -e :a -e '/>$/N; s/\n//; ta' 

который добавляет следующую строку только в том случае, если в конце он уже сделал > совпадение, и должен условно вернуться назад, чтобы обработать случай последовательных совпадающих строк (это 39% Крюмина). Добавьте строку к следующей, если она заканчивается обратная косая черта «\» точно, за исключением замены символа > для \ в качестве символа объединения, и того факта, что символ соединения сохраняется на выходе).

sed не предоставляет способ испускать выход без окончательной новой строки. Ваш подход с использованием N принципе работает, но хранит неполные строки в памяти и, следовательно, может терпеть неудачу, если линии становятся слишком длинными (sed implentations обычно не предназначены для обработки очень длинных строк).

Вместо этого вы можете использовать awk.

 awk '{if (/<$/) printf "%s", $0; else print}' 

Альтернативным подходом является использование tr для замены символа новой строки «скучным», часто встречающимся символом. Пространство может работать здесь – выберите персонажа, который имеет тенденцию появляться на каждой строке или, по крайней мере, большую часть строк в ваших данных.

 tr ' \n' '\n ' | sed 's/> />/g' | tr '\n ' ' \n' 

как насчет использования ed?

 ed -s test.txt <<< $'/fruits/s/apple/banana/g\nw' 

(через http://wiki.bash-hackers.org/howto/edit-ed )

Я закончил использование gsar, как описано в этом ответе следующим образом:

 gsar -F '-s>:x0A' '-r>' 

Есть много способов сделать это, и большинство из них действительно хороши, но я думаю, что это мой любимый:

 tr '>\n' '\n>' | sed 's/^>*//;H;/./!d;x;y/\n>/>\n/' 

Или даже:

 tr '>\n' '\n>' | sed 's/^>*//' | tr '\n>' '>\n'