Есть много причин, по которым чтение всего файла в пространство шаблонов может пойти не так. Логическая проблема в вопросе, касающемся последней строки, является обычной. Он связан 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
в разделе ОБЩИЕ ОТЧЕТНЫЕ ОШИБКИ :
POSIXLY_CORRECT
Переменная окружения упоминается , потому что POSIX специфицирует , что если sed
встречает EOF при попытке N
он должен бросить курить без выхода, но версия GNU намеренно нарушает со стандартом в этом случае. Также обратите внимание, что даже если поведение оправдано выше, предполагается, что ошибка связана с редактированием потока, а не с сохранением целого файла в памяти.
В стандартных определяет N
«ы поведение , таким образом:
N
Добавьте следующую строку ввода, за \n
исключением завершающей ewline, к пространству шаблона, используя встроенную \n
ewline, чтобы отделить добавленный материал от исходного материала. Обратите внимание, что текущий номер строки изменяется.
Если следующая строка ввода недоступна, N
командный глагол должен перейти к концу сценария и выйти без запуска нового цикла или копирования пространства шаблона в стандартный вывод.
На этой ноте в этом вопросе продемонстрированы некоторые другие GNU-измы - в частности, использование скобок :
метки, b
ранчо и {
контекста функции }
. Как правило, любая sed
команда, которая принимает произвольный параметр, понимается как разделитель в \n
строке сценария. Итак, команды ...
:arbitrary_label_name; ...
b to_arbitrary_label_name; ...
//{ do arbitrary list of commands } ...
... все они могут работать хаотично в зависимости от sed
реализации, которая их читает. Портативно они должны быть написаны:
...;:arbitrary_label_name
...;b to_arbitrary_label_name
//{ do arbitrary list of commands
}
То же самое справедливо и для r
, w
, t
, a
, i
, и c
(и , возможно , несколько больше , что я забываю в данный момент) . Почти в каждом случае они также могут быть написаны:
sed -e :arbitrary_label_name -e b\ to_arbitary_label_name -e \
"//{ do arbitrary list of commands" -e \}
... где новый -e
оператор \n
xecution заменяет разделитель ewline. Итак, где info
текст GNU предполагает, что традиционная sed
реализация заставит вас сделать :
/foo/{ $!N; $!N; $!N; $!N; $!N; $!N; $!N; $!N; $!N }
... скорее должно быть ...
/foo/{ $!N; $!N; $!N; $!N; $!N; $!N; $!N; $!N; $!N
}
... конечно, это тоже не правда. Написание сценария таким образом немного глупо. Есть гораздо более простые способы сделать то же самое, например:
printf %s\\n foo . . . . . . |
sed -ne 'H;/foo/h;x;//s/\n/&/3p;tnd
//!g;x;$!d;:nd' -e 'l;$a\' \
-e 'this is the last line'
... который печатает:
foo
.
.
.
foo\n.\n.\n.$
.$
this is the last line
... потому что команда t
est - как и большинство sed
команд - зависит от цикла строки для обновления своего регистра возврата, и здесь циклу строки разрешено выполнять большую часть работы. Это еще один компромисс, который вы заключаете, когда создаете файл, - цикл строки не обновляется никогда, и поэтому многие тесты будут работать ненормально.
Приведенная выше команда не рискует перебить ввод, потому что она просто делает несколько простых тестов, чтобы проверить, что она читает, когда она читает. Со H
старым все строки добавляются в область удержания, но если строка соответствует, /foo/
она перезаписывает h
старое пространство. Затем буферы x
изменяются, и s///
выполняется попытка условной замены, если содержимое буфера соответствует //
последнему адресуемому шаблону. Другими словами, //s/\n/&/3p
попытка заменить третью новую строку в удерживающем пространстве на себя и вывести результаты, если удерживающее пространство в настоящее время совпадает /foo/
. Если это t
прошло успешно, скрипт переходит на метку n
ot d
elete - что делает l
ook и оборачивает скрипт.
В том случае, если оба /foo/
и третий символ новой строки не могут быть сопоставлены вместе в удерживающем пространстве, тогда //!g
, если /foo/
не сопоставлено, то буфер будет перезаписан , или, если сопоставлен, будет перезаписан буфер, если \n
строка не соответствует (таким образом, заменяется /foo/
на сам) . Этот небольшой тонкий тест предотвращает ненужное заполнение буфера при длительных отрезках no /foo/
и гарантирует, что процесс останется быстрым, потому что ввод не накапливается. В случае отказа /foo/
или //s/\n/&/3p
сбоя буферы снова меняются местами, и каждая строка, кроме последней, там удаляется.
Последняя - последняя строка $!d
- простая демонстрация того, как можно создать нисходящий sed
скрипт для обработки нескольких случаев. Когда ваш общий метод заключается в удалении нежелательных случаев, начиная с самых общих и работая в направлении наиболее конкретных, тогда крайние случаи могут быть более легко обработаны, потому что им просто разрешено провалиться в конец сценария с вашими другими требуемыми данными, и когда все это окутывает вас теми данными, которые вам нужны. Однако получение таких крайних случаев из замкнутого цикла может быть гораздо более трудным делом.
И вот последнее, что я должен сказать: если вы действительно хотите извлечь весь файл, то вы можете сделать немного меньше работы, полагаясь на цикл строки, чтобы сделать это для вас. Как правило, вы должны использовать N
ext и n
ext для прогнозирования - потому что они опережают цикл строки. Вместо избыточной реализации замкнутого цикла в цикле - поскольку sed
цикл строк в любом случае является просто циклом чтения - если ваша цель - собирать ввод без разбора, тогда, вероятно, это проще сделать:
sed 'H;1h;$!d;x;...'
... который соберет весь файл или перестанет пытаться.
примечание о N
поведении и последней строке ...
Хотя у меня нет инструментов, доступных для тестирования, учтите , что N
при чтении и редактировании на месте происходит другое поведение, если отредактированный файл является файлом сценария для следующего чтения.