Удалить несколько файлов bash

Обновлено: 07.07.2024

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 badges

16 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:

    Я удаляю выбранные файлы журнала один за другим, используя команду rm -rf см. ниже:

    Есть ли другой способ, чтобы я мог сразу удалить выбранные файлы?

    У меня есть большой файл с несколькими тысячами столбцов. Я хочу удалить некоторые конкретные столбцы и разделители полей сразу с AWK в Bash. Я могу удалить один столбец за раз с помощью этого oneliner (столбец 3 будет удален и соответствующий ему разделитель полей): awk -vkf=3 -vFS=\t -vOFS=\t.

    Я хотел бы ежедневно отслеживать каталог на наличие новых файлов с помощью скрипта linux bash. Новые файлы добавляются в каталог каждые 4 часа или около того. Поэтому я хотел бы в конце дня обработать все файлы. Под процессом я подразумеваю преобразование их в альтернативный тип файла, а затем.

    Bash поддерживает все виды подстановочных знаков и расширений.

    Ваш точный случай будет обработан расширением скобки , например так:

    Вышеизложенное расширится до одной команды со всеми тремя аргументами и будет эквивалентно набору текста:

    Важно отметить, что это расширение выполняется shell, прежде чем rm будет даже загружен.

    Используйте подстановочный знак ( * ) для сопоставления нескольких файлов.

    Например, приведенная ниже команда удалит все файлы с именами, начинающимися с abc.log.2012-03- .

    Я бы рекомендовал запустить ls abc.log.2012-03-* , чтобы перечислить файлы, чтобы вы могли видеть, что вы собираетесь удалить, прежде чем запускать команду rm .

    Для получения более подробной информации см. справочную страницу Bash по расширению имени файла .

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

    Регулярные выражения являются более мощными, чем подстановочные знаки; вы можете передать выходные данные от grep до rm -f . Например, если некоторые имена файлов начинаются с "abc.log" , а некоторые-с "ABC.log" , то grep позволяет выполнить сопоставление без учета регистра:

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

    Когда я делаю это, я сначала запускаю команду ls | grep . и проверяю, что она выдает желаемый результат- особенно если я использую rm -f :

    где !! расширяется до предыдущей команды. Или я могу ввести стрелку вверх или Ctrl-P и отредактировать предыдущую строку, чтобы добавить команду rm -f .

    Это предполагает, что вы используете bash shell. Некоторые другие оболочки, в частности csh и tcsh, а также некоторые более старые оболочки, производные от sh, могут не поддерживать синтаксис $(. ) . Вы можете использовать эквивалентный синтаксис backtick:

    Синтаксис $(. ) легче читать, и если вы действительно амбициозны, он может быть вложенным.

    Наконец, если подмножество файлов, которые вы хотите удалить, не может быть легко выражено регулярным выражением, трюк, который я часто использую, состоит в том, чтобы перечислить файлы во временный текстовый файл, а затем отредактировать его:

    Затем я могу отредактировать файл list вручную, оставив только те файлы, которые я хочу удалить, а затем:

    (Опять же, это предполагает, что ни одно из имен файлов не содержит забавных символов, особенно пробелов.)

    Или, редактируя файл list , я могу добавить rm -f в начало каждой строки, а затем:

    Редактирование файла также дает возможность добавлять кавычки там, где это необходимо, например, изменяя rm -f foo bar на rm -f 'foo bar' .

    У меня есть каталог со многими каталогами внутри, каждый из которых имеет несколько файлов bz2 внутри. Как я могу распаковать их все в bash, используя bzip2 в их собственных каталогах сразу?

    у меня есть несколько файлов в папке (на linux) под названием picture20.mp4 picture21.mp4 picture100.mp4 picture115.mp4 e.t.c Я хотел бы увеличить число отображаемое в каждом имени на 3 так что у меня есть picture23.mp4 picture24.mp4 picture103.mp4 picture118.mp4 Я экспериментировал с командой.

    Просто используйте многострочный выбор в sublime, чтобы объединить все файлы в одну строку и добавить пробел между каждым именем файла, а затем добавить rm в начале списка. Это особенно полезно, когда в именах файлов, которые вы хотите удалить, нет шаблона.

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

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

    Я не гуру linux, но я полагаю, что вы хотите передать свой список выходных файлов в xargs rm -rf . Я использовал что-то подобное в прошлом с хорошими результатами. Сначала протестируйте пример каталога!

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

    если вы хотите удалить все файлы, принадлежащие каталогу сразу. Например: ваше имя каталога-"log", а каталог "log" включает файлы abc.log.2012-03-14, abc.log.2012-03-15. и т. д. Вы должны быть выше каталога журналов и:

    Похожие вопросы:

    Есть ли способ, подобный removeItemAtPath: который позволяет удалить сразу несколько файлов? Это должны быть не все файлы в каталоге, а только некоторые из них. E.g. вы передаете методу несколько.

    Я искал ответ повсюду, но не мог его найти. Можно ли переместить / удалить файл в папку корзины на linux. Но также и с файлом trashinfo. E.g я могу переместить файл в папку корзины нормально, но у.

    У меня есть большой файл с несколькими тысячами столбцов. Я хочу удалить некоторые конкретные столбцы и разделители полей сразу с AWK в Bash. Я могу удалить один столбец за раз с помощью этого.

    Я хотел бы ежедневно отслеживать каталог на наличие новых файлов с помощью скрипта linux bash. Новые файлы добавляются в каталог каждые 4 часа или около того. Поэтому я хотел бы в конце дня.

    У меня есть каталог со многими каталогами внутри, каждый из которых имеет несколько файлов bz2 внутри. Как я могу распаковать их все в bash, используя bzip2 в их собственных каталогах сразу?

    у меня есть несколько файлов в папке (на linux) под названием picture20.mp4 picture21.mp4 picture100.mp4 picture115.mp4 e.t.c Я хотел бы увеличить число отображаемое в каждом имени на 3 так что у.

    У меня есть куча маленьких CSV файлов (несколько сотен файлов около 100 MB каждый), которые я хочу упаковать в несколько больших файлов. Я знаю, как объединить все (или подмножество) этих файлов в.

    Как я могу разделить и записать вывод моей команды в Linux bash на несколько (текстовых) 5M файлов (количество файлов не имеет значения)?

    Дурачась с Linux на моем Pi, я столкнулся с этой проблемой. Я обнаружил, что вы можете удалить определенную строку с помощью команды history -d linenumber , но что делать, если я хочу удалить.

    у меня есть этот список файлов внутри Linux server:

    Я удаляю выбранные файлы журнала один за другим, используя команду rm -rf см. ниже:

    есть ли другой способ, чтобы я мог удалить выбранные файлы сразу??

    Bash поддерживает все виды подстановочных знаков и расширений.

    ваше точное дело будет обрабатываться фигурные скобки, например:

    больше расширить до одна команда со всеми тремя аргументами и эквивалентна набору:

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

    использовать подстановочный знак ( * ) для выбора нескольких файлов.

    например, следующая команда удаляет все файлы с именами, начинающимися с abc.log.2012-03- .

    Я бы рекомендовал под управлением ls abc.log.2012-03-* чтобы перечислить файлы, чтобы вы могли видеть, что вы собираетесь удалить перед запуском .

    для получения более подробной информации см. страницу bash man на расширение имени файла.

    если вы хотите удалить все файлы, имена которых соответствуют определенной форме, по шаблону (шаблон Глоб) является наиболее простым решением. Некоторые примеры:

    регулярные выражения более мощные, чем подстановочные знаки; вы можете кормить вывод grep to rm -f . Например, если некоторые имена файлов начинаются с "abc.log" и с "ABC.log" , grep можно сделать без учета регистра:

    когда я это делаю, я запускаю сначала проверьте, что он выдает результат, который я хочу -- особенно если я использую rm -f :

    здесь !! расширяется до предыдущей команды. Или я могу ввести стрелку вверх или Ctrl-P и отредактировать предыдущую строку, чтобы добавить .

    это предполагает, что вы используете оболочку Bash. Некоторые другие оболочки, особенно csh и tcsh и некоторые более старые оболочки SH, могут не поддерживать $(. ) синтаксис. Вы можете использовать эквивалентный backtick синтаксис:

    на $(. ) синтаксис легче читать, и если вы действительно амбициозны, он может быть вложен.

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

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

    (предполагается, что ни одно из имен файлов не содержит забавных символов, особенно пробелов.)

    или, при редактировании list файл, я могу добавить rm -f в начале каждой строки, а затем:

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

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

    просто используйте многострочный выбор в sublime, чтобы объединить все файлы в одну строку и добавить пробел между каждым именем файла, а затем добавить rm в начале списка. Это в основном полезно, когда в именах файлов, которые вы хотите удалить, нет шаблона.

    Я не гуру linux, но я считаю, что вы хотите передать свой список выходных файлов в xargs rm -rf . Я использовал нечто подобное в прошлом с хорошими результатами. Сначала проверьте пример каталога!

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

    Если вы хотите удалить все файлы, которые относятся к каталог сразу. Например; имя каталога yor - "log", а каталог" log " включает abc.бревно.2012-03-14, abc.бревно.2012-03-15. файлы ect. Вы должны быть над каталогом журнала и

    Я удалял выбранные файлы журнала один за другим, используя команду rm -rf , как показано ниже:

    Есть ли другой способ, чтобы я мог сразу удалить выбранные файлы?

    ОТВЕТЫ

    Ответ 1

    Bash поддерживает всевозможные подстановочные знаки и расширения.

    Ваш конкретный случай будет обрабатываться расширением брекета, например:

    Вышеописанное расширилось до единственной команды со всеми тремя аргументами и будет эквивалентно типу:

    Важно отметить, что это расширение выполняется оболочкой, еще до того, как rm загружен.

    Ответ 2

    Используйте подстановочный знак ( * ) для сопоставления нескольких файлов.

    Например, приведенная ниже команда удалит все файлы с именами, начинающимися с abc.log.2012-03- .

    Я бы рекомендовал запустить ls abc.log.2012-03-* , чтобы просмотреть файлы, чтобы вы могли видеть, что вы собираетесь удалить, прежде чем запускать команду rm .

    Подробнее см. справочную страницу Bash в расширении имени файла.

    Ответ 3

    Если вы хотите удалить все файлы, имена которых соответствуют определенной форме, шаблон подстановки (шаблон glob) является самым простым решением. Некоторые примеры:

    Регулярные выражения более мощные, чем подстановочные знаки; вы можете подать вывод grep на rm -f . Например, если некоторые имена файлов начинаются с "abc.log" , а некоторые с "abc.log" , grep позволяет вам нечувствительность к регистру:

    Когда я это делаю, сначала запускаю команду ls | grep . и проверяю, что она производит вывод, который я хочу, особенно если я использую rm -f :

    где !! расширяется до предыдущей команды. Или я могу набрать стрелку вверх или Ctrl-P и отредактировать предыдущую строку, чтобы добавить команду rm -f .

    Предполагается, что вы используете оболочку bash. Некоторые другие оболочки, в частности csh и tcsh и некоторые старые SH-производные оболочки, могут не поддерживать синтаксис $(. ) . Вы можете использовать эквивалентный синтаксис backtick:

    Синтаксис $(. ) легче читать, и если вы действительно амбициозны, он может быть вложен.

    Наконец, если подмножество файлов, которые вы хотите удалить, не может быть легко выражено с помощью обычного выражения, трюк, который я часто использую, - это перечислить файлы во временный текстовый файл, а затем отредактировать его:

    Затем я могу вручную отредактировать файл list , оставив только файлы, которые хочу удалить, а затем:

    (Предполагается, что ни одно из имен файлов не содержит забавных символов, особенно пробелов.)

    Или, редактируя файл list , я могу добавить rm -f в начало каждой строки, а затем:

    Ответ 4

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

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

    Ответ 5

    Просто используйте многострочный выбор в возвышенном, чтобы объединить все файлы в одну строку и добавить пробел между именами файлов, а затем добавить rm в начало списка. Это в основном полезно, если в именах файлов, которые вы хотите удалить, нет шаблона.

    Ответ 6

    Я не гуру linux, но я считаю, что вы хотите передать свой список выходных файлов на xargs rm -rf . Я использовал что-то подобное в прошлом с хорошими результатами. Сначала проверьте образец каталога.

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

    Читайте также: