Чтение всего файла в шаблонное пространство полезно для замены строк новой строки, & c. и есть много случаев, рекомендующих следующее:
sed ':a;N;$!ba; [commands...]'
Однако, это терпит неудачу, если вход содержит только одну строку.
Например, при двухстрочном вводе каждая строка подвергается команде подстановки:
$ echo $'abc\ncat' | sed ':a;N;$!ba; s/a/xxx/g'
xxxbc
cxxxt
Но, с вводом одной линии, не замена не выполняется:
$ echo 'abc' | sed ':a;N;$!ba; s/a/xxx/g'
abc
Как написать sed
команду, чтобы прочитать все входные данные одновременно и не иметь этой проблемы?
sed -z
опцию GNU . Если ваш файл не имеет нулевого значения, он будет читать до конца файла! Найдено из этого: stackoverflow.com/a/30049447/582917Ответы:
Есть много причин, по которым чтение всего файла в пространство шаблонов может пойти не так. Логическая проблема в вопросе, касающемся последней строки, является обычной. Он связан
sed
с циклом строк - когда больше нет линий иsed
встречает EOF, через который он проходит - он прекращает обработку. И поэтому, если вы находитесь на последней строчке и даете указаниеsed
найти другую, она тут же остановится и больше не будет.Тем не менее, если вам действительно нужно прочитать весь файл в пространстве шаблонов, то, возможно, в любом случае стоит рассмотреть другой инструмент. Дело в том,
sed
что одноименный редактор потоков - он предназначен для одновременной работы строки или логического блока данных.Есть много подобных инструментов, которые лучше оснащены для обработки полных блоков файлов.
ed
иex
, например, может делать многое из того, чтоsed
может делать, и с аналогичным синтаксисом - и многим другим - кроме того, что вместо того, чтобы работать только с входным потоком при преобразовании его в выходной, как этоsed
делается, они также поддерживают временные файлы резервных копий в файловой системе. , Их работа буферизуется на диск по мере необходимости, и они не завершаются внезапно в конце файла (и имеют тенденцию взламываться намного реже при нагрузке буфера) . Более того, они предлагают много полезных функций, которыеsed
не имеют такого рода, которые просто не имеют смысла в контексте потока, такие как отметки строк, отмена, именованные буферы, объединение и многое другое.sed
Основным преимуществом является его способность обрабатывать данные, как только они их читают - быстро, эффективно и в потоке. Когда вы отбрасываете файл, вы его выбрасываете, и вы, как правило, сталкиваетесь с трудностями крайнего случая, такими как проблема последней строки, о которой вы упомянули, переполнением буфера и ужасной производительностью - по мере того, как данные, которые он анализирует, увеличиваются в длине, время обработки обработчиком регулярных выражений при перечислении совпадений увеличивается в геометрической прогрессии .Кстати, об этом последнем пункте: хотя я понимаю, что пример,
s/a/A/g
скорее всего, является просто наивным примером и, вероятно, не является реальным сценарием, для которого вы хотите собрать входные данные, вы, возможно, сочтете целесообразным ознакомиться с ним.y///
, Если вы часто обнаруживаете, чтоg
заменяете одного персонажа другим, то этоy
может быть очень полезно для вас. Это преобразование в противоположность замене, и оно происходит намного быстрее, поскольку не подразумевает регулярное выражение. Этот последний момент также может быть полезен при попытке сохранить и повторить пустые//
адреса, потому что это не влияет на них, но может быть затронуто ими. В любом случае,y/a/A/
это более простой способ сделать то же самое - и обмены возможны так же, как:y/aA/Aa/
который будет переставлять все верхний / нижний регистр, как в строке друг для друга.Вы также должны заметить, что описанное вами поведение на самом деле совсем не то, что должно произойти.
Из GNU
info sed
в разделе ОБЩИЕ ОТЧЕТНЫЕ ОШИБКИ :N
команда в последней строкеБольшинство версий
sed
завершают работу, ничего не печатая, когдаN
команда вводится в последней строке файла. GNUsed
печатает пространство шаблона перед выходом, если, конечно,-n
не был указан переключатель команды. Этот выбор по замыслу.Например, поведение
sed N foo bar
будет зависеть от того, имеет ли foo четное или нечетное количество строк. Или, при написании сценария для чтения следующих нескольких строк после сопоставления с шаблоном, традиционные реализацииsed
заставили бы вас написать что-то вроде,/foo/{ $!N; $!N; $!N; $!N; $!N; $!N; $!N; $!N; $!N }
а не просто/foo/{ N;N;N;N;N;N;N;N;N; }
.В любом случае самый простой обходной путь - это использовать
$d;N
в сценариях, которые полагаются на традиционное поведение, или установитьPOSIXLY_CORRECT
переменную в непустое значение.POSIXLY_CORRECT
Переменная окружения упоминается , потому что POSIX специфицирует , что еслиsed
встречает EOF при попыткеN
он должен бросить курить без выхода, но версия GNU намеренно нарушает со стандартом в этом случае. Также обратите внимание, что даже если поведение оправдано выше, предполагается, что ошибка связана с редактированием потока, а не с сохранением целого файла в памяти.В стандартных определяет
N
«ы поведение , таким образом:N
Добавьте следующую строку ввода, за
\n
исключением завершающей ewline, к пространству шаблона, используя встроенную\n
ewline, чтобы отделить добавленный материал от исходного материала. Обратите внимание, что текущий номер строки изменяется.Если следующая строка ввода недоступна,
N
командный глагол должен перейти к концу сценария и выйти без запуска нового цикла или копирования пространства шаблона в стандартный вывод.На этой ноте в этом вопросе продемонстрированы некоторые другие GNU-измы - в частности, использование скобок
:
метки,b
ранчо и{
контекста функции}
. Как правило, любаяsed
команда, которая принимает произвольный параметр, понимается как разделитель в\n
строке сценария. Итак, команды ...... все они могут работать хаотично в зависимости от
sed
реализации, которая их читает. Портативно они должны быть написаны:То же самое справедливо и для
r
,w
,t
,a
,i
, иc
(и , возможно , несколько больше , что я забываю в данный момент) . Почти в каждом случае они также могут быть написаны:... где новый
-e
оператор\n
xecution заменяет разделитель ewline. Итак, гдеinfo
текст GNU предполагает, что традиционнаяsed
реализация заставит вас сделать :... скорее должно быть ...
... конечно, это тоже не правда. Написание сценария таким образом немного глупо. Есть гораздо более простые способы сделать то же самое, например:
... который печатает:
... потому что команда
t
est - как и большинствоsed
команд - зависит от цикла строки для обновления своего регистра возврата, и здесь циклу строки разрешено выполнять большую часть работы. Это еще один компромисс, который вы заключаете, когда создаете файл, - цикл строки не обновляется никогда, и поэтому многие тесты будут работать ненормально.Приведенная выше команда не рискует перебить ввод, потому что она просто делает несколько простых тестов, чтобы проверить, что она читает, когда она читает. Со
H
старым все строки добавляются в область удержания, но если строка соответствует,/foo/
она перезаписываетh
старое пространство. Затем буферыx
изменяются, иs///
выполняется попытка условной замены, если содержимое буфера соответствует//
последнему адресуемому шаблону. Другими словами,//s/\n/&/3p
попытка заменить третью новую строку в удерживающем пространстве на себя и вывести результаты, если удерживающее пространство в настоящее время совпадает/foo/
. Если этоt
прошло успешно, скрипт переходит на меткуn
otd
elete - что делаетl
ook и оборачивает скрипт.В том случае, если оба
/foo/
и третий символ новой строки не могут быть сопоставлены вместе в удерживающем пространстве, тогда//!g
, если/foo/
не сопоставлено, то буфер будет перезаписан , или, если сопоставлен, будет перезаписан буфер, если\n
строка не соответствует (таким образом, заменяется/foo/
на сам) . Этот небольшой тонкий тест предотвращает ненужное заполнение буфера при длительных отрезках no/foo/
и гарантирует, что процесс останется быстрым, потому что ввод не накапливается. В случае отказа/foo/
или//s/\n/&/3p
сбоя буферы снова меняются местами, и каждая строка, кроме последней, там удаляется.Последняя - последняя строка
$!d
- простая демонстрация того, как можно создать нисходящийsed
скрипт для обработки нескольких случаев. Когда ваш общий метод заключается в удалении нежелательных случаев, начиная с самых общих и работая в направлении наиболее конкретных, тогда крайние случаи могут быть более легко обработаны, потому что им просто разрешено провалиться в конец сценария с вашими другими требуемыми данными, и когда все это окутывает вас теми данными, которые вам нужны. Однако получение таких крайних случаев из замкнутого цикла может быть гораздо более трудным делом.И вот последнее, что я должен сказать: если вы действительно хотите извлечь весь файл, то вы можете сделать немного меньше работы, полагаясь на цикл строки, чтобы сделать это для вас. Как правило, вы должны использовать
N
ext иn
ext для прогнозирования - потому что они опережают цикл строки. Вместо избыточной реализации замкнутого цикла в цикле - посколькуsed
цикл строк в любом случае является просто циклом чтения - если ваша цель - собирать ввод без разбора, тогда, вероятно, это проще сделать:... который соберет весь файл или перестанет пытаться.
примечание о
N
поведении и последней строке ...источник
H
первое место - это прекрасно.:a;$!{N;ba}
как я уже упоминал выше - проще использовать стандартную форму в долгосрочной перспективе, когда вы пытаетесь запустить регулярные выражения в незнакомых системах. Но на самом деле это не то, что я имел в виду: вы реализуете замкнутый цикл - вы не можете так легко попасть в середину этого, когда захотите, как могли бы вместо этого, разветвив - обрезав ненужные данные - и позволив циклу произойти. Это как нисходящая вещь - все, чтоsed
делает, является прямым результатом того, что он только что сделал. Может быть, вы видите это по-другому - но если вы попробуете это, вы обнаружите, что сценарий будет проще.Это терпит неудачу, потому что
N
команда прибывает перед соответствием шаблона$!
(не последняя строка) и sed завершает работу перед выполнением любой работы:Это может быть легко прикреплен к работе с входным сигналом однолинейной, а (и действительно , чтобы быть более ясным в любом случае) просто группирования
N
иb
команды по образцу:Это работает следующим образом:
:a
создать ярлык с именем «а»$!
если не последняя строка, тоN
добавить следующую строку к пробелу шаблона (или выйти, если следующей строки нет) иba
разветвить (перейти к) метку 'a'К сожалению, он не переносим (так как он опирается на расширения GNU), но следующая альтернатива (предложенная @mikeserv) переносима:
источник
:a;N;$!ba;
.