Слишком длинный список аргументов для команд rm, cp, mv
У меня есть несколько сотен PDF-файлов в каталоге в UNIX. Названия PDF-файлов действительно длинные (около 60 символов).
Когда я пытаюсь удалить все PDF-файлы вместе, используя следующую команду:
rm -f *.pdf
Я получаю следующую ошибку:
/bin/rm: cannot execute [Argument list too long]
Каково решение этой ошибки? Эта ошибка возникает для mv
а также cp
команды тоже? Если да, как решить для этих команд?
35 ответов
Причина этого заключается в том, что bash фактически расширяет звездочку на каждый соответствующий файл, создавая очень длинную командную строку.
Попробуй это:
find . -name "*.pdf" -print0 | xargs -0 rm
Предупреждение: это рекурсивный поиск, который также найдет (и удалит) файлы в подкаталогах. Тэкс на -f
в команду rm, только если вы уверены, что не хотите подтверждения.
Чтобы сделать команду нерекурсивной, вы можете сделать следующее:
find . -maxdepth 1 -name "*.pdf" -print0 | xargs -0 rm
Другой вариант заключается в использовании поиска -delete
флаг:
find . -name "*.pdf" -delete
ТЛ; др
Это ограничение ядра на размер аргумента командной строки. Использовать for
цикл вместо
Происхождение проблемы
Это системная проблема, связанная с execve
а также ARG_MAX
постоянная. Об этом много документации (см. Man execve, вики Debian).
По сути, расширение создает команду (с ее параметрами), которая превышает ARG_MAX
предел. По ядру 2.6.23
предел был установлен на 128 kB
, Эта константа была увеличена, и вы можете получить ее значение, выполнив:
getconf ARG_MAX
# 2097152 # on 3.5.0-40-generic
Решение: Использование for
петля
Использовать for
Цикл, как это рекомендовано для BashFAQ / 095, и нет ограничений, кроме объема памяти и памяти:
for f in *.pdf; do rm "$f"; done
Также это переносимый подход, поскольку у glob сильное и согласованное поведение среди оболочек ( часть спецификации POSIX).
Примечание. Как отмечается в нескольких комментариях, это действительно медленнее, но более приемлемо, поскольку может адаптировать более сложные сценарии, например, когда требуется выполнить больше, чем одно действие.
Решение: Использование find
Если вы настаиваете, вы можете использовать find
но на самом деле не используйте xargs, так как он "опасен (сломан, может использоваться и т. д.) при чтении ввода, не разделенного NUL":
find . -maxdepth 1 -name '*.pdf' -delete
С помощью -maxdepth 1 ... -delete
вместо -exec rm {} +
позволяет find
просто выполнить необходимые системные вызовы самостоятельно, без использования внешнего процесса, следовательно, быстрее (благодаря комментарию @chepner).
Рекомендации
- Я получаю "Список аргументов слишком длинный". Как я могу обработать большой список кусками? @ wooledge
- execve (2) - справочная страница по Linux (поиск ARG_MAX);
- Ошибка: слишком длинный список аргументов @ вики Debian;
- Почему я получаю "/bin/sh: список аргументов слишком длинный" при передаче аргументов в кавычках? @ SuperUser
find
имеет -delete
действие:
find . -maxdepth 1 -name '*.pdf' -delete
Другой ответ - заставить xargs
обрабатывать команды в пакетном режиме. Например, чтобы delete
файлы 100
вовремя, cd
в каталог и запустите это:
echo *.pdf | xargs -n 100 rm
Если вы пытаетесь удалить очень большое количество файлов за один раз (сегодня я удалил каталог с 485 000+), вы, вероятно, столкнетесь с этой ошибкой:
/bin/rm: Argument list too long.
Проблема в том, что когда вы печатаете что-то вроде rm -rf *
, *
заменяется списком каждого соответствующего файла, например "rm -rf file1 file2 file3 file4" и так далее. Для хранения этого списка аргументов имеется относительно небольшой буфер памяти, и если он заполнен, оболочка не выполнит программу.
Чтобы обойти эту проблему, многие люди используют команду find, чтобы найти каждый файл и передать их один за другим команде "rm", например так:
find . -type f -exec rm -v {} \;
Моя проблема в том, что мне нужно было удалить 500 000 файлов, и это заняло слишком много времени.
Я наткнулся на гораздо более быстрый способ удаления файлов - команда "find" имеет встроенный флаг "-delete"! Вот что я в итоге использовал:
find . -type f -delete
Используя этот метод, я удалял файлы со скоростью около 2000 файлов в секунду - намного быстрее!
Вы также можете показать имена файлов при их удалении:
find . -type f -print -delete
... или даже показать, сколько файлов будет удалено, а затем время, необходимое для их удаления:
root@devel# ls -1 | wc -l && time find . -type f -delete
100000
real 0m3.660s
user 0m0.036s
sys 0m0.552s
Для того, у кого нет времени.Выполните следующую команду на терминале.
ulimit -S -s unlimited
Затем выполните операцию cp/mv/rm.
Или вы можете попробовать:
find . -name '*.pdf' -exec rm -f {} \;
Вы можете попробовать это:
for f in *.pdf
do
rm $f
done
РЕДАКТИРОВАТЬ: комментарий ThiefMaster предлагает мне не раскрывать такую опасную практику для джедаев молодой оболочки, поэтому я добавлю более "более безопасную" версию (ради сохранения вещей, когда у кого-то есть файл "-rf . ..Pdf")
echo "# Whooooo" > /tmp/dummy.sh
for f in '*.pdf'
do
echo "rm -i $f" >> /tmp/dummy.sh
done
После запуска вышеупомянутого, просто откройте файл /tmp/dummy.sh в вашем фаворе. редактировать и проверять каждую строку на наличие опасных имен файлов, комментируя их, если они найдены.
Затем скопируйте скрипт dummy.sh в ваш рабочий каталог и запустите его.
Все это по соображениям безопасности.
Вы можете использовать массив bash:
files=(*.pdf)
for((I=0;I<${#files[*]};I+=1000)); do rm -f ${files[@]:I:1000}; done
Таким образом, он будет стирать партиями по 1000 файлов за шаг.
Вы можете использовать эту рекомендацию
find -name "*.pdf" -delete
Если это имена файлов с пробелами или специальными символами, используйте:
find -maxdepth 1 -name '*.pdf' -exec rm "{}" \;
Это предложение ищет все файлы в текущем каталоге (-maxdepth 1) с расширением pdf (-name '*.pdf'), а затем удаляет каждый из них (-exec rm "{}").
Выражение {} заменяет имя файла, а "{}" задает имя файла в виде строки, включая пробелы или специальные символы.
У команды rm есть ограничение на количество файлов, которые вы можете удалить одновременно.
Одна возможность, вы можете удалить их, используя несколько раз команды rm, основанные на ваших шаблонах файлов, например:
rm -f A*.pdf
rm -f B*.pdf
rm -f C*.pdf
...
rm -f *.pdf
Вы также можете удалить их с помощью команды find:
find . -name "*.pdf" -exec rm {} \;
И еще один:
cd /path/to/pdf
printf "%s\0" *.[Pp][Dd][Ff] | xargs -0 rm
printf
это встроенная оболочка, и, насколько я знаю, она всегда была таковой. Теперь, учитывая, что printf
это не команда оболочки (а встроенная), она не подлежитargument list too long ...
" фатальная ошибка.
Таким образом, мы можем безопасно использовать его с такими шаблонами, как *.[Pp][Dd][Ff]
, тогда мы передадим свой вывод, чтобы удалить (rm
) через xargs
, который гарантирует, что он соответствует достаточно именам файлов в командной строке, чтобы не потерпеть неудачу rm
команда, которая является командой оболочки.
\0
в printf
служит нулевым разделителем для имен файлов, которые затем обрабатываются xargs
команда, использующая его (-0
) как разделитель, так rm
не вызывает ошибку, если в именах файлов есть пробелы или другие специальные символы.
А как насчет более короткого и надежного?
for i in **/*.pdf; do rm "$i"; done
Попробуйте это также. Если вы хотите удалить файлы / папки более 30/90 (+) или менее 30/90(-) дней, то вы можете использовать приведенные ниже команды ex
Пример: для 90 дней исключается выше после 90 дней удаления файлов / папок, это означает 91,92....100 дней
find <path> -type f -mtime +90 -exec rm -rf {} \;
Пример: только для последних 30-дневных файлов, которые вы хотите удалить, используйте команду ниже (-)
find <path> -type f -mtime -30 -exec rm -rf {} \;
Если вы хотите посмотреть файлы более чем на 2 дня
find <path> -type f -mtime +2 -exec gzip {} \;
Если вы хотите видеть файлы / папки только за последний месяц. Пример:
find <path> -type f -mtime -30 -exec ls -lrt {} \;
Более 30 дней больше, чем только список файлов / папок. Например:
find <path> -type f -mtime +30 -exec ls -lrt {} \;
find /opt/app/logs -type f -mtime +30 -exec ls -lrt {} \;
Я столкнулся с той же проблемой при копировании исходного каталога формы в место назначения
исходный каталог имел файлы ~3 lakcs
я использовал cp с опцией -r, и это сработало для меня
cp -r abc/ def/
он скопирует все файлы из abc в def без предупреждения о слишком длинном списке аргументов
Список аргументов слишком длинный
Как заголовок этого вопроса для
cp
,
mv
и
rm
, но ответ в основном означает
rm
.
Команды un*x
Внимательно прочтите справочную страницу команды!
За
cp
и
mv
, Существует
-t
переключатель, для цели:
find . -type f -name '*.pdf' -exec cp -ait "/path to target" {} +
и
find . -type f -name '*.pdf' -exec mv -t "/path to target" {} +
Скриптовый путь
В сценарии bash используется общее решение:
#!/bin/bash
folder=( "/path to folder" "/path to anther folder" )
[ "$1" = "--run" ] && exec find "${target[@]}" -type f -name '*.pdf' -exec $0 {} +
for file ;do
printf "Doing something with '%s'.\n" "$file"
done
Я столкнулся с этой проблемой несколько раз. Многие из решений будут запускать rm
Команда для каждого отдельного файла, который необходимо удалить. Это очень неэффективно:
find . -name "*.pdf" -print0 | xargs -0 rm -rf
В итоге я написал скрипт на python для удаления файлов на основе первых 4 символов в имени файла:
import os
filedir = '/tmp/' #The directory you wish to run rm on
filelist = (os.listdir(filedir)) #gets listing of all files in the specified dir
newlist = [] #Makes a blank list named newlist
for i in filelist:
if str((i)[:4]) not in newlist: #This makes sure that the elements are unique for newlist
newlist.append((i)[:4]) #This takes only the first 4 charcters of the folder/filename and appends it to newlist
for i in newlist:
if 'tmp' in i: #If statment to look for tmp in the filename/dirname
print ('Running command rm -rf '+str(filedir)+str(i)+'* : File Count: '+str(len(os.listdir(filedir)))) #Prints the command to be run and a total file count
os.system('rm -rf '+str(filedir)+str(i)+'*') #Actual shell command
print ('DONE')
Это сработало очень хорошо для меня. Мне удалось очистить более 2 миллионов временных файлов в папке примерно за 15 минут. Я прокомментировал tar из небольшого фрагмента кода, чтобы любой, кто имеет минимальные знания Python или не обладал ими, мог манипулировать этим кодом.
я решил с
for
я на
macOS
с
zsh
Я перевел только тысячи
jpg
файлы. В пределах
mv
в одной строке команды.
Убедитесь, что в имени файлов, которые вы пытаетесь переместить, нет пробелов или специальных символов.
for i in $(find ~/old -type f -name "*.jpg"); do mv $i ~/new; done
Вы можете создать временную папку, переместить все файлы и подпапки, которые вы хотите сохранить, во временную папку, затем удалить старую папку и переименовать временную папку в старую папку, попробуйте этот пример, пока вы не будете уверены, что все это работает:
mkdir testit
cd testit
mkdir big_folder tmp_folder
touch big_folder/file1.pdf
touch big_folder/file2.pdf
mv big_folder/file1,pdf tmp_folder/
rm -r big_folder
mv tmp_folder big_folder
rm -r big_folder
удалит все файлы в big_folder
независимо от того, сколько. Вы просто должны быть очень осторожны, у вас сначала есть все файлы / папки, которые вы хотите сохранить, в данном случае это было file1.pdf
Я обнаружил, что для очень больших списков файлов (>1e6) эти ответы были слишком медленными. Вот решение, использующее параллельную обработку в Python. Я знаю, я знаю, что это не Linux... но ничего другого здесь не работает.
(Это спасло меня часы)
# delete files
import os as os
import glob
import multiprocessing as mp
directory = r'your/directory'
os.chdir(directory)
files_names = [i for i in glob.glob('*.{}'.format('pdf'))]
# report errors from pool
def callback_error(result):
print('error', result)
# delete file using system command
def delete_files(file_name):
os.system('rm -rf ' + file_name)
pool = mp.Pool(12)
# or use pool = mp.Pool(mp.cpu_count())
if __name__ == '__main__':
for file_name in files_names:
print(file_name)
pool.apply_async(delete_files,[file_name], error_callback=callback_error)
Удалить все *.pdf
в каталоге /path/to/dir_with_pdf_files/
mkdir empty_dir # Create temp empty dir
rsync -avh --delete --include '*.pdf' empty_dir/ /path/to/dir_with_pdf_files/
Чтобы удалить определенные файлы с помощью rsync
Использование подстановочного знака, вероятно, самое быстрое решение, если у вас миллионы файлов. И это позаботится об ошибке, которую вы получаете.
(Необязательный шаг): DRY RUN. Чтобы проверить, что будет удалено без удаления. `
rsync -avhn --delete --include '*.pdf' empty_dir/ /path/to/dir_with_pdf_files/
,,,
Нажмите rsync советы и хитрости для более rsync хаков
Если вы хотите удалить и файлы, и каталоги, вы можете использовать что-то вроде:
echo /path/* | xargs rm -rf
У меня была та же проблема с папкой, полной временных изображений, которая росла день ото дня, и эта команда помогла мне очистить папку
find . -name "*.png" -mtime +50 -exec rm {} \;
Разница с другими командами заключается в параметре mtime, который будет принимать только файлы старше X дней (в примере 50 дней)
Используя это несколько раз, уменьшая при каждом выполнении дневной диапазон, я смог удалить все ненужные файлы
Я знаю только способ обойти это. Идея состоит в том, чтобы экспортировать этот список файлов PDF в ваш файл. Затем разделите этот файл на несколько частей. Затем удалите файлы PDF, перечисленные в каждой части.
ls | grep .pdf > list.txt
wc -l list.txt
wc -l - подсчитать, сколько строк содержит список.txt. Когда у вас есть представление о том, как долго это происходит, вы можете разделить его пополам, вперёд или что-то в этом роде. Использование команды split -l Например, разбить его на 600 строк каждая.
split -l 600 list.txt
это создаст несколько файлов с именами xaa,xab,xac и т. д., в зависимости от того, как вы разделите его. Теперь, чтобы "импортировать" каждый список в этом файле в команду rm, используйте это:
rm $(<xaa)
rm $(<xab)
rm $(<xac)
Извините за мой плохой английский.
Если вам необходимо обеспечить отзывчивость сервера или системы при удалении огромного количества файлов, sleep
между каждым оператором удаления может быть хороший подход.
find . -name "*.pdf" -print0 | while read -d $'\0' file
do
rm "$file"
sleep 0.005 # Sleeps for 5ms, tweak as needed
done
Я столкнулся с подобной проблемой, когда приложение создавало миллионы бесполезных файлов журналов, которые заполняли все иноды. Я прибег к "locate", собрал все файлы, "расположенные" d, в текстовый файл, а затем удалил их один за другим. Потребовалось время, но сделал работу!