Я согласен с вами - это , вероятно , является общая проблема. У некоторых общих утилит есть некоторые средства для обработки этого, все же.
nl
nl, Например, разделяет входной сигнал в логические страницы , как -delimited на два символа секции разделителя . Три вхождения в одной строке указывают начало заголовка , два тела и один нижний колонтитул . Он заменяет любой из них, найденных на входе, пустой строкой на выходе - это единственные пустые строки, которые он печатает
Я изменил ваш пример, включив в него другой раздел и вставив его ./infile. Так это выглядит так:
line A
line B
@@inline-code-start
line X
line Y
line Z
@@inline-code-end
line C
line D
@@start
line M
line N
line O
@@end
Затем я запустил следующее:
sed 's/^@@.*start$/@@@@@@/
s/^@@.*end$/@@/' <infile |
nl -d@@ -ha -bn -w1
nlможно сказать, чтобы накапливать состояние по логическим страницам, но это не по умолчанию. Вместо этого он будет нумеровать строки своего ввода в соответствии со стилями и секциями . Так -haозначает номер всех строк заголовка и -bnозначает отсутствие строк тела - как это начинается в теле .
До тех пор, пока я не узнал об этом, я использовал nlдля любого ввода, но после осознания того, что это nlможет исказить вывод в соответствии с его -dограничителем по умолчанию, \:я научился быть с ним более осторожным и grep -nF ''вместо этого начал использовать для непроверенного ввода. Но еще один урок, извлеченный в тот день, заключался в том, что он nlможет быть очень полезен в других отношениях - например, в этом - если вы просто немного измените его входные данные - как я делаю sedвыше.
ВЫХОД
line A
line B
1 line X
2 line Y
3 line Z
line C
line D
1 line M
2 line N
3 line O
Вот еще немного о том nl, замечаете ли вы выше, как все строки, кроме пронумерованных, начинаются с пробелов? когдаnl номера строк, он вставляет определенное количество символов в голову каждого. Для этих строк он не -wнумеруется - даже пробелы - он всегда соответствует отступу, вставляя ( idth count + -separator len) * пробелы в начале ненумерованных строк. Это позволяет точно воспроизводить ненумерованный контент, сравнивая его с пронумерованным контентом - и при этом не прилагая особых усилий. Если вы считаете, что nlэто разделит его входные данные на логические разделы, и что вы можете вставить произвольные -sтрэны в начало каждой строки, которую он нумерует, тогда становится довольно легко обработать его вывод:
sed 's/^@@.*start$/@@@@@@/
s/^@@.*end/@@/; t
s/^\(@@\)\{1,3\}$/& /' <infile |
nl -d@@ -ha -bn -s' do something with the next line!
'
Вышеуказанные отпечатки ...
line A
line B
1 do something with the next line!
line X
2 do something with the next line!
line Y
3 do something with the next line!
line Z
line C
line D
1 do something with the next line!
line M
2 do something with the next line!
line N
3 do something with the next line!
line O
GNU sed
Если nlэто не ваше целевое приложение, тогда GNUsed может eвыполнить для вас произвольную команду оболочки в зависимости от соответствия.
sed '/^@@.*start$/!b
s//nl <<\\@@/;:l;N
s/\(\n@@\)[^\n]*end$/\1/
Tl;e' <infile
Выше sedвводятся входные данные в пространстве шаблонов, пока их не будет достаточно, чтобы успешно пройти замену Test и прекратить bранчо обратно к :lабелю. Когда это произойдет, этоe выполняет nlввод, представленный <<здесь как документ для всего остального пространства шаблонов.
Рабочий процесс выглядит так:
/^@@.*start$/!b
- если
^вся линия $никак !не /соответствует /указанной выше модели, то она bразводят из сценария и autoprinted - так что с этого момента мы работаем только с серией линий , которая началась с рисунком.
s//nl <<\\@@/
- пустое
s//поле /соответствует последнему адресу, который sedпытался найти соответствие, поэтому вместо него эта команда заменяет всю @@.*startстроку nl <<\\@@.
:l;N
- Команда
:определяет метку ветки - здесь я установил один с именем :label. Команда Next добавляет следующую строку ввода к \nпробелу шаблона, за которой следует символ ewline. Это один из немногих способов получить \newline в sedпространстве паттернов - \nперсонаж ewline является верным разделителем для sedдер, который делал это некоторое время.
s/\(\n@@\)[^\n]*end$/\1/
- эта
s///операция может быть успешной только после того, как встречается начало, и только в первом последующем появлении конечной строки. Он будет действовать только на пространство паттернов, в котором \nсразу же за конечной ewline будет отмечен @@.*endсамый конец $паттерна. Когда он действует, он заменяет всю совпавшую строку \1первой \(группой \)или \n@@.
Tl
- то
Tкоманда Текущей ветви к метке (если имеется) , если успешная замена не произошла с момента последнего ввод линия затащила шаблон (как и я ш / N) . Это означает, что каждый раз, когда \newline добавляется в пространство шаблонов, которое не соответствует вашему конечному разделителю, команда Test завершается неудачно и возвращается к :label, что приводит к sedвытягиванию Nстроки ext и повторению цикла до успешного завершения .
e
Когда замена для конечного матча успешно и сценарий не филиальную назад для неисправного TЭСТА, sedбудет execute команды , которая looks , как это:
nl <<\\@@\nline X\nline Y\nline Z\n@@$
Вы можете убедиться в этом сами, отредактировав последнюю строку там, чтобы она выглядела так Tl;l;e.
Это печатает:
line A
line B
1 line X
2 line Y
3 line Z
line C
line D
1 line M
2 line N
3 line O
while ... read
Последний способ сделать это, и, возможно, самый простой, - это использовать while readцикл, но не без причины. Оболочка - (особенно bashоболочка) - обычно ужасна при обработке ввода в больших количествах или в устойчивых потоках. Это также имеет смысл - работа оболочки заключается в том, чтобы обрабатывать ввод за символом и вызывать другие команды, которые могут обрабатывать больше.
Но важно то, что его роль заключается в том, что оболочка не должна read перегружать ввод - она указана для того, чтобы не буферизовать ввод или вывод до такой степени, что она потребляет так много или недостаточно ретранслирует во времени, чтобы не вызывать недостающие команды, которые она вызывает - к байту. Так readчто это отличный тест ввода - для получения returnинформации о том, остался ли ввод, и вам нужно вызвать следующую команду, чтобы прочитать его, но в целом это не лучший способ.
Однако вот пример того, как можно использовать read и другие команды для обработки ввода синхронно:
while IFS= read -r line &&
case $line in (@@*start) :;; (*)
printf %s\\n "$line"
sed -un "/^@@.*start$/q;p";;
esac;do sed -un "/^@@.*end$/q;=;p" |
paste -d: - -
done <infile
Первое, что происходит для каждой итерации, - это readвытягивание строки. Если он успешен, это означает, что цикл еще не достиг EOF, и, таким образом, caseон соответствует начальному разделителю, и doблок немедленно выполняется. Остальное printfпечатает $lineоноread и sedназывается.
sedбудет pнабирать каждую строку, пока не встретит начальный маркер - когда он qполностью использует ввод. Переключатель -unbuffered необходим для GNU, sedпотому что он может жадно буферизовать в противном случае, но - согласно спецификации - другой POSIXsed должны работать без какого-либо специального рассмотрения - при условии, что <infileэто обычный файл.
При первом sed qиспользовании оболочка выполняет doблок цикла, который вызывает другой, sedкоторый печатает каждую строку, пока не встретит маркер конца . Он передает свой вывод paste, потому что он печатает номера строк, каждый на своей строке. Как это:
1
line M
2
line N
3
line O
pasteзатем вставляет их вместе в :символы, и весь вывод выглядит так:
line A
line B
1:line X
2:line Y
3:line Z
line C
line D
1:line M
2:line N
3:line O
Это всего лишь примеры - все может быть сделано здесь либо в тесте, либо в блоках do, но первая утилита не должна потреблять слишком много входных данных.
Все задействованные утилиты читают один и тот же ввод - и печатают свои результаты - каждая по-своему. Такого рода вещи могут быть трудно получить навык - потому что различные утилиты будут помещать в буфер больше , чем другие , - но вы можете вообще полагаться на dd, headи sedделать правильные вещи (хотя, для GNU sed, вам нужно CLI-переключатель) и Вы всегда должны быть в состоянии положиться, readпотому что это, по своей природе, очень медленно . И именно поэтому вышеуказанный цикл вызывает его только один раз на входной блок.
nlне должен накапливать государство . Посмотритеnl -dи проверьтеman/infoстраницы для получения информации оnl«S разделе разделителем .