Вопрос Есть ли недостатки в использовании rm $ (ls) для удаления файлов?


Мне было интересно, rm $(ls) для удаления файлов (или rm -r $(ls) для удаления каталогов) было безопасно? Потому что на всех сайтах люди дают другие способы сделать это, хотя эта команда кажется намного проще, чем другие команды.


13
2017-09-03 01:35


происхождения


Основной ответ, нет. ls не может обрабатывать специальные символы. Я могу написать ответ немного, чтобы объяснить это более подробно - Sergiy Kolodyazhnyy
touch 'foo -r .. bar' ; rm $(ls); где находится моя родительская директория? Кроме того, какие альтернативы вы видите, что еще сложнее, чем это? rm * гораздо легче печатать и думать, и безопаснее (но не совсем безопасно, см. ответ Денниса). - Peter Cordes
В дополнение к отличным ответам, имейте в виду, что ls может варьироваться между реализациями и, следовательно, является нестандартным. В зависимости от того, что вам нужно, рассмотрите альтернативы, такие как find а также stat, Вы должны использовать ls только для потребления человеком, никогда не для использования другими командами или сценариями. - Paddy Landau


ответы:


Что это предназначено?

  • ls списки файлов в текущем каталоге
  • $(ls) заменяет выпуск ls указывает, что в качестве аргумента для rm
  • по существу rm $(ls) предназначен для удаления всех файлов в текущем каталоге

Что случилось с этой картиной?

ls не может правильно обрабатывать специальные символы в имени файла. Пользователи Unix обычно советовали использовать разные подходы, Я также показал, что в связанный вопрос о подсчете имен файлов, Например:

$ touch file$'\n'name                                                                                                    
$ ls                                                                                                                     
file?name
$ rm $(ls)
rm: cannot remove 'file': No such file or directory
rm: cannot remove 'name': No such file or directory
$ 

Кроме того, как правильно сказано в ответе Дениса, имя файла с ведущими штрихами, можно интерпретировать как аргумент rm после подстановки, которая побеждает цель удаления имени файла.

Что работает

Вы хотите удалить файлы в текущем каталоге. Так что используйте glob rm *:

$ ls                                                                                                                     
file?name
$ rm $(ls)
rm: cannot remove 'file': No such file or directory
rm: cannot remove 'name': No such file or directory
$ rm *
$ ls
$ 

Вы можете использовать find команда. Этот инструмент часто рекомендуется для большего, чем просто текущего каталога - он может рекурсивно пересекать все дерево каталогов и работать с файлами через -exec . . .{} \; 

$ touch "file name"                                
$ find . -maxdepth 1 -mindepth 1                                                                                         
./file name
$ find . -maxdepth 1 -mindepth 1 -exec rm {} \;                                                                          
$ ls
$ 

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

python -c 'import os; [ os.remove(i) for i in os.listdir(".") if os.path.isfile(i) ]'

Фактически, приведенная выше команда может быть превращена в функцию или псевдоним в ~/.bashrc для краткости. Например,

rm_stuff()
{
    # Clears all files in the current working directory
    python -c 'import os; [ os.remove(i) for i in os.listdir(".") if os.path.isfile(i) ]'

}

Perl версия этого будет

perl -e 'use Cwd;my $d=cwd();opendir(DIR,$d); while ( my $f = readdir(DIR)){ unlink $f;}; closedir(DIR)'

7
2017-09-03 01:49



Ваш "$(ls)" пример работает только в том случае, если в каталоге есть только один файл. Вы можете просто использовать tab-completion для расширения имени файла, поскольку это единственное завершение. - Peter Cordes
@PeterCordes действительно, по какой-то причине он работает только с одним файлом. Это еще один аргумент против использования ls то :) Я отредактирую это - Sergiy Kolodyazhnyy
Я бы не назвал это странной причиной: вы либо цитируете $(ls) чтобы отключить расщепление слов, или вы разрешаете расщепление слов (с катастрофическими результатами). Единственный чистый способ передать список нескольких строк без обработки данных как кода с помощью переменных массива или с помощью \0 как разделитель, но сама оболочка не может этого сделать. Все еще IFS=$'\n' является наименее опасным, но не может совпадать find -print0 | xargs -0, Или grep -l --null, Или вы избегаете всей проблемы с такими вещами, как find -exec rm {} +, (Обратите внимание + передать несколько аргументов каждому вызову rm; ПУТЬ более эффективны). - Peter Cordes
@PeterCordes Yup, полностью согласился. Но IFS=$'\n' в этом случае тоже не удастся, так как у меня есть символ новой строки в имени файла, поэтому wordplitting будет рассматривать его как два имени файла вместо одного. Однако странная причина заключается в том, что при дефолте IFS который является пространством, вкладкой, новой линией, оригиналом rm "$(ls)" должен также потерпеть неудачу, он должен лечить, как я сказал, имя файла как два отдельных, но это не так. Обычно я использую find с -exec , или find . . .-print0 | while IFS= read -d'' FILENAME ; do . . . done структура для обработки имен файлов. Или можно использовать python , Я добавил пример этого. - Sergiy Kolodyazhnyy
"$(ls)" всегда расширяется до одного аргумента, поскольку кавычки защищают расширение $(ls) от расщепления слов. Так же, как они защищают "$foo", - Peter Cordes


Нет, это небезопасно, и обычно используемая альтернатива rm * не намного безопаснее.

Есть многие проблемы с rm $(ls), Как уже отмечалось в ответах других, результаты ls будут разделены на символы, присутствующие в внутренний разделитель полей,

Лучший сценарий, он просто не работает. В худшем случае вы должны удалить только файлы (но не каталоги) - или выборочно удалить некоторые файлы с помощью -i - но есть файл с именем c -rf в текущем каталоге. Давай посмотрим что происходит.

$ mkdir a
$ touch b
$ touch 'c -rf'
$ rm -i $(ls)
$ ls
c -rf

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

rm -i a b c -rf

поэтому он сделал что-то еще полностью.

Обратите внимание, что rm * лишь незначительно лучше. В структуре каталогов, как и раньше, он будет вести себя так, как предполагалось здесь, но если у вас есть файл, называемый -rf, вам все еще не повезло.

$ mkdir a
$ touch b
$ touch ./-rf
$ rm -i *
$ ls
-rf

Есть несколько лучших альтернатив. Самые простые из них связаны только с комната и глобусы.

  • Команда

    rm -- *
    

    будет работать точно так, как предполагалось, где -- сигнализирует, что все после него не должно интерпретироваться как вариант.

    Это уже более двух десятилетий является частью руководства по синтаксису утилиты POSIX. Он широко распространен, но вы не должны ожидать, что он будет присутствовать везде.

  • Команда

    rm ./*
    

    делает glob расширяться по-разному и, следовательно, не требует поддержки от вызываемой утилиты.

    Для моего примера сверху вы можете увидеть команду, которая в конечном итоге будет выполнена путем добавления эхо,

    $ echo rm ./*
    rm ./a ./b ./-rf
    

    Ведущий ./ предотвращает комната от случайного обращения с любыми именами файлов, например, опциями.


28
2017-09-03 04:31



Очень хорошая точка, имена файлов с - после расширения становятся флагами rm, +1 - Sergiy Kolodyazhnyy
Даже противнее: touch 'foo -rf .. bar, Я не думаю, что злоумышленник может получить больше, чем родительский каталог, если мы не можем создать разделитель путей в lsвывода. - Peter Cordes
У злоумышленника @Peter должно быть разрешение на запись, чтобы удалить патентный каталог, в первую очередь, нет? - Sergiy Kolodyazhnyy
@PeterCordes Я не уверен, что все версии комнатаэто безотказно, но на Ubuntu, openSUSE и Fedora, говорится: rm: refusing to remove '.' or '..' directory: skipping '..' или что-то подобное при попытке удалить родительский каталог. - Dennis
@Serg: злоумышленник просто отправляет вам .zip с этим именем файла в нем и позволяет вам стрелять в ногу, извлекая его, а затем пытаясь удалить содержимое. Или создав это имя файла в / var / tmp или что-то в этом роде. - Peter Cordes