Поиск и замена строк ПОСЛЕ совпадения с регулярным выражением с использованием «sed»

Кажется, вы хотите обрезать почтовые ящики для всех пользователей. Вы не можете сделать это, вызвав :из find, так как :не является внешней утилитой (, это оболочка, встроенная -в утилиту ). Использование trueвместо :сработало бы, так как это обычно доступно как внешняя команда, но...

Вы также не можете использовать перенаправление в команде, выполняемой через -exec, так как это перенаправление будет задействовано оболочкой во время первого запуска утилиты find(не один раз для каждого найденного файла ).

Ваша команда практически такая же, как

find /var/spool/mail/ -regextype sed -regex "^.*[^/]$" -exec : \; >{}

т. е. будет создан файл с именем {}, в который перенаправляется стандартный поток вывода find.

Вместо этого вы можете сделать что-то вроде

find /var/spool/mail -type f -exec sh -c '
    for pathname do
        : >"$pathname"
    done' sh {} +

Как «один -лайнер»:

find /var/spool/mail -type f -exec sh -c 'for pathname do : >"$pathname"; done' sh {} +

Или, если все почтовые ящики находятся непосредственно под /var/spool/mail,

for pathname in /var/spool/mail/*; do
    : >"$pathname"
done

Как «один -лайнер»:

for pathname in /var/spool/mail/*; do : >"$pathname"; done

В обоих этих вариантах утилита :вызывается правильно, и перенаправление будет происходить для каждого найденного имени пути (любого обычного файла в указанном пути поиска или ниже него ).

Похожие:

0
16.11.2021, 02:37
3 ответа

Создать счетчик в буфере (пространстве шаблонов )и проверять его на каждой строке:

sed '
/^line 1/{x;/11/!s/^/1/;x}
x;/11/{x;b1};x;b
:1;s/^line 3$/replaced/
' file

используем 1в качестве счетчика

[обновление] Повышение производительности:

sed '
/^line 1$/{x;/11/!s/^/1/;x}
/^line 3$/!b
x;/11/!{x;b}
x;s/.*/replaced/
' file

Счетчик проверяется только в строках, соответствующих шаблону-/^line 3$/

1
16.11.2021, 04:33

Использование GNU sedв частности:

sed -e '0,/^line 1$/ b' \
    -e '//,$ s/^line 3$/replaced/' file

Сначала выводятся все строки между началом файла и первой строкой line 1. Использование 0в качестве начала диапазона адресов требует GNU sedи позволяет нам справиться со случаем, когда line 1является самой первой строкой файла.

Достигнув первой строки line 1и выведя ее, он применяет замену ко всем строкам со следующей строки line 1до конца файла, заменяя строки line 3текстом replaced.

Если вы хотите вставить несколько строк текста, используйтеc(изменить ), а неs(заменить):

sed -e '0,/^line 1$/ b' \
    -e '//,$ {' \
      -e '/^line 3$/ c\' \
      -e 'replace 1\' \
      -e 'replace 2' \
    -e '}' file

Каждая строка, кроме последней, которой вы хотите заменить строки line 3, должна заканчиваться обратной косой чертой.

Может быть проще увидеть, что делает этот sedскрипт, если мы напишем скрипт как отдельный скрипт редактирования:

0,/^line 1$/ b

//,$ {
        /^line 3$/ c\
replaced 1\
replaced 2
}

Используя ed, мы могли бы найти первую line 1, а затем выполнить командуc(change )для каждой строки, соответствующей line 3, начиная со следующей строки, соответствующей line 1, и до конца.

/^line 1$/; //,$ g/^line 3$/ c\
replaced

Из командной строки, отправив результат на терминал,

ed -s file <<'END_ED'
/^line 1$/; //,$ g/^line 3$/ c\
replaced
,p
Q
END_ED

или,

printf '%s\n' '/^line 1$/; //,$ g/^line 3$/ c\' 'replaced' ',p' 'Q' |
ed -s file

Чтобы заменить строки line 3несколькими строками текста, убедитесь, что каждая добавляемая строка текста завершается обратной косой чертой:

ed -s file <<'END_ED'
/^line 1$/; //,$ g/^line 3$/ c\
replaced\
more replace text\
even more
,p
Q
END_ED

Чтобы выполнить редактирование места -, используйтеw(запись буфера в файл )иq(выход )вместо,p(печать всех строк в буфере )иQ(выйти, безоговорочно )в сценарии редактирования.

Обратите внимание, что edне подходит для редактирования огромных файлов, так как редактор считывает файл в память.


Если вы хотите заменить строки одной строкой текста, вы можете избежать многострочного сценария -ed, используяs(замену )вместо c(.нужны ,pи Qтоже):

/^line 1$/; //,$ g/^line 3$/ s//replaced/

Тестирование:

$ printf '%s\n' '/^line 1$/; //,$ g/^line 3$/ s//replaced/' ',p' 'Q' | ed -s file
line 1
line 2
line 3
line 1
line 2
replaced
line 1
line 2
replaced
1
16.11.2021, 15:16

С помощью GNU sed мы можем выполнить задачу путем сохранения счета в виде точек .в трюме пространство.

sed -e '
  /^line 1$/,/^line 1$/{//!b
    x;s/^$/./;x;t
    :a;n;s/^line 3$/REPLACED/;ba
  }
' file

Другая возможность без использования диапазона оператор и с расширенным режимом регулярных выражений в GNU сед. Здесь символ новой строки \nфункционирует как счетчик, хранящийся в области удержания.

sed -Ee '
  /^line 1$/H
  x;s/^(\n{1,2}).*/\1/;x
  /^line 3$/G
  /\n.{2}/c REPLACED
  s/\n//g
' file

Perl может сделать то же самое аналогичным образом

perl -lpe '
  s/^line 3$/$a?"REPLACED":$&/e
           unless
  m?^line 1$?... /^line 1$(?{$a++})/;
' file

Используя awk, мы можем красиво комбинировать регулярные выражения с переменными и увеличением на единицу

awk '
1 < ( k += /^line 1$/ ) &&
sub(/^line 3$/,"REPLACED") ||
1' file

1
17.11.2021, 13:12

Теги

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