Будет ли ls всегда перечислять файлы, которые удалит rm?

Что-то, что я чувствую, я должен знать наверняка: если я ls <something>, будут rm <something> удалить точно такие же файлы, которые ls отображается? Есть ли обстоятельства, когда rm может удалить файлы, которые ls не показывал? (Это в 18.04 Bash)

Редактировать: спасибо всем, кто ответил. Я думаю, что полный ответ представляет собой комбинацию всех ответов, поэтому я принял ответ с наибольшим количеством голосов как "ответ".

Неожиданные вещи, которые я узнал по пути:

  • ls не так просто, как вы могли бы подумать, обрабатывая свои аргументы
  • В простой и беспроблемной установке Ubuntu псевдонимы.bashrc ls
  • Не называйте ваши файлы, начинающиеся с дефиса, так как они могут выглядеть как аргументы команды, а имя один -r запрашивает это!

10 ответов

Решение

Ну и то и другое ls а также rm оперировать аргументами, которые им передаются.

Эти аргументы могут быть простым файлом, поэтому ls file.ext а также rm file.ext оперируйте одним и тем же файлом, и результат ясен (перечислите файл / удалите файл).

Если вместо аргумента это каталог, ls directory перечисляет содержимое каталога в то время как rm directory не будет работать как есть (т.е. rm без флагов не может удалить каталоги, а если вы делаете rm -r directory, он рекурсивно удаляет все файлы в directory и сам каталог).

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

В качестве крайнего примера подумайте ls $(rand).txt а также rm $(rand).txtаргументы "одинаковы", но результаты совершенно разные!

Если вы думаете о чем-то вроде ls foo*.txt против rm foo*.txt, тогда да, они будут показывать и удалять те же файлы. Оболочка расширяет глобус и передает его соответствующей команде, и команды работают с указанными файлами. Один перечисляет их, один удаляет их.

Очевидное отличие состоит в том, что если какой-либо из этих файлов оказался каталогом, то ls перечислил бы его содержимое, но rm не удалится. Это обычно не проблема, так как rm удалит меньше, чем было показано ls,

Большая проблема здесь связана с бегом ls * или же rm * в каталоге, содержащем имена файлов, начинающиеся с тире. Они расширились бы до командных строк двух программ, как будто вы сами их написали, и ls взял бы -r означать "обратный порядок сортировки", в то время как rm взял бы -r означать рекурсивное удаление. Разница имеет значение, если у вас есть подкаталоги как минимум на два уровня. (ls * покажет содержимое каталогов первого уровня, но rm -r * будет все мимо первого подуровня тоже.)

Чтобы избежать этого, пишите разрешительные глобусы с ведущими ./ указать текущий каталог и / или поставить -- сигнализировать об окончании обработки опции перед глобусом (т.е. rm ./* или же rm -- *).

С шариком вроде *.txtэто на самом деле не проблема, так как точка является недопустимым символом опции и будет вызывать ошибку (пока кто-то не расширит утилиты, чтобы придумать для нее смысл), но все же безопаснее поставить ./ там все равно.


Конечно, вы также можете получить разные результаты для двух команд, если вы изменили параметры глобирования оболочки или создали / переместили / удалили файлы между командами, но я сомневаюсь, что вы имели в виду какой-либо из этих случаев. (Работать с новыми / перемещенными файлами было бы крайне затруднительно.)

Оставляя в стороне поведение оболочки, давайте сосредоточимся только на том, что rm а также ls можно разобраться с собой. По крайней мере, один случай, когда ls покажет что rm удалить нельзя, включает права доступа к каталогам, а другое - специальные каталоги . а также ..,

Разрешения для папок

rm является операцией над каталогом, потому что, удаляя файл, вы изменяете содержимое каталога (или, другими словами, перечисление записей каталога, поскольку директория - это не что иное, как список имен файлов и inode). Это означает, что вам нужны разрешения на запись в каталог. Даже если вы являетесь владельцем файла, без прав доступа к каталогу вы не сможете удалить файлы. Обратное также верно: rm Можно удалить файлы, которые могут принадлежать другим, если вы являетесь владельцем каталога.

Таким образом, у вас вполне могут быть права на чтение и выполнение для каталога, что позволит вам, например, просматривать каталог и просматривать содержимое в очень хорошем состоянии. ls /bin/echo, но вы не можете rm /bin/echo если вы не являетесь владельцем /bin или поднять свои привилегии с sudo,

И вы увидите такие случаи повсюду. Вот один из таких случаев: /questions/440316/kak-uznat-ispolzuya-cli-tip-razdela-kotoryij-sootvetstvuet-ego-identifikatoru/440317#440317


Специальные каталоги "." а также '..'

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

$ rm -rf .
rm: refusing to remove '.' or '..' directory: skipping '.'

Если вы печатаете ls * а потом rm *возможно, вы удалите больше файлов, чем ls показал - они могли быть созданы в крошечный промежуток времени между концом ls и начало rm,

ls * а также rm * не несут ответственности за расширение глобуса - это делается оболочкой перед передачей команды.

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

Поэтому лучший способ сделать это (или, по крайней мере, другой способ) - пропустить посредника.

echo * покажет вам, что именно будет передано вашему rm команда.

Как насчет:

$ mkdir what
$ cd what
$ mkdir -p huh/uhm ./-r
$ ls *
uhm
$ rm *
$ ls
-r
$ ls -R
.:
-r

./-r:

В основном подстановочные знаки расширяются до материала, начинающегося с - (или введенный вручную материал, начинающийся с - но это больше похоже на мошенничество) может быть по-разному ls а также rm,

Если вы делаете только ls вместо ls -a, да rm может удалить скрытые файлы, которые вы не видели с ls без -a,

Пример:

В соответствии с:

dir_test
├── .test
└── test2

ls dir_test: будет отображаться только test2

ls -A dir_test: отобразит test2 +.test

rm -r dir_test: удалит все (.test + test2)

Я надеюсь, что это поможет вам.

Есть крайние случаи, когда что ls показывает не то, что rm удаляет. Довольно экстремальный, но, к счастью, доброжелательный, если передаваемый аргумент является символической ссылкой на каталог: ls покажет вам все файлы в каталоге с символическими ссылками, в то время как rm удалит символическую ссылку, оставив исходный каталог и его содержимое без изменений:

% ln -s $HOME some_link
% ls some_link    # Will display directory contents  
bin    lib    Desktop ...
% rm some_link
% ls $HOME
bin    lib    Desktop ...

Уже есть много хороших ответов, но я хочу добавить более глубокое понимание.

Задайте себе вопрос: сколько параметров передается ls, если ты пишешь

ls *

...? Обратите внимание, что ls команда не получает * в качестве параметра, если есть какие-либо файлы, которые * может быть расширен до. Вместо этого оболочка сначала выполняет глобирование, прежде чем вызывать команду, поэтому ls Команда на самом деле получает столько параметров, сколько файлов сопоставлено с глобализацией. Чтобы подавить сглаживание, укажите параметр.

Это верно для любой команды: echo * против echo '*',

Есть скрипт, назовите его countparams.sh проверить эффект. Он сообщает вам, сколько параметров он получил и перечисляет их.

#!/bin/bash
echo "This script was given $# parameters."
arr=( "$@" )
for ((i=0;i<$#;i++)); do
        echo "Parameter $((i+1)): ${arr[$i]}"
done

Сделайте его исполняемым и запустите ./countparams.sh *, Учитесь на его выходе!

Глоб будет расширяться одинаково оба раза, если содержимое каталога одинаково в эти два разных времени.


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

Это гарантированно защищено от условий гонки:
ls *.txt / новый файл создан / rm *.txt
потому что вам предлагается для каждого файла одна и та же программа, которая выполняет удаление.


Это слишком громоздко для нормального использования, и если вы псевдоним rm в rm -i, вы обнаружите, что используете \rm или же rm -f довольно часто. Но стоит хотя бы упомянуть, что есть решение для условий гонки. (Это даже переносимо на не-GNU системы: POSIX rm(1) указывает на -i вариант.)

Другим вариантом будет массив bash: to_remove=(*.txt), затем попросите пользователя подтвердить (возможно, после выполнения ls -ld -- "${to_remove[@]}"), затем rm -- "${to_remove[@]}", Таким образом, расширение glob выполняется только один раз, и список передается дословно rm,

Еще один практичный вариант - GNU rm -I ( man-страница), которая запрашивает удаление более 4 элементов. (Но не показывает вам список, только итог.) Я использую alias rm='rm -I' на моем рабочем столе.

Это хорошая гарантия против возврата жира с полузакрытым шаблоном, который слишком подходит. Но используя ls Первый вариант обычно подходит для вашей собственной директории или для однопользовательской системы, и когда нет фоновых процессов, которые могут асинхронно создавать новые файлы там. Для защиты от аппетита, не печатайте rm -rf /foo/bar/baz слева направо. rm -rf / в специальном корпусе, но rm -rf /usr нет! Оставьте -rf часть или начать с ls и только добавить rm -rf часть после ввода пути.

Другие вопросы по тегам