Удалить пустую строку перед шаблоном с помощью sed

#sed

#sed

Вопрос:

Контекст

Например, у меня есть этот тестовый файл foo.py :

 #!/usr/bin/env python3
'''foo'''
# comment
import ...

# [END import]
import ...

# [END import]
import ...
# [END import]
# [END import]
import even...

# [END import]

# [END import]
import odd...

# [END import]

# [END import]
 

Ожидаемый

Я хотел бы удалить пустую строку перед # [END import

 #!/usr/bin/env python3
'''foo'''
# comment
import ...
# [END import]
import ...
# [END import]
import ...
# [END import]
# [END import]
import even...
# [END import]
# [END import]
import odd...
# [END import]
# [END import]
 

Может кто-нибудь дать мне рабочую версию с использованием sed и / или объяснить, почему следующее не сработало

Тест 0

 sed '$!N;s/^n(# [END)/1/g' foo.py
 

Наблюдается

 #!/usr/bin/env python3
'''foo'''
# comment
import ...
# [END import]
import ...

# [END import]
import ...
# [END import]
# [END import]
import even...

# [END import]

# [END import]
import odd...
# [END import]
# [END import]
 

Здесь изменилась только «четная» строка, поскольку здесь мы «потребляем» по две строки за раз
, используя N; без возврата…

Тест 1

 sed ':r;$!{N;br};s/^n(# [END)/1/g' foo.py
 

Наблюдается

ничего не меняется, здесь я не понимаю, почему это не работает (т. Е. Почему Шаблон не соответствует)…

Тест 2

без ^ привязки.

 sed ':r;$!{N;br};s/n(# [END)/1/g' foo.py
 

Наблюдается

 #!/usr/bin/env python3
'''foo'''
# comment
import ...
# [END import]
import ...
# [END import]
import ...# [END import]# [END import]
import even...
# [END import]
# [END import]
import odd...
# [END import]
# [END import]
 

обратите внимание на double #[END в той же строке, что и ожидалось командой, но не на ожидаемый результат.

Тест 3

 sed ':r;$!{N;br};s/n(n# [END)/1/g' foo.py
 

Наблюдается

РАБОТАЕТ, как и ожидалось, НО я не могу понять, почему он может совпадать nn , т.Е. два последовательных возврата

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

1. Похоже, вы удаляете все пустые строки с учетом вашего образца ввода / вывода. Если это так, то это просто grep '.' или sed -n '/./p' или awk 'NF' . Если это не все, что вам нужно, тогда предоставьте более репрезентативный образец ввода / вывода, включающий пустые строки, которые вы не хотите удалять.

Ответ №1:

Вам нужно будет добавить m флаг для теста 1, чтобы ^ $ якоря и соответствовали начальному и конечному местоположениям каждой строки, в противном случае они будут соответствовать началу / концу всей строки. Это предполагает m , что флаг поддерживается вашей реализацией, как GNU sed это делает.

 sed ':r;$!{N;br};s/^n(# [END)/1/mg'
 

Тест 3 работает, потому что перед пустой строкой есть новая строка как часть этой предыдущей строки. Приведенный ниже пример может помочь вам лучше визуализировать его:

 $ printf 'anbncn'
a
b
c
$ printf 'anbnncn'
a
b

c
 

С помощью perl:

 perl -0777 -pe 's/nKn(?=# [END)//g'
 
  • -0777 будет отображать весь ввод в виде одной строки
  • nKn(?=# [END) будет соответствовать новой строке при условии, что до и # [END после этой новой строки есть символ новой строки

Другой вариант с GNU sed , не нужно читать весь файл за один раз.

 sed '/^$/{N; s/n(# [END)/1/; P; D}'
 
  • /^$/ будет соответствовать пустой строке
    • N добавьте следующую строку в пространство шаблона
    • s/n(# [END)/1/ удалите новую строку, если требуется совпадение регулярных выражений

P и D здесь имеют решающее значение, поэтому я процитирую руководство:

P Распечатайте часть пространства шаблона до первой новой строки.

D Если пространство шаблона не содержит новой строки, запустите обычный новый цикл, как если бы была выдана команда d. В противном случае удалите текст в пространстве шаблонов до первой новой строки и перезапустите цикл с результирующим пространством шаблонов, не читая новую строку ввода.

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

1. в тесте 3 сопоставление nna , похоже, потребляет для меня 3 строки, т.Е. Я должен был бы использовать N;N;

2. Я не знаком с /m флагом в sed ; это sed расширение GNU? Справочная страница молчит.

3. @tripleee только что проверил руководство по GNU sed , и там действительно сказано, что это расширение GNU.. кроме того, ` и ' всегда будет соответствовать началу / концу строки

Ответ №2:

Это может сработать для вас (GNU sed):

 sed 'N;/^n# [END import]/!P;D' file
 

Откройте окно из 2 строк по всему файлу, и если первая строка пуста, а вторая строка — # [END import] не печатайте первую строку.

Примечание. Идиома N;...;P;D печатает все строки в файле, но позволяет программисту обрабатывать около 2 строк за раз.