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

33

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

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

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

  • ls не так просто, как вы могли бы подумать, обрабатывая свои аргументы
  • В простой и беспроблемной установке Ubuntu псевдонимы .bashrc ls
  • Не называйте ваши файлы, начинающиеся с дефиса, так как они могут выглядеть как аргументы команды, а имя один -r запрашивает это!
B.Tanner
источник
8
Я несколько удивлен тем, что rmнет --dry-runфлага ...
fkraiem
20
@Rinzwind Почему бы find -deleteлучше, чем rm? Вы говорите: «Вот почему» , но мне совершенно непонятно, к чему это относится. Также обратите внимание, что ваш findвызов рекурсивно удалит все файлы в текущем каталоге, где rmпросто удалит файлы в непосредственном каталоге. Также -name *нет оп. В общем, я весьма озадачен вашим советом ...
marcelm
3
@marcelm Я думаю, что совет для использования find, потому что вы можете запустить его, просмотреть все файлы, а затем выполнить ту же команду с -delete. Поскольку вы уже видели результаты find, не должно быть никакой двусмысленности относительно того, что будет удалено (на самом деле я хотел бы услышать больше подробностей об этом в форме ответа)
Scribblemacher
5
@Scribblemacher "... вы можете запустить его, просмотреть все файлы, а затем выполнить ту же команду с помощью команды -delete" - Но как это лучше, чем запустить ls <filespec>, а затем rm <filespec>(что ОП уже знает, как это сделать)?
марта
12
@Rinzwind "Это решает ответ Чоробы на мгновение, когда файл создается после ls и до rm." - Нет, это не так. Если вы find ... -printсначала запустите, чтобы подтвердить, какие файлы будут удалены, а затем find ... -deleteвы все равно удалите файлы, созданные между двумя командами. Если вы используете и то, -printи другое -delete, вы не получите подтверждение, а просто отчет о том, что было удалено (и вы можете также использовать rm -v).
марта

Ответы:

40

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

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

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

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

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

Мистер Шунц
источник
3
Также lsобратите внимание на то, rm *где есть «скрытые» (точечные) файлы, хотя даже это не совсем справедливое сравнение, поскольку я не писал ls *. Но rmсамо по себе бессмысленно, так что все дело в яблоках и апельсинах. Если я правильно понял, это суть вашего ответа, так что хорошая работа :)
Легкость гонок с Моникой
5
Еще один комментарий lsпротив rm -r: команда ls <directory>не будет показывать скрытые файлы в директории, но rm -r <directory> будет удалять даже скрытые файлы.
Даниэль Вагнер
1
@LightnessRacesinOrbit lsне будет перечислять их (если у них нет псевдонима ls -a) и rm *не удалит их (если вы не dotglobустановили).
Стоп Harm Monica
20

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

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

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

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

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


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

ilkkachu
источник
1
Спасибо. Кажется, это подчеркивает проблему со всем синтаксисом командной строки: если вы называете файлы, начинающиеся с тире, вы плывете в опасных водах. Кто знает, какие команды вы можете использовать в будущем, после того, как вы забыли о - файлах.
Б.Таннер
1
@ Б.Таннер, да. В некотором смысле, проблема в том, что аргументы командной строки - это просто строки. Если бы система была спроектирована сегодня, в ней могло бы быть больше структуры, чтобы исполняемая программа могла знать, должен ли аргумент быть флагом опции или нет. Есть и другие проблемы, связанные с очень слабой структурой имен файлов. На это есть очень тщательное эссе от dhheler , но я должен предупредить, что чтение это повредит. (Либо из-за подробностей, либо из-за крайней ужасности того, что может пойти не так.)
ilkkachu
3
Я не могу удержаться, вы продали его мне, я сейчас читаю это, с воспоминаниями о том времени, когда мне удалось получить символ возврата каретки в конце множества имен файлов (пытаясь понять, смогу ли я создать пакетный файл Windows, который также может быть запущен как bash-скрипт ... Я не
ожидал, что он появится
17

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

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

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

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

И вы увидите такие случаи повсюду. Вот один из таких случаев: https://superuser.com/a/331124/418028


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

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

$ rm -rf .
rm: refusing to remove '.' or '..' directory: skipping '.'
Сергей Колодяжный
источник
15

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

choroba
источник
1
Это вероятный случай /tmp, когда многие приложения могут создавать временные файлы, так что это всегда возможно в обеих командах с *. Тем не менее, некоторые приложения также делают файлы анонимными, unlink()сохраняя их открытыми, поэтому они могут отображаться, ls *но rm *не могут его перехватить.
Сергей Колодяжный,
1
@OrangeDog Вы упускаете суть. Мы говорим о состоянии гонки
Сергей Колодяжный
1
@OrangeDog Мне нужно проверить это, так как я сейчас на телефоне, но вопрос остается в силе даже с тем, *что есть разница между тем, что lsбудет показано, и тем, что rmработает, потому что список содержимого каталога изменился между ними.
Сергей Колодяжный
1
Даже если вы делаете только одну команду, существует условие гонки между расширением аргументов и последующим их удалением.
Стоп Harm Моника
1
@OrangeDog Я могу согласиться с этим. Поскольку расширение подстановочного знака работает с существующими именами файлов, да, существует условие состязания между оболочкой, расширяющей подстановочный знак, и командой, обрабатывающей его, поэтому ls *фактически может показать имя файла, которое уже пропало.
Сергей Колодяжный
9

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

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

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

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

Тень
источник
2
Спасибо, мне нравится идея использовать echo вместо ls в этом сценарии.
Б.Таннер
3
Или printf "%s\n" *получить однозначный взгляд на имена файлов с пробелами. (Или %qвместо этого иметь дело с символами новой строки и управляющими символами тоже за счет более уродливого вывода.)
ilkkachu
4

Как насчет:

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

./-r:

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


источник
4

Там являются крайние случаи , когда то , что 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 ...
Alexis
источник
1
Да. ln -s $HOME some_link; ls some_linkвыходы some_link@для меня, но я lsпсевдоним ls -F. По-видимому, -Fизменяет поведение, чтобы показывать ссылку, а не разыменовывать ее. Не ожидал этого.
марта
Верно! Также, ls -lнапример, целевая ссылка, а не пункт назначения ... должны быть способы проверки самой ссылки.
Алексис
1
Добавление опций к вопросу открывает слишком много возможностей - мой ответ о неизмененном поведении lsи rm.
Алексис
Если вы говорите ls some_link/,  ls -H some_linkили ls -L some_link, он будет перечислять связанный каталог, даже если вы добавите -Fили -l. Наоборот (вроде) -dговорит , что нужно смотреть на каталог, а не на его содержимое; сравнить ls -l /tmpи ls -ld /tmp.
G-Man говорит «Восстановить Монику»
Конечно, вы можете добавить флаги, которые меняют поведение ls. Вы в основном показываете, почему перечисление поведения с разными флагами не стоит беспокоиться по этому вопросу ...
alexis
4

Если вы только 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)

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

DevHugo
источник
Можете ли вы привести пример? Потому что обычно rm *не удаляет файлы точек. Если это так, ls *также покажет их.
марта
Нет, ls *не отображать скрытые файлы.
DevHugo
Но да, это немного запутано, я добавил несколько примеров.
DevHugo
1
ls -aПеречислим ., .., .testи test2. Возможно, вы захотите изменить свой пример для использования ls -A, который перечисляет все, кроме . и ..(то есть, только .testи test2).
G-Man говорит: «Восстановите Монику»
3

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

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

ls *

...? Обратите внимание, что lsкоманда не получает *параметр as, если есть какие-либо файлы, которые *можно расширить. Вместо этого оболочка сначала выполняет глобирование перед вызовом команды, поэтому 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 *. Учитесь на его выходе!

rexkogitans
источник
1

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


Если вы действительно хотите проверить, что будет удалено, используйте 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'на своем рабочем столе.

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

Питер Кордес
источник