Bash удалить файлы кроме
Is there a simple way, in a pretty standard UNIX environment with bash, to run a command to delete all but the most recent X files from a directory?
To give a bit more of a concrete example, imagine some cron job writing out a file (say, a log file or a tar-ed up backup) to a directory every hour. I'd like a way to have another cron job running which would remove the oldest files in that directory until there are less than, say, 5.
And just to be clear, there's only one file present, it should never be deleted.
112k 46 46 gold badges 107 107 silver badges 128 128 bronze badges16 Answers 16
The problems with the existing answers:
- inability to handle filenames with embedded spaces or newlines.
- in the case of solutions that invoke rm directly on an unquoted command substitution ( rm `. ` ), there's an added risk of unintended globbing.
wnoise's answer addresses these issues, but the solution is GNU-specific (and quite complex).
Here's a pragmatic, POSIX-compliant solution that comes with only one caveat: it cannot handle filenames with embedded newlines - but I don't consider that a real-world concern for most people.
Note: This command operates in the current directory; to target a directory explicitly, use a subshell ( (. ) ) with cd :
(cd /path/to && ls -tp | grep -v '/$' | tail -n +6 | xargs -I <> rm -- <>)
The same applies analogously to the commands below.The above is inefficient, because xargs has to invoke rm separately for each filename.
However, your platform's specific xargs implementation may allow you to solve this problem:A solution that works with GNU xargs is to use -d '\n' , which makes xargs consider each input line a separate argument, yet passes as many arguments as will fit on a command line at once:
Note: Option -r ( --no-run-if-empty ) ensures that rm is not invoked if there's no input.
A solution that works with both GNU xargs and BSD xargs (including on macOS) - though technically still not POSIX-compliant - is to use -0 to handle NUL -separated input, after first translating newlines to NUL ( 0x0 ) chars., which also passes (typically) all filenames at once:
Explanation:
ls -tp prints the names of filesystem items sorted by how recently they were modified , in descending order (most recently modified items first) ( -t ), with directories printed with a trailing / to mark them as such ( -p ).
- Note: It is the fact that ls -tp always outputs file / directory names only, not full paths, that necessitates the subshell approach mentioned above for targeting a directory other than the current one ( (cd /path/to && ls -tp . ) ).
grep -v '/$' then weeds out directories from the resulting listing, by omitting ( -v ) lines that have a trailing / ( /$ ).
- Caveat: Since a symlink that points to a directory is technically not itself a directory, such symlinks will not be excluded.
tail -n +6 skips the first 5 entries in the listing, in effect returning all but the 5 most recently modified files, if any.
Note that in order to exclude N files, N+1 must be passed to tail -n + .xargs -I <> rm -- <> (and its variations) then invokes on rm on all these files; if there are no matches at all, xargs won't do anything.
- xargs -I <> rm -- <> defines placeholder <> that represents each input line as a whole, so rm is then invoked once for each input line, but with filenames with embedded spaces handled correctly.
- -- in all cases ensures that any filenames that happen to start with - aren't mistaken for options by rm .
A variation on the original problem, in case the matching files need to be processed individually or collected in a shell array:
Подскажите, как удалить файлы и папки кроме одной? В Ubuntu 16.04. Сама папка находится по такому пути /var/lib/tomcat8/webapps/ROOT/ .
Делаю скрипт, который сам деплоит (распаковывает) проект для tomcat'а из архива. Сам скрипт .sh находится в корне.
Я попробовал сделать так:
Но так получилось, что у меня снеслась вся система - пришлось переустанавливать.
Подскажите, пожалуйста, как правильно это сделать?
31.9k 22 22 золотых знака 119 119 серебряных знаков 206 206 бронзовых знаков
3,805 4 4 золотых знака 18 18 серебряных знаков 47 47 бронзовых знаков @NickVolynkin наверно для задержки. нашел это в инете.Рассмотрим одну из строчек вашего скрипта. Тут содержится уязвимость, вероятно, из-за этого и сломалась система.
Вообще говоря, разбирать вывод утилиты ls — очень плохая идея. То, что показывает ls, предназначено в первую очередь для чтения человеком.
Лучшим подходом будет использование утилиты find в сочетании с xargs примерно таким образом
-
-maxdepth 1 ограничивает область поиска текущим каталогом (так сделано для упрощения последующего условия)
-not -name test — а это по сути само условие поиска (файлы, чьё имя не test)
Если файлов, которые хотим сохранить, несколько, их можно перечислять, добавляя дополнительные предикаты -name .
-print0 заставляет find печатать имена файла, разделяя их нулевым байтом, чтобы xargs в последствии правильно бы их считала (для этого используется флаг -0 )
Мне ещё импонирует вот такой «башизм» для указания всех файлов в текущем каталоге кроме заданного. Активируется он с помощью встроенной команды shopt, которая меняет параметры работы текущей оболочки.
Опция extglob включает такой расширенный синтаксис файловой подстановки, dotglob нужна, чтобы также подставлялись и скрытые файлы.
Несколько файлов, которые нужно уберечь от удаления, перечисляются в круглых скобках с разделителями — вертикальными палочками | .
И да, мне не очень нравится, что у вас sudo суть ли не в каждой строчке. Думаю, вам стоит избавиться от этого, но сам скрипт запускать, дав ему права суперпользователя.
Иногда вам нужно удалить почти все файлы из каталога, но вы хотите сохранить один или несколько из них. Когда их много, идти по одному - утомительное занятие. Это не лучший вариант, есть способы значительно упростить работу в Linux и можно сразу избавиться от всех, что вам нужно.
Например, вы можете удалить только те, которые начинаются с определенного имени, или те, которые имеют определенное расширение, и так далее. Все что возможноФактически, в других случаях я уже показывал аналогичные уроки в LxA. Здесь вы можете шаг за шагом и простым способом следовать руководству, чтобы удалить все нужные файлы, кроме тех, которые вы хотите сохранить.
И самое лучшее то, что вам не нужно будет устанавливать какую-либо программу, это легко сделать с помощью таких команд, как rm и find. То есть программы, которые уже предустановлены в любом дистрибутиве Linux. И, конечно же, метод будет основан на поиске шаблонов и использовании этих совпадений для удаления только того, что вы хотите.
Ну чтобы там исключить несколько альтернатив, Кто они такие…
Удалить файлы из каталога с помощью rm
Ну, чтобы использовать команда rm Чтобы избавиться от того, что вы чувствуете, вы должны знать перед некоторыми способами выявления закономерностей:
- * (список шаблонов) - соответствует нулю или более вхождений указанных шаблонов
- ? (список шаблонов) - соответствует нулю или одному вхождению указанных шаблонов
- + (список шаблонов) - соответствует одному или нескольким экземплярам указанных шаблонов
- @ (список шаблонов) - соответствует одному из указанных шаблонов
- ! (список шаблонов) - соответствует чему угодно, кроме одного из заданных шаблонов
к активировать extglob Чтобы использовать их, вы должны сначала выполнить следующую команду:
глаз! Я не указываю это, но предполагается, что у вас есть разрешения на выполнение этих операций и что вы находитесь внутри этого каталога, когда выполняете команду rm. Будьте осторожны с этим, потому что, если вы запустите его по другому пути, вы можете удалить файлы, которые вам не нужны. То есть перед выполнением этих команд убедитесь, что вы вошли в нужный каталог с помощью cd.
Теперь вы можете использовать rm, чтобы удалить все, что захотите. Например, удалить все файлы из каталога, кроме тех, которые соответствуют имени «Lxa»:
Вы также можете указать два или более имен, которые вы не хотите удалять. Например, чтобы избежать удаления lxa и desdelinux:
Вы можете удалить все файлы, минус те, с расширением .mp3. Например:
В конце вы можете вернуться к отключить extglob:
Удалить файлы из каталога с помощью find
Другая альтернатива rm - используйте find, чтобы удалить то, что вам нравится. Вы можете использовать канал и xargs с rm или использовать параметр -delete для поиска. То есть общий синтаксис будет таким:
Например, представьте, что вы хотите удалить все файлы из каталога, кроме файлов с расширением .jpg, вы можете использовать одну из этих двух команд, поскольку обе они дают одинаковый результат:
Вместо этого, если вы хотите добавить дополнительный узор, ты тоже мог бы. Например, предположим, что вы не хотите удалять файлы .pdf или .odt из каталога:
Конечно, вы можете сделать то же самое с | и xargs, как в предыдущем примере. Кстати, мы использовали -не отрицать, но вы можете удалить это, чтобы сделать его положительным, то есть удалить совпадающие шаблоны, а не исключать их.
Удалите файлы из каталога с помощью переменной GLOBIGNORE
Наконец, есть Другая альтернатива чтобы найти и rm, и он использует переменную среды, чтобы указать файлы, которые вы хотите удалить или исключить. Например, представьте, что вы хотите удалить все файлы в каталоге с именем Downloads, сохранив файлы .pdf, .mp3 и .mp4. В этом случае вы можете сделать следующее:
Содержание статьи соответствует нашим принципам редакционная этика. Чтобы сообщить об ошибке, нажмите здесь.
Полный путь к статье: Любители Linux » GNU / Linux » Системное администрирование » Как удалить все файлы в каталоге, кроме одного
Материал, перевод которого мы публикуем сегодня, предназначен для тех, кто хочет освоить командную строку Linux. Умение эффективно пользоваться этим инструментом позволяет экономить немало времени. В частности, речь здесь пойдёт о командной оболочке Bash и о 21 полезной команде. Также мы поговорим о том, как пользоваться флагами команд и псевдонимами Bash, которые позволяют ускорить ввод длинных инструкций.
Термины
В ходе освоения работы в командной строке Linux вам может встретиться множество понятий, в которых полезно будет ориентироваться. Некоторые из них, вроде «Linux» и «Unix», или «командная оболочка» и «терминал», иногда путают. Поговорим об этих и о других важных терминах.
Unix — это популярная операционная система, которая была разработана Bell Labs в 1970-х. Её код был закрытым.
Linux — это самая популярная Unix-подобная операционная система. Она в наши дни используется на множестве устройств, в том числе — и на компьютерах.
Терминал (terminal), или эмулятор терминала — это программа, дающая доступ к операционной системе. Одновременно можно открывать несколько окон терминала.
Оболочка (shell) — это программа, которая позволяет отправлять операционной системе команды, написанные на особом языке.
Bash расшифровывается как Bourne Again SHell. Это — самый распространённый язык командной оболочки, используемый для взаимодействия с операционной системой. Кроме того, оболочка Bash по умолчанию используется в macOS.
Скрипт (script) — это небольшая программа, которая содержит последовательность команд командной оболочки. Скрипты записывают в файлы, использовать их можно многократно. При написании скриптов можно пользоваться переменными, условными конструкциями, циклами, функциями и другими возможностями.
Теперь, когда мы рассмотрели важные термины, хочу отметить, что здесь я буду пользоваться терминами «Bash», «оболочка» и «командная строка» как взаимозаменяемыми, равно как и понятиями «директория» (directory) и «папка» (folder).
Стандартные потоки, которыми мы будем здесь пользоваться — это стандартный ввод (standard input, stdin ), стандартный вывод (standard output, stdout ) и стандартный вывод ошибок (standard error, stderr ).
Если в примерах команд, которые будут приводиться ниже, вы встретите нечто вроде my_whatever — это означает, что данный фрагмент нужно заменить чем-то вашим. Например — именем файла.
Теперь, прежде чем приступать к разбору команд, которым посвящён этот материал, давайте взглянем на их список и на их краткие описания.
21 Bash-команда
▍Получение информации
- man : выводит руководство пользователя (справку) по команде.
- pwd : выводит сведения о рабочей директории.
- ls : выводит содержимое директории.
- ps : позволяет просматривать сведения о работающих процессах.
▍Манипуляции с файловой системой
- cd : изменение рабочей директории.
- touch : создание файла.
- mkdir : создание директории.
- cp : копирование файла.
- mv : перемещение или удаление файла.
- ln : создание ссылки.
▍Перенаправление ввода-вывода и конвейеры
- < : перенаправление stdin .
- > : перенаправление stdout .
- | : перенаправление с помощью конвейера вывода одной команды на вход другой команды.
▍Чтение файлов
- head : чтение начала файла.
- tail : чтение конца файла.
- cat : чтение файла и вывод его содержимого на экран или конкатенация файлов.
▍Удаление файлов, остановка процессов
- rm : удаление файла.
- kill : остановка процесса.
▍Поиск
- grep : поиск информации.
- ag : продвинутая команда для поиска.
▍Архивация
Подробности о командах
Для начала давайте разберёмся с командами, результаты работы которых выдаются в форме stdout . Обычно эти результаты появляются в окне терминала.
▍Получение информации
man command_name : вывод руководства по команде, то есть — справочной информации.
pwd : вывод на экран пути к текущей рабочей директории. В ходе работы с командной строкой пользователю часто нужно узнавать то, где именно в системе он находится.
ls : вывод содержимого директории. Эта команда тоже используется весьма часто.
ls -a : вывод скрытых файлов. Здесь применён флаг -a команды ls . Использование флагов помогает настраивать поведение команд.
ls -l : вывод подробной информации о файлах.
Обратите внимание на то, что флаги можно комбинировать. Например — так: ls -al .
ps : просмотр выполняющихся процессов.
ps -e : вывод сведений обо всех выполняющихся процессах, а не только о тех, которые связаны с текущей оболочкой пользователя. Данную команду часто используют именно в таком виде.
▍Манипуляции с файловой системой
cd my_directory : изменение рабочей директории на my_directory . Для того чтобы перейти на один уровень выше в дереве каталогов используйте в качестве my_directory относительный путь ../ .
touch my_file : создание файла my_file по заданному пути.
mkdir my_directory : создание папки my_directory по заданному пути.
mv my_file target_directory : перемещение файла my_file в папку target_directory . При указании целевой директории нужно использовать абсолютный путь к ней (а не конструкцию вроде ../ ).
Команду mv , кроме того, можно использовать для переименования файлов или папок. Например, выглядеть это может так:
mv my_old_file_name.jpg my_new_file_name.jpg
cp my_source_file target_directory : создание копии файла my_source_file и помещение её в папку target_directory .ln -s my_source_file my_target_file : создание символической ссылки my_target_file на файл my_source_file . Если изменить ссылку, то изменится и исходный файл.
Если файл my_source_file будет удалён, то my_target_file останется. Флаг -s команды ln позволяет создавать ссылки и для директорий.
Теперь поговорим о перенаправлении ввода-вывода и конвейерах.
▍Перенаправление ввода-вывода и конвейеры
my_command < my_file : заменяет дескриптор файла стандартного ввода ( stdin ) на файл my_file . Это может оказаться полезным в том случае, если команда ожидает ввода неких данных с клавиатуры, а эти данные заранее сохранены в файле.
my_command > my_file : перенаправляет результаты работы команды, то есть то, что обычно попадает в stdout и выводится на экран, в файл my_file . Если файл my_file не существует — он создаётся. Если файл существует — он перезаписывается.
Например, после выполнения команды ls > my_folder_contents.txt будет создан текстовый файл, содержащий список того, что находится в текущей рабочей директории.
Если вместо символа > воспользоваться конструкцией >> , то, при условии существования файла, в который перенаправляется вывод команды, этот файл перезаписан не будет. Данные будут добавлены в конец этого файла.
Теперь взглянем на конвейерную обработку данных.
То, что выводит одна команда, подаётся на вход другой команды. Это похоже на подключение одной трубы к другой
В Linux конвейерную обработку данных можно организовать с использованием практически любой правильно составленной команды. Часто говорят, что всё в Linux — это конвейер.
С помощью символа конвейера можно объединять в цепочку несколько команд. Выглядит это так:
Конвейер из нескольких команд можно сравнить с трубопроводом
Обратите внимание на то, что когда команда, находящаяся слева от символа | , выводит что-то в stdout , то, что она вывела, немедленно становится доступным в виде stdin второй команде. То есть оказывается, что, используя конвейер, мы имеем дело с параллельным выполнением команд. Иногда это может привести к неожиданным результатам. Подробности об этом можно почитать здесь.
Теперь поговорим о чтении данных из файлов и о выводе их на экран.
▍Чтение файлов
head my_file : считывает строки из начала файла и выводит их на экран. Читать можно не только содержимое файлов, но и то, что команды выводят в stdin , используя эту команду в качестве элемента конвейера.
tail my_file : считывает строки из конца файла. Эту команду тоже можно использовать в конвейере.
Head (голова) находится спереди, а tail (хвост) — сзади
Если вы работаете с данными, используя библиотеку pandas, тогда команды head и tail должны быть вам знакомы. Если это не так — взгляните на вышеприведённый рисунок, и вы без труда их запомните.
Рассмотрим другие способы чтения файлов, поговорим о команде cat .
Команда cat либо выводит содержимое файла на экран, либо конкатенирует несколько файлов. Это зависит от того, сколько файлов передано этой команде при вызове.
Команда cat
cat my_one_file.txt : когда этой команде передают один файл — она выводит его в stdout .
Если же передать ей два файла или большее количество файлов, то она ведёт себя по-другому.
cat my_file1.txt my_file2.txt : получив на вход несколько файлов эта команда конкатенирует их содержимое и выведет то, что получилось в stdout .
Если результат конкатенации файлов нужно сохранить в виде нового файла, можно воспользоваться оператором > :
Теперь поговорим о том, как удалять файлы и останавливать процессы.▍Удаление файлов, остановка процессов
rm my_file : удаляет файл my_file .
rm -r my_folder : удаляет папку my_folder и все содержащиеся в ней файлы и папки. Флаг -r указывает на то, что команда будет работать в рекурсивном режиме.
Для того чтобы система не запрашивала подтверждение при выполнении каждой операции удаления файла или папки, воспользуйтесь флагом -f .
kill 012345 : останавливает указанный выполняющийся процесс, давая ему время на корректное завершение работы.
kill -9 012345 : принудительно завершает указанный запущенный процесс. Флаг вида -s SIGKILL означает то же самое, что и флаг -9 .
▍Поиск
Для поиска данных можно использовать разные команды. В частности — grep , ag и ack . Начнём наше знакомство с этими командами с grep . Это — проверенная временем, надёжная команда, которая, правда, медленнее других и не так, как они, удобна в использовании.
Команда grep
grep my_regex my_file : выполняет поиск my_regex в my_file . При обнаружении совпадений возвращается, для каждого из них, вся строка. По умолчанию my_regex воспринимается как регулярное выражение.
grep -i my_regex my_file : поиск выполняется без учёта регистра символов.
grep -v my_regex my_file : возвращает все строки, в которых не содержится my_regex . Флаг -v означает инверсию, он напоминает оператор NOT , имеющийся во многих языках программирования.
grep -c my_regex my_file : возвращает сведения о количестве совпадений с искомым шаблоном, найденных в файле.
grep -R my_regex my_folder : выполняет рекурсивный поиск во всех файлах, находящихся в заданной папке и в папках, вложенных в неё.
Теперь поговорим о команде ag . Она появилась позже grep , она быстрее, работать с ней удобнее.
ag my_regex my_file : возвращает сведения о номерах строк, и сами строки, в которых найдены совпадения с my_regex .
ag -i my_regex my_file : поиск выполняется без учёта регистра символов.
Команда ag автоматически обрабатывает файл .gitignore и исключает из вывода то, что найдено в папках или файлах, перечисленных в этом файле. Это очень удобно.
ag my_regex my_file -- skip-vcs-ignores : содержимое файлов систем автоматического контроля версий (наподобие .gitignore ) при поиске не учитывается.
Кроме того, для того чтобы указать команде ag на то, какие пути к файлам нужно исключить из поиска, можно создать файл .agignore .
В начале этого раздела мы упомянули о команде ack . Команды ack и ag очень похожи, можно сказать, что они взаимозаменяемы на 99%. Однако команда ag работает быстрее, поэтому я описал именно её.
Теперь поговорим о работе с архивами.
▍Архивация
tar my_source_directory : объединяет файлы из папки my_source_directory в один файл tarball. Такие файлы удобно использовать для того, чтобы передавать кому-нибудь большие наборы файлов.
Команда tar
Tarball-файлы, создаваемые этой командой, представляют собой файлы с расширением .tar (Tape ARchive). То, что в названии команды и в расширении имён файлов, создаваемых ей, скрыто слово «tape» (лента), говорит о том, как давно существует эта команда.
tar -cf my_file.tar my_source_directory : создаёт tarball-файл с именем my_file.tar с содержимым папки my_source_directory . Флаг -c расшифровывается как «create» (создание), а флаг -f как «file» (файл).
Для извлечения файлов, находящихся в .tar -файле, используется команда tar c флагами -x («extract», извлечение) и -f («file», файл).
tar -xf my_file.tar : извлекает файлы из my_file.tar в текущую рабочую директорию.
Теперь поговорим о том, как сжимать и распаковывать .tar -файлы.
tar -cfz my_file.tar.gz my_source_directory : здесь, с использованием флага -z («zip», алгоритм сжатия) указано, что для сжатия файлов должен использоваться алгоритм gzip (GNU zip). Сжатие файлов позволяет экономить дисковое пространство при хранении таких файлов. Если же файлы планируется, например, передавать другим пользователям, это способствует более быстрой загрузке таких файлов.
Распаковать файл .tar.gz можно, добавив флаг -z к команде извлечения содержимого .tar -файлов, которую мы рассматривали выше. Выглядит это так:
tar -xfz my_file.tar.gz
Надо отметить, что у команды tar есть ещё множество полезных флагов.Bash-псевдонимы
Bash-псевдонимы (их ещё называют алиасами или сокращениями) предназначены для создания сокращённых наименований команд или их последовательностей, использование которых вместо обычных команд ускоряет работу. Если у вас, предположим, имеется псевдоним bu , за которым скрывается команда python setup.py sdist bdist_wheel , то для вызова данной команды достаточно воспользоваться этим псевдонимом.
Для создания подобного псевдонима достаточно добавить следующую команду в файл
Если в вашей системе нет файла/.bash_profile , то вы можете создать его самостоятельно, воспользовавшись командой touch . После создания псевдонима перезапустите терминал, после чего вы сможете этим псевдонимом пользоваться. В данном случае ввод двух символов заменяет ввод более чем трёх десятков символов команды, которая предназначена для сборки Python-пакетов.
/.bash_profile можно добавлять псевдонимы для любых часто используемых команд.
▍Итоги
В этом материале мы рассмотрели 21 популярную команду Bash и поговорили о создании псевдонимов для команд. Если вам эта тема интересна — вот цикл публикаций, посвящённый Bash. Здесь можно найти pdf-версию этих публикаций. Кроме того, если вы хотите освоить Bash, помните о том, что тут, как и при изучении любой другой программной системы, важна практика.
Читайте также: