Видаліть файл, але тільки якщо це символьне посилання


11

В ідеалі я хотів би такої команди

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

тому що я занадто багато разів спалював себе, випадково видаляючи файл замість символьної посилання, що вказує на файл. Це може бути особливо погано, якщо бере участь судо. Тепер я, звичайно, роблю це, ls -alщоб переконатися, що це справді симпосилання та таке, але воно вразливе до помилок оператора (аналогічно названих файлів, друкарських помилок тощо) та умов перегонів (якщо хтось хотів, щоб я чомусь видалив файл). Чи є спосіб перевірити, чи файл є символьним посиланням, і видалити його, лише якщо він знаходиться в одній команді?


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'     

4
Звичайно, було б зрозуміліше видалити !та змінити ||на&&
glenn jackman

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всередину свого тесту; б) використовуйте фактичне, якщо заява, в) не обходитесь, використовуючи -eвнутрішні інтерактивні оболонки (і тестуйте свої сценарії!). Якщо ви хочете сперечатися з приводу читабельності, ifпереможець використовує для перевірки, чи є щось посилання.
Бойкот SE для Моніки Селліо

5

Ви можете використовувати findта його -type lтестовий стан (які тести, щоб побачити, чи знайдений об’єкт є чорнилом 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
"Це видалить усі посилання в поточному каталозі" ... і будь-яку точку під ним.
Джозеф Р.

1
Як @JosephR. сказав, це піде цілком по дорозі. Додайте -maxdepth 1 у пошуку.
Бобан П.

1
Досі розбивається на назви файлів з пробілами. Запропонуйте використовувати -print0для findі -0для xargs.
Wildcard

4
zsh -c 'rm foo(@)'

@є глобальним класифікатором ; візерунок foo(@)відповідає тому, що fooвідповідає, але лише символічним посиланням.

foo(-@)відповідав би лише розірваним символічним посиланням. foo(@,L0)відповідатиме лише символічним посиланням та порожнім файлам.

Звичайно, якщо ви запускаєте zsh в першу чергу, вам просто потрібно набрати rm foo(@). Потрібно подбати про те, щоб не натискати, Enterперш ніж набирати текст (@).


1
Zsh звучить усе сильніше, коли я бачу відповіді, як це.
Джефф Шаллер
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.