Будет ли 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
часть после ввода пути.