it-swarm.xyz

Слишком длинный список аргументов для команд rm, cp, mv

У меня есть несколько сотен PDF-файлов в каталоге в UNIX. Названия PDF-файлов действительно длинные (около 60 символов).

Когда я пытаюсь удалить все PDF-файлы вместе, используя следующую команду:

rm -f *.pdf

Я получаю следующую ошибку:

/bin/rm: cannot execute [Argument list too long]

Как решить эту ошибку? Эта ошибка возникает также для команд mv и cp? Если да, как решить для этих команд?

474
Vicky

Причина этого заключается в том, что bash фактически расширяет звездочку на каждый соответствующий файл, создавая очень длинную командную строку.

Попробуй это:

find . -name "*.pdf" -print0 | xargs -0 rm

Предупреждение: это рекурсивный поиск, который также найдет (и удалит) файлы в подкаталогах. Прикрепите -f к команде rm, только если вы уверены, что не хотите подтверждения.

Чтобы сделать команду нерекурсивной, вы можете сделать следующее:

find . -maxdepth 1 -name "*.pdf" -print0 | xargs -0 rm

Другой вариант - использовать флаг -delete команды find:

find . -name "*.pdf" -delete
679
DPlusV

тЛ; др

Это ограничение ядра на размер аргумента командной строки. Вместо этого используйте цикл for.

Происхождение проблемы

Это системная проблема, связанная с константами execve и ARG_MAX. Об этом много документации (см. man execve , вики Debian ). 

По сути, расширение создает команду command (с ее параметрами), которая превышает предел ARG_MAX . В ядре 2.6.23 предел был установлен равным 128 kB. Эта константа была увеличена, и вы можете получить ее значение, выполнив:

getconf ARG_MAX
# 2097152 # on 3.5.0-40-generic

Решение: Использование for Loop

Используйте цикл 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 comment ).

Рекомендации

295
Édouard Lopez

find имеет действие -delete:

find . -maxdepth 1 -name '*.pdf' -delete
172
ThiefMaster

Другой ответ - заставить xargs обрабатывать команды в пакетном режиме. Например, delete файлы 100 одновременно, cd в каталог и запустите это:

echo *.pdf | xargs -n 100 rm

18
portforwardpodcast

Или вы можете попробовать:

find . -name '*.pdf' -exec rm -f {} \;
10
Jon Lin

вы можете попробовать это:

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 в рабочий каталог и запустите его.

Все это по соображениям безопасности.

8
BigMike

Если вы пытаетесь удалить очень большое количество файлов за один раз (сегодня я удалил каталог с 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

... или даже показать, сколько файлов будет удалено, а затем время, необходимое для их удаления:

[email protected]# ls -1 | wc -l && time find . -type f -delete
100000
real    0m3.660s
user    0m0.036s
sys     0m0.552s
7
Bibin Joseph

Вы можете использовать массив bash:

files=(*.pdf)
for((I=0;I<${#files[*]};I+=1000)); do rm -f ${files[@]:I:1000}; done

Таким образом, он будет стирать партиями по 1000 файлов за шаг.

5
danjperron

вы можете использовать эту рекомендацию 

find -name "*.pdf"  -delete
4
Sarath Ak

find . -type f -name '*xxx' -print -delete

3
pigletfly

Команда rm имеет ограничение на количество файлов, которые вы можете удалить одновременно.

Одна из возможностей, которую вы можете удалить, используя несколько раз команду rm, основана на ваших шаблонах файлов, например:

rm -f A*.pdf
rm -f B*.pdf
rm -f C*.pdf
...
rm -f *.pdf

Вы также можете удалить их через команду find:

find . -name "*.pdf" -exec rm {} \;
3
Fabio Farath

Если это имена файлов с пробелами или специальными символами, используйте:

find -maxdepth 1 -name '*.pdf' -exec rm "{}" \;

Это предложение ищет все файлы в текущем каталоге (-maxdepth 1) с расширением pdf (-name '* .pdf'), а затем удаляет каждый из них (-exec rm "{}").

Выражение {} заменяет имя файла, а "{}" задает имя файла в виде строки, включая пробелы или специальные символы.

3
Alejandro Salamanca Mazuelo

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

исходный каталог имел файлы ~ 3 lakcs

я использовал cp с опцией -r и это работает для меня

cp -r abc/def/

он скопирует все файлы из abc в def без предупреждения о слишком длинном списке аргументов

2
user3405020

И еще один:

cd  /path/to/pdf
printf "%s\0" *.[Pp][Dd][Ff] | xargs -0 rm

printf - это встроенная оболочка, и, насколько я знаю, она всегда была таковой. Теперь, учитывая, что printf не является командой Shell (но является встроенной), она не подвержена фатальной ошибке «argument list too long ...».

Таким образом, мы можем безопасно использовать его с шаблонами глобализации Shell, такими как *.[Pp][Dd][Ff], затем передаем по выводу команду на удаление (rm) через xargs, чтобы убедиться, что он соответствует достаточному количеству имен файлов в командной строке, чтобы не пропустить команду rm, которая является командой Shell.

\0 в printf служит нулевым разделителем для имен файлов, которые затем обрабатываются командой xargs, используя его (-0) в качестве разделителя, поэтому rm не завершается ошибкой, если в именах файлов есть пробелы или другие специальные символы. 

1
lind

Я удивлен, что здесь нет ulimit ответов. Каждый раз, когда у меня возникает эта проблема, я в конечном итоге здесь или здесь . Я понимаю, что это решение имеет ограничения, но ulimit -s 65536, кажется, часто помогает мне.

1
dps

Я столкнулся с этой проблемой несколько раз. Многие из решений будут запускать команду 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 или не обладал ими, мог манипулировать этим кодом.

1
Pedro Montero

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

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 

0
Keithhn

Попробуйте это также. Если вы хотите удалить файлы/папки за 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 {} \;
0
raja

Я столкнулся с подобной проблемой, когда приложение создавало миллионы бесполезных файлов журналов, которые заполняли все иноды. Я прибег к «locate», собрал все файлы, «расположенные» d, в текстовый файл, а затем удалил их один за другим. Потребовалось время, но сделал работу!

0
asatsi

Я знаю только способ обойти это .. Идея состоит в том, чтобы экспортировать этот список 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)

Извините за мой плохой английский.

0
user219776

Я обнаружил, что для очень больших списков файлов (> 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 __== '__main__':
    for file_name in files_names:
        print(file_name)
        pool.apply_async(delete_files,[file_name], error_callback=callback_error)
0
mmann1123

Удалить все *.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

0
Raman Kathpalia

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

find . -name "*.pdf" -print0 | while read -d $'\0' file
do
    rm "$file"
    sleep 0.005 # Sleeps for 5ms, Tweak as needed
done
0
Ecker00