Чи завжди будуть перераховані файли, які буде видалено rm?


33

Щось, що я вважаю, я повинен знати точно: якщо я ls <something>, rm <something>вилучу точно ті самі файли, що й lsвідображалися? Чи є обставини, коли rmможна було видалити файли, які lsне відображалися? (Це в 18.04 баш)

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

Несподівані речі, які я навчився на цьому шляху:

  • ls не так просто, як можна подумати, в обробці його аргументів
  • У простому, не налаштованому на встановлення Ubuntu, псевдонімах .bashrc ls
  • Не називайте ваші файли, починаючи з тире, оскільки вони можуть виглядати як аргументи команд, а іменування одного -r це вимагає!

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>(що ОП вже знає, як робити)?
marcelm

12
@Rinzwind "Це вирішує відповідь choroba миттєво, де файл створюється після ls і перед rm." - Ні, це не так. Якщо ви запускаєте find ... -printспочатку, щоб підтвердити, які файли буде видалено, а потім find ... -deleteви все одно видалите файли, створені між двома командами. Якщо ви використовуєте і те, -printі інше -delete, ви не отримаєте підтвердження, а лише звіт про те, що було видалено (а ви можете також використовувати rm -v).
marcelm

Відповіді:


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
Також розглянемо lsvs, rm *де є "приховані" (крапкові) файли, хоча навіть це зовсім не справедливе порівняння, як я не писав ls *. Але rmвона сама по собі безглузда, тому вся справа - це яблука та апельсини справді. Якщо я правильно зрозумів, це суть вашої відповіді, так що добре :)
Легкість перегонів з Монікою

5
Ще один коментар lsпроти rm -r: команда ls <directory>стане нормальним приховані файли в директорії, але rm -r <directory> буде видаляти навіть приховані файли.
Даніель Вагнер

1
@LightnessRacesinOrbit lsне буде перераховувати їх (якщо тільки це не було дозволено ls -a), і rm *не буде видаляти їх (якщо ви не dotglobвстановили).
Зупиніть шкодити Моніці

20

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

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

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

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

З таким глобусом *.txtце насправді не проблема, оскільки крапка є недійсним символом опції, і вона спричинить помилку (поки хтось не розширить утиліти, щоб вигадати для неї значення), але все одно безпечніше все-таки розміщувати ./там.


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


1
Дякую. Це, здається, висвітлює проблему з усім синтаксисом командного рядка: якщо ви називаєте файли, починаючи з тире, ви пливете в небезпечних водах. Хто знає, які команди ви можете використовувати в майбутньому, довго після того як ви забули про файли -.
B.Tanner

1
@ B.Tanner, так. У певному сенсі проблема полягає в тому, що аргументи командного рядка - це просто прості рядки. Якщо система була спроектована сьогодні, в ній може бути більше структури, щоб виконана програма могла знати, чи повинен аргумент бути опціоном опції чи ні. Є й інші проблеми, які виникають із дуже млявої структури імен файлів. Існує дуже ґрунтовний нарис цього колеса , але я мушу попередити, що читання цього зашкодить. (Або з подробиць, або з усієї жахливості того, що може піти не так.)
ilkkachu

3
Я не можу чинити опір, ти мені це продав, я зараз читаю це, зі спогадами про час, коли мені вдалося отримати символу повернення вагона наприкінці безлічі імен (намагаюся зрозуміти, чи зможу я
побудуйте

17

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

Дозволи до папок

rm- це операція над каталогом, оскільки, видаляючи файл, ви змінюєте вміст каталогів (або, іншими словами, перелік записів каталогів, оскільки d irectory - це не що інше, як перелік імен файлів і вкладень ). Це означає, що вам потрібні дозволи на запис у каталог. Навіть якщо ви власник файлу , без дозволів каталогів ви не можете видалити файли. Зворотне також вірно : 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.


1
Це вірогідний випадок /tmp, коли багато програм можуть створювати тимчасові файли, тому це завжди є можливістю в обох командах *. Однак деякі програми також роблять файли анонімними, unlink()зберігаючи ручку файлу відкритою, тому вона може відображатись, ls *але rm *може не сприймати її.
Сергій Колодяжний

1
@OrangeDog Ви пропускаєте суть. Ми говоримо про стан гонки
Сергій Колодяжний,

1
@OrangeDog Мені потрібно це перевірити, оскільки я зараз по телефону, але справа все ще *існує, навіть якщо існує різниця між тим, що lsбуло б показано, і тим, що rmпрацює на ньому, оскільки перелік вмісту каталогів змінився між ними.
Сергій Колодяжний

1
Навіть якщо ви виконуєте лише одну команду, існує умова перегону між розширенням аргументів та їх видаленням.
Перестаньте шкодити Моніці

1
@OrangeDog Я можу погодитися з цим. Оскільки розширення wildcard працює на існуючих іменах, так, існує умова перегону між оболонкою, що розширює підстановку, і командою, що обробляє її, так що ls *насправді може відображатись ім'я файлу, яке вже немає.
Сергій Колодяжний

9

ls *і rm *не несуть відповідальності за розширення глобуса - це робиться оболонкою перед передачею її команді.

Це означає, що ви можете використовувати будь-яку команду зі розширеним списком файлів - тому я би використовував щось, що робить якнайменше.

Тож кращий спосіб зробити це (або, принаймні, іншим способом) - пропустити середнього чоловіка.

echo *покаже вам саме те, що було б передано вашій rmкоманді.


2
Дякую, мені подобається ідея використання ехо замість ls у цьому сценарії.
B.Tanner

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 ...

1
Ага. ln -s $HOME some_link; ls some_linkВихідні дані some_link@для мене, але я це lsвідчужив ls -F. Мабуть, -Fзмінює поведінку на показ посилання, а не на перенаправлення. Не очікував цього.
marcelm

Справді! Також, ls -lнаприклад, націлена на посилання, а не на місце призначення ... повинні бути способи перевірити саме посилання.
alexis

1
Додавання варіантів до питання відкриває занадто багато можливостей - моя відповідь стосується немодифікованої поведінки lsта rm.
alexis

Якщо ви скажете 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 : відобразиться лише тест2

ls -A dir_test : відобразиться test2 + .test

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

Я сподіваюся, що вам це допоможе.


Чи можете ви навести приклад? Тому що нормально rm *не буде видалено точкові файли. Якщо це станеться, ls *також покаже їх.
marcelm

Ні, ls *не показувати приховані файли.
DevHugo

Але так, це трохи заплутано, я додав кілька прикладів.
DevHugo

1
ls -aПерерахуємо ., .., .testі test2. Ви можете змінити свій приклад на використання ls -A, в якому перераховано все, крім . і ..(тобто, лише .testі test2).
G-Man каже: "Відновіть Моніку"

3

Вже є багато хороших відповідей, але я хочу додати ще глибшого розуміння.

Задайте собі питання: Скільки параметрів передано 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 *. Дізнайтеся з її результатів!


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[@]}". Тож глобальне розширення робиться лише один раз, і список передається дослівно rm.

Ще один практично корисний варіант - GNU rm -I( man page ), який підкаже, якщо видалити більше 4 елементів. (Але не показує список, а лише загальний.) Я використовую alias rm='rm -I'на своєму робочому столі.

Це хороший захист від повернення жиру, з напівнаписаним малюнком, який занадто відповідає. Але використання lsспочатку, як правило, добре у вашому каталозі або в однокористувацькій системі, і коли немає фонових процесів, які могли б асинхронно створювати нові файли там. Щоб не захищати жиру, не друкуйте rm -rf /foo/bar/bazзліва направо. rm -rf /є спеціальним, але rm -rf /usrце не так! Залиште -rfчастину або почніть з ls, і додайте її лише rm -rfпісля введення контуру.

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