Вопрос или проблема
Я не совсем уверен, как нужно правильно обрабатывать текст, поэтому позвольте задать вопрос.
У меня есть файл: ~/.config/mpv/input.conf
, содержащий, возможно, другие опции, а также v disable
.
Если я захочу удалить эту строку из файла, я полагаю, могу сделать следующее:
grep -v -q -F 'v disable' input.conf; echo $?
1
Где – если файл не содержит ничего другого, как и сейчас, это вернет 1 (страница руководства говорит, что 1
– “Не выбрано ни одной строки.”), что затрудняет использование простого if-statement
.
Так что мне придется сохранить $?
после команды, а затем проверить, больше ли оно 1 для ошибки.
Также мне неясно, является ли замена файла на месте хорошей идеей:
grep ... input.conf > input.conf
Если есть другой способ, дайте знать. Также, если части моих предложений правильны, пожалуйста, дайте знать.
Спасибо, и пока.
ИЗМЕНЕНИЕ1:
Желаемое поведение – переключаться между состоянием, когда есть эта строка (выполнено с помощью простого добавления >>
после проверки, если она там есть, и нет (удалите ее, таким образом это вопрос).
ИЗМЕНЕНИЕ2:
Принятое решение должно быть совместимо с POSIX. Без басхизмов или инструментов, не совместимых с POSIX. Однако вы можете включить решения, не совместимые с POSIX, которые могут быть полезны для других людей. В любом случае, спасибо.
Обычно grep
вернет 0 (истинно), если одна или несколько строк совпадают с вашим регулярным выражением. Флаг -v
инвертирует это, поэтому grep
будет считать успешными все строки, не совпадающие с регулярным выражением. В этом случае вы получите отказ по совпадению v
только тогда, когда в источнике будет ровно одна строка, и она точно совпадает с вашим регулярным выражением.
Таким образом, удаление совпадающей строки и также предоставление указания на то, что изменение было применено, становится немного сложнее:
remove="v disable"
if grep -qxF "$remove" input.conf
then
# Примените удаление, сохраняя резервную копию
cp -fp input.conf input.conf.bak &&
grep -vxF "$remove" input.conf.bak >input.conf
# Выполните другие действия, которые имеют значение, если строка была удалена
else
# Выполните действия, которые имеют значение, если строка отсутствовала
:
fi
Альтернативное предложение от Валентина Байрами было сделано в комментарии с использованием ed
для изменений файла прямо на месте:
if printf '%s\n' '/^v disable/d' wq | ed -s input.conf >/dev/null
then
# Выполните другие действия, которые имеют значение, если строка была удалена
:
else
# Выполните действия, которые имеют значение, если строка отсутствовала
:
fi
Обратите внимание, что в этом случае совпадающая строка рассматривается как регулярное выражение, а не как литерал.
Используя Raku (ранее известный как Perl_6)
Обратите внимание, что этот ответ был обновлен в ответ на отличный комментарий от @roaima.
Код Raku ниже показывает, сколько строковых совпадений с регулярным выражением найдено в файле. Паттерн given
/when
– это в основном “switch” (или “case”) оператор Raku. После нахождения совпадения оно сохраняется в переменной $/
(или $<>
):
~$ raku -e '
my regex RE { ^^ v \s disable $$ };
given "/path/to/original.txt".IO.lines() {
when $_.elems > 0 and none / <RE> / { say "no match"; };
when $_.elems == 1 and / <RE> / { say "one line file, with match"; };
when $_.elems > 1 and / <RE> / { say "multi-line file, with match"; };
default {say "error"; }
};'
Таким образом (например), в третьем операторе when
выше (“multi-line file, with match”), следующий код может быть добавлен в конец этого блока when
:
#`(
spurt("/path/to/original_bak.txt", $_.subst(:global, "$/ "), createonly => True);
unlink("/path/to/original.txt".IO) if "/path/to/original_bak.txt".IO ~~ :e & :f;;
copy("/path/to/original_bak.txt", "/path/to/original.txt", createonly => True)
)
Обработка ошибок встроена для трех операций с файлами выше: см. spurt, class X::IO::Unlink, и class X::IO::Copy. Вы можете добавить Операторы тестирования файлов для проверки еще большего количества аспектов.
Оператор spurt
записывает в новый файл _bak
, unlink
отвязывает оригинал, а copy
копирует резервную копию _bak
к оригинальному имени, оставляя два файла. Удалите многострочные комментарии (первую и последнюю строки) и измените оба экземпляра createonly
на False
, когда вы будете уверены, что код работает правильно (оценено по текстовым значениям возврата).
Вы можете сделать аналогично для случая “one line file, with match”, или просто изменить условие для объединения двух операторов when
в один. Если вам не нравится однострочный формат, вы можете сохранить как скрипт, сделать исполняемым, запускать в cron и т. д.
Ниже приведены предыдущие ответы (и для Raku, и для Perl), которые не выполняют обработку ошибок:
Raku: ~$ raku -ne '.put unless /^v\sdisable$/;' input.conf > tmp_input.conf
Perl: ~$ perl -ne 'print unless /^v disable$/;' input.conf > tmp_input.conf
Эти ответы очень близки к ответу sed
от @Panki, с тем преимуществом, что здесь Perl/Raku удалят созданную пустую строку. См. вторую ссылку ниже для объяснения различий между Perl и Raku.
Perl может выполнять замену на месте с помощью -i
; Raku не может. Но вы можете адаптировать третью ссылку ниже для использования перенаправления оболочки, чтобы обойти ограничение Raku (сначала сделайте резервные копии, в любом случае!).
Примечание: Raku не соответствует POSIX, как запросил автор в изменении. Я оставляю этот ответ для всех, кто ищет подход, не соответствующий POSIX.
https://docs.raku.org/language/5to6-nutshell#given-when
https://unix.stackexchange.com/a/749407/227738
https://unix.stackexchange.com/a/204378/227738
https://raku.org
Ответ или решение
Удаление конкретной строки из файла, если она присутствует, с сохранением всех остальных строк и с корректной обработкой ошибок — задача, требующая подхода с учетом тонкостей обработки текста. В этом ответе подробно рассматриваются шаги, которые помогут вам достичь желаемого результата, используя средства, уверенно работающие в POSIX-средах.
Шаг 1: Подготовка
Заранее создайте резервную копию файла на случай, если потребуется вернуться к предыдущей версии при возникновении ошибок:
cp -fp ~/.config/mpv/input.conf ~/.config/mpv/input.conf.bak
Шаг 2: Определение и удаление строки
Для поиска и удаления строки из файла воспользуемся комбинацией команд grep
и перенаправления вывода:
remove="v disable"
if grep -qxF "$remove" ~/.config/mpv/input.conf; then
grep -vxF "$remove" ~/.config/mpv/input.conf.bak > ~/.config/mpv/input.conf
echo "Строка успешно удалена."
else
echo "Строка отсутствует в файле."
fi
Объяснение:
- В переменной
remove
хранится строка, которую необходимо удалить. grep -qxF "$remove"
используется для проверки наличия строки в файле. Опции-q
и-x
обеспечивают тихий режим и соответствие строки только в полном совпадении, а-F
указывает на использование литералов.grep -vxF "$remove" ~/.config/mpv/input.conf.bak > ~/.config/mpv/input.conf
фильтрует файл, исключая указанную строку, после чего результат сохраняется в исходный файл.
Шаг 3: Проверка и обработка ошибок
Вся суть обработки ошибок заключается в надежной проверке найденной строки и правильной обработки в случае, если операция не удалась:
- В случае успеха скрипт сообщает пользователю о том, что указанная строка была удалена.
- Если строка отсутствует, выдается сообщение об этом, и выполняется соответствующая логическая ветвь.
Альтернатива с использованием ed
ed
– это крайне эффективный редактор, который модифицирует файлы напрямую, что может быть полезно в определенных ситуациях:
if printf '%s\n' '/^v disable/d' wq | ed -s ~/.config/mpv/input.conf > /dev/null; then
echo "Строка удалена."
else
echo "Строка не найдена."
fi
Заключение
Этот подход позволяет эффективно и безопасно удалять строку из файла, при этом подготавливая резервную копию и обеспечивая корректную обработку ошибок. Предложенные методы POSIX-совместимы и могут быть использованы в большинстве Unix-подобных систем.