Является ли предоставленный вами oneliner действительно тем, что вы хотели? Особенно так как вы упоминаете честный образец. Потому что, как он есть сейчас, он подает patterns.txt
в xargs
..., что будет продолжаться и будет вызывать sed
для каждого шаблона индивидуально, один за другим. И весь вывод xargs
подается на головку, которая прерывает его после n
строк. Другими словами, ваш первый шаблон уже может исчерпать все строки, которые вы хотели видеть, даже если другие шаблоны могли совпадать сколько угодно раз на линиях, встречающихся перед представленными вам совпадениями. Подробный пример между горизонтальными линейками.
Если у меня есть patterns.txt
из:
_Pat1
_Pat2
_Pat3
А bigtext.txt
с:
1matchx_Pat1x
2matchx_Pat2x
2matchy_Pat2y
2matchz_Pat2z
3matchx_Pat3x
3matchy_Pat3y
3matchz_Pat3z
1matchy_Pat1y
1matchz_Pat1z
И я запускаю ваш oneliner, ограниченный пятью ударами, я не получаю результат (первые пять совпадений для всех трех шаблонов, найденных в файле):
1matchx
2matchx
2matchy
2matchz
3matchx
Но (все (3) патчи для _Pat1
плюс 2 совпадения для _Pat2
, после которых у меня закончились выходные строки):
1matchx
1matchy
1matchz
2matchx
2matchy
Теперь к вашей проблеме производительности, которая частично связана. Я должен признать, что я не мог воспроизвести это. Я взял ваш пример из комментария, взорвал "большой" файл размером до 1 ГБ, повторив шаблон, и запустил свой oneliner:
$ time { cat patterns.txt | xargs -I '{}' stdbuf -oL sed -n 's/{}.*$//p' bigtext.txt | head -5 ; }
1aaa
2aaabbb
3aaaccc
1aaa
2aaabbb
xargs: stdbuf: terminated by signal 13
real 0m0.012s
user 0m0.013s
sys 0m0.008s
Заметьте, я опустил -eL
, stderr обычно не буферизован (это то, что вы обычно хотите) и на самом деле здесь не играет никакой роли. Также обратите внимание, что я выполнил stdbuf
без префикса "g", который говорит мне, что вы, вероятно, находитесь в системе, где инструменты GNU не используются по умолчанию ... и, вероятно, причины, по которым вы получаете другое поведение. Я попытаюсь объяснить, что происходит, и рискну немного догадаться ... и в заключение предложу. Также обратите внимание, что мне действительно не нужно было использовать stdbuf
(манипулирование буферизацией) вообще, или, скорее, это не оказало заметного влияния на результат, но опять же, это могло бы зависеть от платформы и инструментов (а также сценария).
Когда вы читаете строку с ее конца, head
читает стандартный ввод по мере его поступления из xargs
(и, как следствие, запускается sed
(или stdbuf
), который xargs
разветвляется, они оба прикреплены к концу записи), пока не будет достигнут предел количества строк для печати, а затем head
завершится. Это «разрывает» канал и xargs
и sed
(или stdbuf
, в который он был обернут) получает сигнал SIGPIPE
, и по умолчанию они также завершаются (что вы можете увидеть в выходных данных моего прогона: xargs: stdbuf: terminated by signal 13
).
Что делает stdbuf -oL
и почему кто-то мог это предложить. Если больше не использовать консоль для чтения / записи, которая обычно бывала буферизованной строкой, и использовать каналы, мы вместо этого обычно видим буферизованный ввод-вывод. stdbuf -oL
изменяет это обратно на строку с буферизацией. Без этого задействованный процесс мог бы обмениваться данными большими блоками, и это могло бы занять head
дольше, чтобы понять, что это делается и не требует дополнительного ввода, в то время как sed
продолжает работать, чтобы увидеть, есть ли еще какие-либо подходящие совпадения. Как уже упоминалось, в моих системах (буфер 4K) и на этом примере (с повторяющимся шаблоном) это не имело никакого значения. Также обратите внимание, что, хотя это снижает риск того, что мы не узнаем, что мы можем это сделать, буферизация строки увеличивает накладные расходы, связанные с обменом данными между процессами.
Так почему же эти механики не дают ожидаемых для вас результатов? На ум приходит пара вариантов:
- , так как вы запускаете и запускаете
sed
один раз для каждого шаблона, каждый раз целый файл. Может случиться так, что вы получите серию из нескольких прогонов без каких-либо попаданий. Я предполагаю, что это действительно так.
- , так как вы даете
sed
файл для чтения, у вас может быть другая реализация sed
, которая пытается прочитать намного больше, прежде чем предпринимать действия с содержимым файла (мой читает 4K за раз). Не вероятная причина, но в теории вы могли бы также кормить sed
построчно, чтобы заставить меньшие куски и получить это SIGPIPE
раньше.
Теперь, предполагая, что последовательное сопоставление с шаблоном на самом деле нежелательно, сводка всего вышеперечисленного будет такой: сначала обработайте ваши шаблоны в один, а затем выполните один проход по «большому» файлу (опционально ограничивая вывод курс). Возможно, стоит перейти от оболочки в основном к чему-то более удобному в использовании, или, по крайней мере, не сохранять формат oneliner, который может привести к путанице.
Не соответствует моей собственной рекомендации, скрипт awk
, который называется так, печатает первые 5 попаданий и завершает работу:
awk -v patts="$(cat patterns.txt)" -v last=5 'BEGIN{patts="(" patts ; gsub(/\n/, "|", patts) ; sub(/.$/, ")", patts); cnt=1 ;} $0~patts{sub(patts ".*", ""); print; cnt++;} cnt>last{exit;}' bigtext.txt