Я смотрел на этот вопрос, а потом удивлялся, как мне реализовать свой ответ, использующий
sed
исключительно
POSIX
ex
.
Хитрость заключается в том, что хотя sed
я могу сравнить пространство удержания с пространством шаблона, чтобы увидеть, являются ли они в точности эквивалентными (с
G;/^\(.*\)\n\1$/{do something}
), я не знаю способа выполнить такой тест ex
.
Я знаю, что в Vim я мог бы Y
прикрепить первую строку, а затем набрать,
:2,$g/<C-r>0/d
чтобы почти выполнить то, что я указываю, но если первая строка содержит что-либо, кроме очень простого буквенно-цифрового текста, это действительно становится случайным, поскольку строка выводится как
регулярное выражение , а не просто строка для сравнения. (И если первая строка содержит косую черту, остальная часть будет интерпретирована как команда!)
Поэтому, если я хочу удалить все строки в myfile
этой строке , идентичные первой строке, но не удалить первую строку, как я могу это сделать, используя ex
? В этом отношении, как я мог сделать это, используя vi
?
Есть ли способ POSIX удалить строку, если она точно совпадает с другой строкой?
Возможно, что-то вроде этого воображаемого синтаксиса:
:2,$g/**lines equal to "0**/d
источник
:execute '2,$g/\V' . escape(getline(1), '\') . '/d'
sed
в качестве фильтра изнутриex
и запустить весь мойsed
ответ по всему буферу ... который , конечно, будет работать (и на самом деле переносим в отличие отsed -i
).<C-r>0
очень хорошим. Я не уверен, что вы могли бы добиться большего успеха только с помощью команд Ex, потому что вы должны защищать специальные символы. Без ограничения, совместимого с POSIX, я думаю, что вы бы использовали очень номагический переключатель,\V
а затем защитили бы обратную косую черту (потому что она сохраняет свое особое значение даже с помощью\V
) с помощьюescape()
функции, второй аргумент которой является строкой, содержащей все символы, которые вы хотите экранировать / защитить ,:execute '2,$g/\V' . escape(getline(1), '\/') . '/d'
Или вы можете использовать другой символ для разделителя шаблонов, например точку с запятой. В этом случае вам не нужно защищать косую черту в шаблоне. Было бы что-то вроде::execute '2,$g;\V' . escape(getline(1), '\') . ';d'
sed
тоже очень хорош. С Vim вы часто делегируете определенные специальные задачи другим программам, иsed
, вероятно, хороший пример этого. Кстати, вам не нужно запускатьsed
весь буфер. Если вы хотите запустить его только на части буфера, вы можете указать диапазон. Например, если вы хотите , чтобы отфильтровать только строки между 50 и 100, вы можете набрать::50,100!<your sed command>
.Ответы:
напор
В Vim вы можете сопоставить любой символ, включая перевод строки
\_.
. Вы можете использовать это для построения шаблона, который соответствует целой строке, любому количеству материала, а затем той же строке:Теперь вы хотите удалить все строки в файле, которые соответствуют первому, кроме первого. Подстановка для удаления последней строки, которая соответствует первой:
Вы можете использовать,
:global
чтобы убедиться, что подстановка повторяется достаточно раз, чтобы удалить все строки:POSIX ex
@saginaw демонстрирует изящный способ сделать это в Vim в комментарии к вашему вопросу, но мы можем адаптировать вышеописанную технику для POSIX ex.
Чтобы сделать это POSIX-совместимым способом, вам нужно запретить многострочное сопоставление, но вы все равно можете использовать обратные ссылки. Это требует дополнительной работы:
Вот разбивка:
Таким образом, если текущая строка является дубликатом строки 1, регистр x будет содержать
d
. Выполнение этого удалит текущую строку. Если он не является дубликатом, он будет содержать бессмысленный префикс, с"
которым при выполнении будет запрещен, так как"
начинается комментарий. Я не знаю, если это самый лучший способ сделать это, это только первое, что пришло в голову!Просто так получилось, что первую строку удалить нельзя, потому что процесс копирования временно меняет, что такое строка 1. Если бы это было не так, вы можете вместо этого поставить префикс
:g
с2,$
диапазоном.Протестировано в Vim и ex-vi версии 4.0.
РЕДАКТИРОВАТЬ
И более простой способ, который экранирует специальные символы для создания шаблона поиска (с помощью
'nomagic'
set), создает:global
команду, а затем выполняет ее:Вы не можете сделать это как одну строку, так как у вас будет вложенный
:global
, что не разрешено.источник
Казалось бы, единственный способ сделать это в POSIX - это использовать внешний фильтр, например
sed
.Например, чтобы удалить 17-ю строку вашего файла, только если она в точности совпадает с 5-й строкой, и в противном случае оставить ее без изменений, вы можете сделать следующее:
(Вы можете запустить
sed
весь буфер здесь, или вы можете запустить его только в строках 5-17, но в первом случае вы выполняете ненужную фильтрацию - ничего страшного - и в последнем случае вам придется использовать цифры 1 и 13 в вашейsed
команде вместо 5 и 17. сбивает с толку.)Поскольку
sed
выполняется только один проход вперед, простого способа сделать обратный ход и удалить 5-ю строку нет, только если она идентична 17-й строке. Некоторое время я пытался из любопытства ... это сложно .Прорыв - Вы можете сделать это так:
Это на самом деле более общий метод. Он также может быть использован для получения того же результата, что и первая команда (и удаление 17-й строки, только если она идентична 5-й строке), например так:
Для более широкого использования, такого как удаление всех строк файла, идентичных строке 37, при этом оставляя строку 37 без изменений, вы можете сделать следующее:
Вывод здесь заключается в том, что для проверки, идентичны ли две строки, лучший инструмент -
sed
нетex
. Но, как отметил DevSolar в комментарии , это не является ошибкойvi
илиex
- они предназначены для работы с инструментами Unix; это главная сила.источник