Удалить последнюю часть файла по шаблону — удалить последний сертификат из цепочки

#awk #sed #ack

#awk #sed #ack

Вопрос:

У меня есть цепочка SSL-сертификатов, подобных этой

 -----BEGIN CERTIFICATE-----
MIICPjCCAeSgAwIBAgIRALMMpKnhRM2C7mnKI/rl8ggwCgYIKoZIzj0EAwIwgY4x
CERT1
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDIjCCAsegAwIBAgIOAMjnPM1wShDmOWUELuIwCgYIKoZIzj0EAwIwgagxCzAJ
CERT2
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDIDCCAsWgAwIBAgIOAMjnPL8JUbVSmpMadWUwCgYIKoZIzj0EAwIwbDELMAkG
CERT3
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDBjCCAqygAwIBAgIFFRCCEwYwCgYIKoZIzj0EAwIwgZQxFDASBgNVBAoMC0Ft
CERT4
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDNjCCAtugAwIBAgIJAKpBxYNyH8biMAoGCCqGSM49BAMCMIGUMRQwEgYDVQQK
CERT5
-----END CERTIFICATE-----
  

и мне нужно удалить из него последний сертификат.

В macOS / BSD команда split имеет флаг -p для разделения по шаблону, и я использовал его:

 cat cert | split -p "-----BEGIN CERTIFICATE-----" 
cat xa{a,b,c,d}
  

Я полагаю, что в Linux тоже есть команда для выполнения этого в одной строке, но в Ubuntu команда split не может разделяться по шаблону.

Мне нужно выполнить задание, используя стандартные команды Linux, такие как те, которые я пометил.

Ответ №1:

Этого решения GNU Sed должно быть достаточно:

 sed -zE 's/(.*n)-----BEGIN CERTIFICATE-----.*/1/' your_input
  
  • -E позволяет использовать (…) вместо (…) захвата чего-либо;
  • -z (доступно в GNU Sed) заключается в обработке всего ввода как одной длинной строки со встроенными n s.

Поэтому первый .* соответствует столько, сколько может (и захватывает его вместе с n правом после него, чтобы он мог ссылаться на него при замене с помощью using 1 ), если за ним следует n-----BEGIN CERTIFICATE----- и что-либо еще после него (второе .* ).

Комментарии:

1. он удаляет последний -----END CERTIFICATE----- , но уверен, что он работает. Спасибо.

2. @kyb, ты не хочешь его удалить? Тогда, пожалуйста, укажите желаемый результат в вашем вопросе.

3. Эта команда выводит 4 сертификата, удаляя 5-й. Это то, что я ищу. Но 4-й сертификат выглядит сломанным, потому что у него нет —— КОНЕЧНОГО СЕРТИФИКАТА —— . Я хотел бы видеть 4 действительных сертификата на выходе. (Конечно, его легко добавить вручную с echo помощью)

4. @kyb Я исправил это (по предложению Эда, честно). В основном у меня был (.*)n , который не захватывал, удаляя, таким образом, последнюю новую строку в EOF, что требуется некоторыми редакторами VS (VS = очень глупо); изменение этого на (.*n) сохранение последней новой строки в захваченной группе и, следовательно, в выходных данных, что делает этих редакторов счастливыми.

5. да. теперь все работает так, как ожидалось. Спасибо. Я не знал об -z этом раньше.

Ответ №2:

Только с любым awk:

 $ awk '/-----BEGIN CERTIFICATE-----/{printf "%s", rec; rec=""} {rec=rec $0 ORS}' file
-----BEGIN CERTIFICATE-----
MIICPjCCAeSgAwIBAgIRALMMpKnhRM2C7mnKI/rl8ggwCgYIKoZIzj0EAwIwgY4x
CERT1
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDIjCCAsegAwIBAgIOAMjnPM1wShDmOWUELuIwCgYIKoZIzj0EAwIwgagxCzAJ
CERT2
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDIDCCAsWgAwIBAgIOAMjnPL8JUbVSmpMadWUwCgYIKoZIzj0EAwIwbDELMAkG
CERT3
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDBjCCAqygAwIBAgIFFRCCEwYwCgYIKoZIzj0EAwIwgZQxFDASBgNVBAoMC0Ft
CERT4
-----END CERTIFICATE-----
  

или, если у вас есть tac :

 $ tac file | awk 'f; /-----BEGIN CERTIFICATE-----/{f=1}' | tac
-----BEGIN CERTIFICATE-----
MIICPjCCAeSgAwIBAgIRALMMpKnhRM2C7mnKI/rl8ggwCgYIKoZIzj0EAwIwgY4x
CERT1
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDIjCCAsegAwIBAgIOAMjnPM1wShDmOWUELuIwCgYIKoZIzj0EAwIwgagxCzAJ
CERT2
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDIDCCAsWgAwIBAgIOAMjnPL8JUbVSmpMadWUwCgYIKoZIzj0EAwIwbDELMAkG
CERT3
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDBjCCAqygAwIBAgIFFRCCEwYwCgYIKoZIzj0EAwIwgZQxFDASBgNVBAoMC0Ft
CERT4
-----END CERTIFICATE-----
  

Комментарии:

1. Есть ли у вас какие-либо идеи, на что жалуется OP с этим выводом? (См. Их Комментарий под моим вопросом.)

2. @Enrico Я подозреваю, что они используют редактор или какой-либо другой инструмент для проверки вывода, который отбрасывал / скрывал последнюю строку вашего вывода, потому что она не заканчивалась новой строкой (я знаю, что вы исправили это сейчас), поэтому для них эта последняя конечная строка отсутствовала.

3. Оооооо, я думал, что они ссылаются на исходную последнюю строку! Очевидно, они имели в виду ошибку, которую вы исправили в моем коде. Тогда еще раз спасибо.

4. Пожалуйста. Я просто предполагаю, что это то, о чем это было, конечно, но это кажется вероятным, и я не могу думать ни о чем другом!

Ответ №3:

Используя GNU awk gensub , вы можете попробовать следующее, написанное и протестированное только на основе показанных образцов.

 awk -v RS="" -v regex="(.*)n(-----BEGIN CERTIFICATE-----.*)" '
{
  print gensub(regex,"\1","1",$0)
}' Input_file
  

Комментарии:

1. Насколько я понимаю, RS =»» для «slurp mode», поэтому $ 0 содержит полный входной файл. Верно?

2. @kyb нет, RS="" (или RS='' ) для «режима абзаца», где записи разделяются пустыми строками. RS='^$' (если ваша версия awk поддерживает RS с несколькими символами) предназначен для «режима slurp», где $ 0 содержит полный входной файл. Они ведут себя аналогично, когда входные данные не содержат пустых строк (что, вероятно, является вашей ситуацией), за исключением того, что с RS="" $0 не будет содержать последнюю новую строку в файле, в то время как с RS='' ним будет. Чтобы увидеть разницу, попробуйте echo 7 | awk -v RS='' '{print "<" $0 ">"}' и echo 7 | awk -v RS='^$' '{print "<" $0 ">"}' и обратите внимание на новую строку раньше > в первом выводе.

3. @EdMorton, спасибо, сэр, за разъяснение, извините, что я неправильно понял.