Удалить файл, но только если это символическая ссылка

11

В идеале я бы хотел такую ​​команду

rm --only-if-symlink link-to-file

потому что я слишком часто сжигал себя, удаляя файл вместо символической ссылки, указывающей на файл. Это может быть особенно плохо, когда задействован sudo. Теперь я, конечно, делаю, ls -alчтобы убедиться, что это действительно символическая ссылка и тому подобное, но она уязвима для ошибок оператора (файл с таким же именем, опечатка и т. Д.) И условий гонки (если кто-то хочет, чтобы я удалил файл по какой-то причине). Есть ли способ проверить, является ли файл символической ссылкой, и удалить его, только если он находится в одной команде?

Shelvacu
источник
1
Я не думаю, что состояние гонки должно быть проблемой. Любой, кто сможет удалить символическую ссылку и заменить ее файлом, должен иметь разрешение на запись в каталог, что означает, что он также может удалить файл самостоятельно.
Нейт Элдредж

Ответы:

19
 $ rm_if_link(){ [ ! -L "$1" ] || rm -v "$1"; }

 #test
 $ touch nonlink; ln -s link
 $ rm_if_link nonlink
 $ rm_if_link link
   removed 'link'     
PSkocik
источник
4
Конечно, было бы более ясно, чтобы удалить !и изменить ||на&&
Гленн Джекман
5
@glennjackman Я сделал привычкой отдавать предпочтение ||форме. &&сбивает вещи, если вы находитесь в set -eрежиме.
PSkocik
@PSkocik В целом это рискованно, поскольку не различает интересный случай (файл существует и является символической ссылкой) и другие причины ненулевого кода выхода, например пустое значение без кавычек ( [ ! -L $1 ]), с использованием недопустимого оператора ( [ ! -l foo ]), или даже синтаксическая ошибка ( [ ! -q foo || echo foo)
l0b0
2
@XiongChiamiov Проблема в rm_if_link(){ [ -L "$1" ] && rm -v "$1"; }том, что запуск его в set -e режиме без ссылки в режиме убьет ваш процесс оболочки. Вы можете добавить, || :но тогда, IMHO, это станет еще менее понятным, чем ! ||версия, и это предотвратит rmнеудачу set -e, которая вас убьет , что, вероятно, нежелательно. ! ||довольно кратко и ясно, и не страдает ни от одной из этих проблем.
PSkocik
1
Неа . Другие варианты: а) поставить rmвнутреннюю часть вашего теста, б) использовать фактическое выражение if, в) не использовать -eвнутри интерактивных оболочек (и тестировать свои сценарии!). Если вы хотите поспорить о читабельности, то использование ifдля проверки, является ли ссылка какой-то ссылкой, явно выигрывает.
бойкот SE для Моники Челлио
5

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

Например, если у вас есть файл с именем fooв текущем каталоге, вы можете сделать это:

$ find . -type l -iname "foo" -delete

Вы можете упростить это с помощью всего лишь:

$ find . -maxdepth 1 -type l -delete

Что бы удалить все символические ссылки в текущем каталоге.

Предупреждение:

-deleteВариант в findдействительно очень опасно. ОБЯЗАТЕЛЬНО РАЗМЕСТИТЕ ЭТО В КОНЦЕ НАЙТИ КОМАНДУ. Если вы его не поместите, он удалит все, что найдет, независимо от того, соответствуют ли результаты вашему условию.

Как предлагается в комментариях, более безопасным вариантом может быть использование findи rm -i(что заставляет вас подтвердить удаление файла) в сочетании:

$ $ find . -type l -iname "foo" | xargs rm -i

Лично я использую -exec trash {} \;для временного удаления файлов, потому что я, как и вы, был сожжен rmв прошлом. Двойник идет за неуместный -deleteфлаг.

http://slackermedia.ml/trashy

Клаату фон Шлакер
источник
1
Вместо -delete вы можете просто распечатать его и передать xargs: find . -type l -iname "foo" | xargs rm -i это безопаснее.
Бобан П.
1
Мне нравится использование @BobanP. rm -iДля безопасности, конечно.
Клаату фон Шлакер
3
Msgstr "Это удалит все символические ссылки в текущем каталоге" ... и любую точку под ним.
Джозеф Р.
1
Как @JosephR. сказал, что он будет идти по дороге. Добавьте -maxdepth 1 в находке.
Бобан П.
1
Все еще разбивается на имена файлов с пробелами. Предложите использовать -print0для findи -0для xargs.
Уайлдкарт
4
zsh -c 'rm foo(@)'

@является классификатором глобуса ; шаблон foo(@)соответствует тому foo, что соответствует, но только символические ссылки.

foo(-@)будет соответствовать только неработающим символическим ссылкам. foo(@,L0)будет соответствовать только символическим ссылкам и пустым файлам.

Конечно, если вы запускаете zsh, вам просто нужно набрать rm foo(@). Вы должны позаботиться о том, чтобы не нажимать Enterперед вводом (@).

Жиль "ТАК - перестань быть злым"
источник
1
Зш звучит все более и более мощно, когда я вижу такие ответы.
Джефф Шаллер