Насколько безопасно выводить на <dir> одновременно с rm <dir> / *

Иногда мне нужно удалить все содержимое каталога и создать там новые файлы. Могу ли я сделать что-то подобное и ожидать, что все новые файлы останутся нетронутыми:

% rm -rf regression/* & ( sleep 10 ; run_regression ) 

где run_regression временные метки своих выходных файлов, чтобы они имели уникальные имена и run_regression их в regression ?

Я считаю, что оболочка разрешит regression/* в явный список ранее существовавших имен файлов, а затем rm будет удалять файлы в этом явном списке, но не новые файлы, которые run_regression будут создавать одновременно с rm . Поскольку run_regression временные отметки, его файлы не должны встречаться с именами.

Тем не менее, я не совсем уверен, как сказать, когда оболочка закончила перечислять файлы, а rm начинает работать. Является ли выше 10 секунд адекватным? Могу ли я сделать что-то подобное в bash :

 % rm -rf regression/* & ( wait_unil_names_are_resolved ; run_regression ) 

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

5 Solutions collect form web for “Насколько безопасно выводить на <dir> одновременно с rm <dir> / *”

Это небезопасно.

Вы не указали, в чем проблема, которую вы пытаетесь решить. Если ваша проблема заключается в том, что вы хотите, чтобы ваш каталог всегда был там, но время от времени его очищали, я предлагаю явно удалять файлы, более старые, чем файл проверки (сон 1 – это я параноик):

 touch regression.delete \ && find regression \! -newer regression.delete -delete & \ && sleep 1 \ && run_regression 

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

 touch regression.delete \ && find regression -mindepth 1 -maxdepth 1 \! -newer regression.delete -exec rm -rf '{}' \; & \ && sleep 1 \ && run_regression 

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

 mkdir regression.new \ && chmod --reference regression regression.new \ && mv regression regression.delete \ && mv regression.new regression \ && rm -rf regression.delete & \ run_regression 

Это должно позволить вам начать run_regression почти мгновенно.

Отвечая на ваше редактирование (и отредактировав себя после исследования в другом ответе), подстановочные символы должны быть расширены до запуска команды rm , но суть проблемы заключается в том, чтобы узнать, выполняется ли расширение после вилки оболочки. Спецификация POSIX асинхронного выполнения явно не указывает так или иначе, насколько я могу видеть, и раздел 2.1, безусловно, подразумевает, что расширение является отдельной операцией и до фактического fork / exec команды, но тестирование (by @adonis, реплицировано я использую bash 4.3.42 (1)) показывает, что bash берет наиболее эффективный способ: если расширение подстановки требует времени, модификации, выполняемые следующей командой, могут влиять на это расширение. Поэтому исходная идея может быть удалена из файлов, которые вы не хотите удалять.

Я посмотрел на источник bash, и execute_cmd.c явно заявляет, что fork выполняется перед расширением слова:

 3922 | /* If we're in a pipeline or run in the background, set DOFORK so we 3923 | make the child early, before word expansion. This keeps assignment 3924 | statements from affecting the parent shell's environment when they 3925 | should not. */ 

Хотя ваша команда, вероятно, работает, вот пример:

 $ ls $ echo * $(sleep 1)&touch file1 [1] 12798 $ file1 [1]+ Done echo * $(sleep 1) 

Обратите внимание, что file1 не был введен, это был результат команды echo.

Редактировать:

Другой тестовый прогон:

 $ ls $ touch file1 $ for i in {1..5000}; do rm * & touch file$i; wait;done|grep file rm: cannot remove '*': No such file or directory ***previous line repeated 14 times*** 

rm -rf regression/* работает параллельно с ( sleep 10 ; run_regression ) . Это означает, что у вас нет гарантии относительно порядка вещей. rm -rf regression/* сначала собирает список файлов в каталоге regression , а затем вызывает rm для их удаления. Это не происходит по волшебству, это оболочка, выполняющая работу как часть оценки команды rm -rf regression/* , и это происходит после вилки, вызванной оператором & . Если шаг сбора занимает менее 10 секунд, файлы, созданные run_regression , безопасны. Если для шага сбора потребуется более 10 секунд, чтобы достигнуть файла, созданного run_regression , этот файл будет удален.

Удаление файла на самом деле не повлияет на run_regression , если оно не закрывает файл и не открывает его снова. Удаление файла не влияет на процессы, в которых файл открыт: файл сохраняется, без записи в каталоге (т. Е. Количество жестких ссылок 0), до тех пор, пока все процессы, открывающие его, не закрывают его. Но вы не сможете получить доступ к выходу программы, поскольку он будет удален.

Так что не делай этого. Не полагайтесь на сроки: с такой высокой задержкой, как 10 секунд, она будет работать во время тестирования (особенно, поскольку, вероятно, будет несколько файлов, теплый кеш, отсутствие пика ввода-вывода, отсутствие системной приостановки и т. Д. Во время ваше тестирование), но рано или поздно это не сработает.

Если вы действительно хотите сохранить каталог и удалить файлы в нем, сначала создайте коллекцию имен файлов.

 files_to_delete=(regression/*) rm -rf "${files_to_delete[@]}" & run_regression 

(Предполагается, что файлы run_regression создают только файлы, которые не существуют, если они перезаписывают существующие файлы, то эти файлы будут удалены.

Вам, вероятно, не нужна вся эта сложность: просто запустите

 rm -rf regression/* run_regression 

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

Если производительность операции удаления очень плохая (что, опять же, было бы необычно), создайте новый каталог.

 mv regression regression.old mkdir regression rm -rf regression.old & run_regression 
 mv regression regression.old rm -rf regression.old & mkdir regression run_regression 

Переименуйте старый каталог регрессии, удалите его в фоновом режиме, создайте новый каталог регрессии, а затем запустите свою программу.

если run_regression создает сам каталог, если он не существует, то третий шаг не требуется.

Более безопасная версия, в случае, если regression.old уже существует, было бы использовать mktemp для создания и использования временного каталога в текущем каталоге:

 td=$(mktemp -d -p .) mv regression "$td/" rm -rf "$td" & unset td mkdir regression run_regression 

Это безопасно, если вы используете новые имена файлов. Оболочка знает о именах файлов, а не об их inode и т. Д., И делает флешинг (расширение подстановочных знаков) перед запуском команды. Согласно POSIX :

2.6.6 Расширение пути

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

То есть, это четко определенный шаг в разборе, который имеет место до фактического выполнения команды. Большинство сложных случаев в POSIX связаны с перенаправлением и назначением . В этом примере их нет, так что это применимо:

2.9.1 Простые команды

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

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

Предположительно, ваша временная метка (десять секунд различается в секундах в метке времени) будет частью результирующих имен файлов.

  • Удалить файлы-призраки со специальными символами
  • rm -rf все файлы и все скрытые файлы. & .. ошибка
  • find - exec rm vs -delete
  • Как удалить папку тильды (~)?
  • Как удалить файлы и папки под определенной папкой
  • rm -fr не работает
  • Как удалить файл с двумя словами, разделенными пробелом в оболочке?
  • Переместить файлы и удалить каталоги с помощью rsync?
  • Undeletable directory
  • Как удалить родительский каталог, не удаляя подпапки?
  • Удалить соответствующий файл из каждой подпапки текущего каталога
  • Interesting Posts

    Как использовать grep с шаблонами в файле и получить количество вхождений каждого шаблона?

    Обоснование статистики NIC (ethtool -S eth1)

    Courier IMAP – каталог почтовых ящиков учетной записи не принадлежит правильному uid или gid

    Сделать SSL-сертификат доверенным в браузере через SSL Bump Squid

    Создание пользовательского пользователя во FreeBSD

    правило udev не работает

    Помогите мне, пожалуйста, найти мое недоразумение об этом маленьком фрагменте скрипта

    Не удается открыть как корневой терминал, так и nautilus (в Debian 7) после обновления графического драйвера Intel

    CentOS 7 не загружается после `yum update`

    RYSNC над SSH не работает для ограниченной оболочки

    Как я могу использовать локальный (loopback) последовательный порт?

    Разъем для наушников в звуковом гнезде как вход X

    Удаление файла по сравнению с переписыванием и ссылка на / proc / pid / fd

    Arch с XFCE и SDDM: новый пользователь не получает полную среду рабочего стола … Почему?

    Как получить размер каталога в командной строке?

    Linux и Unix - лучшая ОС в мире.