Список аргументов слишком длинный для команд rm, cp, mv
У меня есть несколько сотен PDF файлов под каталогом в UNIX. Имена файлов PDF очень длинные (около 60 символов).
Когда я пытаюсь удалить все файлы PDF вместе, используя следующую команду:
rm -f *.pdf
Я получаю следующую ошибку:
/bin/rm: cannot execute [Argument list too long]
Каково решение этой ошибки?
Происходит ли эта ошибка для команд mv
и cp
? Если да, как решить эту команду?
Ответы
Ответ 1
Причина этого заключается в том, что bash фактически расширяет звездочку на каждый соответствующий файл, создавая очень длинную командную строку.
Попробуй это:
find . -name "*.pdf" -print0 | xargs -0 rm
Предупреждение: это рекурсивный поиск, который также найдет (и удалит) файлы в подкаталогах. -f
к команде rm, только если вы уверены, что не хотите подтверждения.
Чтобы сделать команду нерекурсивной, вы можете сделать следующее:
find . -maxdepth 1 -name "*.pdf" -print0 | xargs -0 rm
Другой вариант - использовать флаг find -delete
:
find . -name "*.pdf" -delete
Ответ 2
ТЛ; др
Это ограничение ядра на размер аргумента командной строки. Вместо этого используйте цикл for
.
Происхождение проблемы
Это системная проблема, связанная с константой execve
и ARG_MAX
. Об этом много документации (см. Man execve, Debian Wiki).
По сути, расширение создает команду (с ее параметрами), которая превышает предел 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).
Рекомендации
Ответ 3
find
имеет действие -delete
:
find . -maxdepth 1 -name '*.pdf' -delete
Ответ 4
Другой ответ - заставить xargs
обрабатывать команды в партиях. Например, в delete
файлы 100
за раз, cd
в каталог и запустите это:
echo *.pdf | xargs -n 100 rm
Ответ 5
Или вы можете попробовать:
find . -name '*.pdf' -exec rm -f {} \;
Ответ 6
вы можете попробовать следующее:
for f in *.pdf
do
rm $f
done
EDIT:
Комментарий ThiefMaster предлагает мне не раскрывать такую опасную практику молодым оболочка jedis, поэтому я добавлю более "безопасную" версию (ради сохранения вещей, когда у кого-то есть файл "-rf...pdf" )
echo "# Whooooo" > /tmp/dummy.sh
for f in '*.pdf'
do
echo "rm -i $f" >> /tmp/dummy.sh
done
После запуска выше, просто откройте файл /tmp/dummy.sh в своем fav. редактор и проверять каждую строку для опасных имен файлов, комментируя их, если они найдены.
Затем скопируйте dummy.sh script в свой рабочий каталог и запустите его.
Все это по соображениям безопасности.
Ответ 7
Если вы пытаетесь удалить очень большое количество файлов за один раз (сегодня я удалил каталог с 485, 000+), вы, вероятно, столкнетесь с этой ошибкой:
/bin/rm: Argument list too long.
Проблема в том, что когда вы rm -rf *
что-то вроде 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
... или даже показать, сколько файлов будет удалено, а затем время, необходимое для их удаления:
[email protected]# ls -1 | wc -l && time find . -type f -delete
100000
real 0m3.660s
user 0m0.036s
sys 0m0.552s
Ответ 8
Вы можете использовать массив bash:
files=(*.pdf)
for((I=0;I<${#files[@]};I+=1000)); do
rm -f "${files[@]:I:1000}"
done
Таким образом, он будет стирать партиями по 1000 файлов за шаг.
Ответ 9
вы можете использовать эту оценку
find -name "*.pdf" -delete
Ответ 10
У команды rm есть ограничение на количество файлов, которые вы можете удалить одновременно.
Одна возможность, вы можете удалить их, используя несколько раз команды rm, основанные на ваших шаблонах файлов, например:
rm -f A*.pdf
rm -f B*.pdf
rm -f C*.pdf
...
rm -f *.pdf
Вы также можете удалить их с помощью команды поиска:
find . -name "*.pdf" -exec rm {} \;
Ответ 11
Если они являются именами файлов с пробелами или специальными символами, используйте:
find -maxdepth 1 -name '*.pdf' -exec rm "{}" \;
Это предложение ищет все файлы в текущем каталоге (-maxdepth 1) с расширением pdf (-name '*.pdf'), а затем удаляет каждый из них (-exec rm "{}" ).
Выражение {} заменит имя файла, а "{}" задает имя файла как строку, включая пробелы или специальные символы.
Ответ 12
i столкнулась с такой же проблемой при копировании исходного каталога исходного кода в пункт назначения
исходный каталог имел файлы ~ 3 lakcs
я использовал cp с опцией -r, и это сработало для меня
cp -r abc/def/
он скопирует все файлы из abc в def без слишком долгого предупреждения о списке аргументов
Ответ 13
find. -type f -name '*xxx' -print -delete
Ответ 14
Я удивлен, что здесь нет ulimit
ответов. Каждый раз, когда у меня возникает эта проблема, я оказываюсь здесь или здесь. Я понимаю, что это решение имеет ограничения, но ulimit -s 65536
кажется, часто ulimit -s 65536
мне.
Ответ 15
И еще один:
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
не -0
ошибкой, если в файле есть пробелы или другие специальные символы имена.
Ответ 16
Я столкнулся с этой проблемой несколько раз. Многие из решений будут запускать команду rm
для каждого отдельного файла, который необходимо удалить. Это очень неэффективно:
find . -name "*.pdf" -print0 | xargs -0 rm -rf
В итоге я написал python script для удаления файлов на основе первых 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 000 файлов temp в папке примерно через 15 минут. Я прокомментировал tar из небольшого числа кода, поэтому любой, обладающий минимальным знанием питона, может манипулировать этим кодом.
Ответ 17
Я знаю только об этом.
Идея состоит в том, чтобы экспортировать этот список файлов PDF, которые у вас есть, в файл. Затем разделите этот файл на несколько частей. Затем удалите pdf файлы, перечисленные в каждой части.
ls | grep .pdf > list.txt
wc -l list.txt
wc -l - подсчитать, сколько строк содержится в файле list.txt. Когда у вас есть представление о том, как долго это происходит, вы можете решить разделить его на полтора-четвертого или что-то еще. Использование команды split -l
Например, разделите его по 600 строк.
split -l 600 list.txt
это создаст несколько файлов с именем xaa, xab, xac и т.д., зависит от того, как вы его разделяете.
Теперь, чтобы "импортировать" каждый список в этот файл в команду rm, используйте это:
rm $(<xaa)
rm $(<xab)
rm $(<xac)
Извините за мой плохой английский.
Ответ 18
Я обнаружил, что для очень больших списков файлов (> 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)
Ответ 19
Попробуйте это также. Если вы хотите удалить более 30/90 дней (+) или еще ниже 30/90 (-) дней файлов/папок, вы можете использовать приведенные ниже команды ex
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 {} \;
Если вы хотите giz файлы для файлов более чем на 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 {} \;
Ответ 20
Удалить все *.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 хаков
Ответ 21
Вы можете создать временную папку, переместить все файлы и подпапки, которые вы хотите сохранить, во временную папку, затем удалить старую папку и переименовать временную папку в старую папку, попробуйте этот пример, пока вы не будете уверены, что все это работает:
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
Ответ 22
Если вам необходимо обеспечить отзывчивость сервера или системы при удалении огромного количества файлов, хорошим подходом может быть sleep
между каждым оператором удаления.
find . -name "*.pdf" -print0 | while read -d $'\0' file
do
rm "$file"
sleep 0.005 # Sleeps for 5ms, tweak as needed
done
Ответ 23
Я столкнулся с подобной проблемой, когда приложение создавало миллионы бесполезных файлов журналов, которые заполняли все иноды. Я прибег к "locate", собрал все файлы, "расположенные" d, в текстовый файл, а затем удалил их один за другим. Потребовалось время, но сделал работу!
Ответ 24
Предположим, что введено имя входного каталога и выведено имя выходного каталога.
Затем вы можете использовать простой цикл для копирования всех
for f in input/*
do
cp $f output
done
Ответ 25
У меня была та же проблема с папкой, полной временных изображений, которые росли день ото дня, и эта команда помогла мне очистить папку
find . -name "*.png" -mtime +50 -exec rm {} \;
Разница с другими командами - это параметр mtime, который будет принимать только файлы старше X дней (в примере 50 дней)
Используя это несколько раз, уменьшая при каждом выполнении дневной диапазон, я смог удалить все ненужные файлы
Ответ 26
Если у вас есть похожие проблемы с grep, самым простым решением является переход на один каталог обратно и выполнение рекурсивного поиска.
Итак, вместо
grep "something" *
вы можете использовать:
cd ..
grep "something" -R search_in_this_dir/
Обратите внимание, что он также будет рекурсивно искать подпапки в каталоге "search_in_this_dir".
Ответ 27
Более безопасная версия, чем использование xargs, также не рекурсивная:
ls -p | grep -v '/$' | grep '\.pdf$' | while read file; do rm "$file"; done
Фильтрация наших каталогов здесь немного не нужна, так как "rm" в любом случае не удалит ее, и ее можно удалить для простоты, но зачем запускать что-то, что обязательно вернет ошибку?
Ответ 28
Использование GNU parallel (sudo apt install parallel
) очень просто
Он выполняет команды многопоточности, где '{}' - это аргумент, переданный
например.
ls /tmp/myfiles* | parallel 'rm {}'
Ответ 29
Для удаления первых 100 файлов:
rm -rf 'ls | голова -100 '
Ответ 30
Этот параметр кажется простым для этой проблемы. Я получил эту информацию из какой-то другой темы, но это помогло мне.
for file in /usr/op/data/Software/temp/application/openpages-storage/*; do
cp "$file" /opt/sw/op-storage/
done
Просто запустите указанную выше команду, и она выполнит задачу.