Получение текста от последнего маркера до EOF в POSIX.2

Мне нравятся руководства TLDP http://tldp.org/guides.html

Существует все от Новичков Усовершенствованным руководствам Bash; это - как я учился так или иначе.

8
09.09.2018, 16:02
5 ответов

Если Ваши сегменты не действительно огромны (как в: Вы действительно не можете сэкономить так много RAM, по-видимому, потому что это - крошечная встроенная система, управляющая большой файловой системой), единственная передача является действительно лучшим подходом. Не только, потому что это будет быстрее, но и самое главное потому что это позволяет источнику быть потоком, от которого потеряно любое чтение данных и не сохраненное. Это - действительно задание для awk, хотя sed может сделать это также.

sed -n -e 's/^---$//' -e 't a' \
       -e 'H' -e '$g' -e '$s/^\n//' -e '$p' -e 'b' \
       -e ':a' -e 'h'              # you are not expected to understand this
awk '{if (/^---$/) {chunk=""}      # separator ==> start new chunk
      else {chunk=chunk $0 RS}}    # append line to chunk
     END {printf "%s", chunk}'     # print last chunk (without adding a newline)

Если необходимо использовать подход с двумя передачами, определите смещение строки последнего разделителя и печать от этого. Или определите байтовое смещение и печать от этого.

</input/file tail -n +$((1 + $(</input/file         # print the last N lines, where N=…
                               grep -n -e '---' |   # list separator line numbers
                               tail -n 1 |          # take the last one
                               cut -d ':' -f 1) ))  # retain only line number
</input/file tail -n +$(</input/file awk '/^---$/ {n=NR+1} END {print n}')
</input/file tail -c +$(</input/file LC_CTYPE=C awk '
    {pos+=length($0 RS)}        # pos contains the current byte offset in the file
    /^---$/ {last=pos}          # last contains the byte offset after the last separator
    END {print last+1}          # print characters from last (+1 because tail counts from 1)
')

Приложение: Если у Вас есть больше, чем POSIX, вот простая версия с одной передачей, которая полагается на общее расширение awk, который позволяет разделитель записей RS быть регулярным выражением (POSIX только позволяет отдельный символ). Это не абсолютно корректно: если файл заканчивается разделителем записей, он печатает блок перед последним разделителем записей вместо пустой записи. Второе использование версии RT избегает того дефекта, но RT характерно для GNU awk.

awk -vRS='(^|\n)---+($|\n)' 'END{printf $0}'
gawk -vRS='(^|\n)---+($|\n)' 'END{if (RT == "") printf $0}'
6
27.01.2020, 20:10
  • 1
    @Gilles: sed хорошо работает, но я не могу добраться awk пример для выполнения; это зависает..., и я получаю ошибку в 3-м примере: cut -f ':' -t 1 ... сокращение: недопустимая опция - 't' –  Peter.O 31.03.2011, 22:33
  • 2
    @fred.bear: Я понятия не имею, как это произошло — я протестировал все свои отрывки, но так или иначе испортил редактирование post-copy-paste на cut пример. Я ничего не вижу неправильно с awk пример, какую версию awk Вы используете, и что вводится Ваш тест? –  Gilles 'SO- stop being evil' 31.03.2011, 22:41
  • 3
    ... на самом деле awk версия работает.. это - выступ, занимающий очень долгое время на большом файле.. sed версия обработала тот же файл в 0,470 с.. Мои данные тестирования очень взвешены... только два блока с одиноким '---' три строки от конца 1 миллиона строк... –  Peter.O 31.03.2011, 22:51
  • 4
    @Gilles.. (Я думаю, что должен прекратить тестировать в 3:00. Я так или иначе протестировал все три из "передачи двух" awks как единый блок :(... Я теперь протестировал каждого индивидуально, и второй очень быстр в 0,204 секунды... Howerver, первые awk выводы "с двумя передачами" только: "(стандартный вход)" (-l, кажется, преступник)... что касается третьего awk "с двумя передачами", я ничего не произвожу..., но второй "с двумя передачами" является самым быстрым из всех представленных методик (POSIX или иначе :)... –  Peter.O 01.04.2011, 12:42
  • 5
    @fred.bear: Фиксированный, и зафиксированный. Мой QA не очень хорош для этих коротких отрывков — я обычно, вставка копии из командной строки, формата, затем замечает ошибку и пытается зафиксировать встроенный, а не переформатировать. Мне любопытно видеть, более ли подсчет символов эффективен, чем подсчет строк (2-й по сравнению с 3-ми методами с двумя передачами). –  Gilles 'SO- stop being evil' 01.04.2011, 14:34

Две стратегии передачи, кажется, правильная вещь. Вместо sed я использовал бы awk(1). Две передачи могли быть похожими на это:

$ LINE=`awk '/^---$/{n=NR}END{print n}' file`

получить номер строки. И затем повторите весь текст, начинающий с того номера строки с:

$ awk "NR>$LINE" file

Это не должно требовать чрезмерной буферизации.

3
27.01.2020, 20:10
  • 1
    и затем они могут быть объединены: awk -v line=$(awk '/^---$/{n=NR}END{print n}' file) 'NR>line' file –  glenn jackman 31.03.2011, 23:59
  • 2
    Видение, что я был время, тестируя другие представления, я теперь также протестировал "jackman's долины реки" выше отрывка. Требуется 0,352 секунды (с тем же файлом данных, упомянутым в моем ответе)... Я начинаю получать сообщение, что awk может быть быстрее, чем я первоначально думал возможный (я думал, что sed был почти так хорош, как это добралось, но это, кажется, случай "лошадей для курсов")... –  Peter.O 01.04.2011, 12:58
  • 3
    Очень интересный видеть все эти сравниваемые сценарии. Хорошая работа Fred. –  Mackie Messer 01.04.2011, 13:39
  • 4
    Быстрые решения используют tac и хвост, которые на самом деле читают входной файл назад. Теперь, если только awk мог бы считать входной файл назад... –  Mackie Messer 01.04.2011, 13:46
lnum=$(($(sed -n '/^---$/=' file | sed '$!d') +1)); sed -n "${lnum},$ p" file 

Первое sed номера строки ouputs "---" строк...
Второе sed извлекает последнее число из вывода первого sed...
Добавьте 1 к тому числу для получения запуска "ccc" блока...
Третьи 'sed' выводы от запуска "ccc" блока к EOF

Обновление (с исправленным информационным ре методы Gilles)

Хорошо я был wondereing о как glenn jackman's tac работал бы, таким образом, я испытанный три ответа (во время записи)... Тестовый файл (файлы) каждый содержал 1 миллион строк (их собственных номеров строки).
Все ответы сделали то, что ожидалось...

Вот времена..


Gilles sed (единственная передача)

# real    0m0.470s
# user    0m0.448s
# sys     0m0.020s

Gilles awk (единственная передача)

# very slow, but my data had a very large data block which awk needed to cache.

Gilles,' с двумя передачами' (первый метод)

# real    0m0.048s
# user    0m0.052s
# sys     0m0.008s

Gilles,' с двумя передачами' (второй метод)... очень быстро

# real    0m0.204s
# user    0m0.196s
# sys     0m0.008s

Gilles,' с двумя передачами' (третий метод)

# real    0m0.774s
# user    0m0.688s
# sys     0m0.012s

Gilles 'таращит глаза' (метод RT)... очень быстро, но не POSIX.

# real    0m0.221s
# user    0m0.200s
# sys     0m0.020s

glenn jackman... очень быстро, но не POSIX.

# real    0m0.022s
# user    0m0.000s
# sys     0m0.036s

fred.bear

# real    0m0.464s
# user    0m0.432s
# sys     0m0.052s

Mackie Messer

# real    0m0.856s
# user    0m0.832s
# sys     0m0.028s
3
27.01.2020, 20:10
  • 1
    Из любопытства, которое из моих версий с двумя передачами Вы тестировали, и какую версию awk Вы использовали? –  Gilles 'SO- stop being evil' 01.04.2011, 02:02
  • 2
    @Gilles: Я использовал GNU Awk 3.1.6 (в Ubuntu 10.04 с 4 ГБ RAM). Все тесты имеют 1 миллион строк в первом "блоке", затем "маркер", сопровождаемый 2 строками "данных"... Потребовалось 15,540 секунд для обработки меньшего файла 100 000 строк, но для этих 1 000 000 строк, я выполняю его теперь, и это были больше чем 25 минут до сих пор. Это использует одно ядро для 100%... уничтожающих его теперь... Вот еще некоторые возрастающие тесты: lines=100000 (0m16.026s) - lines=200000 (2m29.990 s) - lines=300000 (5m23.393 s) - lines=400000 (11m9.938 s) –  Peter.O 01.04.2011, 08:05
  • 3
    Ой.. В моем выше комментария я пропустил Вашу awk ссылку "с двумя передачами". Вышеупомянутая деталь для "однопроходного" awk... awk версия правильна... Я сделал дальнейшее ре комментария различными версиями "с двумя передачами" в соответствии с Вашим ответом (измененный результаты времени выше) –  Peter.O 01.04.2011, 12:45

Используйте "tac", который производит строки файла от конца до начала:

tac afile | awk '/---/ {exit} {print}' | tac
2
27.01.2020, 20:10
  • 1
    tac не POSIX, это является определенным для Linux (это находится в GNU coreutils, и в некоторых busybox установках). –  Gilles 'SO- stop being evil' 31.03.2011, 21:54

Можно просто использоватьed

ed -s infile <<\IN
.t.
1,?===?d
$d
,p
q
IN

Как это работает:tдублирует текущую(.)строку -, которая всегда является последней строкой при запуске ed(на тот случай, если разделитель присутствует в последней строке ), 1,?===?dудаляет все строки до предыдущего совпадения включительно(edостается в последней строке )затем $dудаляет (дубликат )последнюю строку, ,pпечатает текстовый буфер (заменить с wдля редактирования файла на месте )и, наконец, qвыход ed.


Если вы знаете, что во входных данных есть хотя бы один разделитель (, и вам все равно, напечатан ли он ), тогда

sed 'H;/===/h;$!d;x' infile

будет короче.
Как это работает :добавляет все строки в Hстарый буфер, перезаписывает hстарый буфер при обнаружении совпадения, dудаляет все строки, кроме одной $t, когда он xизменяет буферы (и автопечатает ).

1
27.01.2020, 20:10

Теги

Похожие вопросы