У меня есть несколько сотен 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's wiki). По сути, расширение выдает команду (с ее параметрами), которая превышает предел ARG_MAX. В ядре 2.6.23 этот предел был установлен на уровне 128 кБ. Эта константа была увеличена, и вы можете получить ее значение, выполнив:
getconf ARG_MAX
# 2097152 # on 3.5.0-40-generic
Решение: Использование цикла for
Используйте цикл for, так как он рекомендован в BashFAQ/095 и нет никаких ограничений, кроме пространства оперативной/памяти. Проведите пробный запуск, чтобы убедиться, что он удалит то, что вы ожидаете:
for f in *.pdf; do echo rm "$f"; done
И выполните его:
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 просто выполнить необходимые системные вызовы самостоятельно, не используя внешний процесс, следовательно, быстрее.
Ответ 3
Если вы пытаетесь удалить очень большое количество файлов за один раз, вы, вероятно, столкнетесь с этой ошибкой:
/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
Linux