Список аргументов слишком длинный для команд 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

Просто запустите указанную выше команду, и она выполнит задачу.