your programing

rm, cp, mv 명령에 대한 인수 목록이 너무 김 오류

lovepro 2020. 10. 3. 11:25
반응형

rm, cp, mv 명령에 대한 인수 목록이 너무 김 오류


UNIX의 디렉토리 아래에 수백 개의 PDF가 있습니다. PDF의 이름이 정말 깁니다 (약 60 자).

다음 명령을 사용하여 모든 PDF를 함께 삭제하려고 할 때 :

rm -f *.pdf

다음과 같은 오류가 발생합니다.

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

이 오류에 대한 해결책은 무엇입니까? 이 오류가 mvcp명령에서도 발생합니까 ? 그렇다면 이러한 명령을 해결하는 방법은 무엇입니까?


이것이 발생하는 이유는 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

tl; dr

명령 줄 인수의 크기에 대한 커널 제한입니다. for대신 루프를 사용하십시오 .

문제의 기원

이것은 관련된 시스템 문제입니다 execveARG_MAX정수입니다. 이에 대한 많은 문서가 있습니다 ( man execve , debian 's wiki 참조 ).

기본적으로 확장 한계 를 초과 하는 명령 (파라미터 포함)을 생성합니다 ARG_MAX. 커널 2.6.23에서 제한은로 설정되었습니다 128 kB. 이 상수가 증가했으며 다음을 실행하여 값을 얻을 수 있습니다.

getconf ARG_MAX
# 2097152 # on 3.5.0-40-generic

솔루션 : for루프 사용

BashFAQ / 095for 에서 권장 하는 루프를 사용 하고 RAM / 메모리 공간을 제외하고 제한이 없습니다.

for f in *.pdf; do rm "$f"; done

또한 glob은 셸간에 강력하고 일관된 동작을 갖기 때문에 이식 가능한 접근 방식 입니다 (POSIX 사양의 일부 ).

참고 : 여러 의견에서 언급했듯이 이것은 실제로 더 느리지 만 유지 관리가 더 용이합니다. 예를 들어 하나 이상의 작업을 수행하려는 경우 와 같이 더 복잡한 시나리오를 적용 할 수 있기 때문 입니다.

솔루션 : 사용 find

당신이 주장한다면, "NUL로 구분되지 않은 입력을 읽을 때 위험하다 (파손, 악용 가능 등)" 때문에 xargs사용할 수는 find있지만 실제로 는 사용하지 마십시오 .

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

사용 -maxdepth 1 ... -delete대신하는 것은 -exec rm {} +허용 find단순히 때문에 빠르고, 외부 프로세스를 사용하지 않고 (덕분에 필요한 시스템 호출 자체를 실행하는 @chepner 주석 ).

참고 문헌


find-delete조치를 :

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

또 다른 대답은 xargs명령을 일괄 처리 하도록 강제 하는 것입니다. 예를 들어 한 번에 delete파일 100cd디렉토리로 이동하여 다음을 실행하십시오.

echo *.pdf | xargs -n 100 rm


또는 시도해 볼 수 있습니다.

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

한 번에 매우 많은 파일을 삭제하려는 경우 (오늘 485,000 개 이상의 디렉토리를 삭제했습니다) 다음 오류가 발생할 수 있습니다.

/bin/rm: Argument list too long.

문제는 당신이 무언가 같이 입력 할 때이다 rm -rf *의는 *"RF RM은 파일 1 파일 2 파일 3 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

당신은 이것을 시도 할 수 있습니다 :

for f in *.pdf
do
  rm $f
done

편집 : 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 파일을 엽니 다. 편집하고 위험한 파일 이름이 있는지 모든 행을 확인하고 발견되면 주석 처리하십시오.

그런 다음 작업 디렉토리에 dummy.sh 스크립트를 복사하고 실행하십시오.

이 모든 것은 보안상의 이유입니다.


이 칭찬을 사용할 수 있습니다

find -name "*.pdf"  -delete

bash 배열을 사용할 수 있습니다.

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

이렇게하면 단계 당 1000 개의 파일이 일괄 적으로 삭제됩니다.


은 rm 명령을 동시에 제거 할 수있는 파일의 제한이 있습니다.

One possibility you can remove them using multiple times the rm command bases on your file patterns, like:

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

You can also remove them through the find command:

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

If they are filenames with spaces or special characters, use:

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

This sentence search all files in the current directory (-maxdepth 1) with extension pdf (-name '*.pdf'), and then, delete each one (-exec rm "{}").

The expression {} replace the name of the file, and, "{}" set the filename as string, including spaces or special characters.


i was facing same problem while copying form source directory to destination

source directory had files ~3 lakcs

i used cp with option -r and it's worked for me

cp -r abc/ def/

it will copy all files from abc to def without giving warning of Argument list too long


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


I'm surprised there are no ulimit answers here. Every time I have this problem I end up here or here. I understand this solution has limitations but ulimit -s 65536 seems to often do the trick for me.


I ran into this problem a few times. Many of the solutions will run the rm command for each individual file that needs to be deleted. This is very inefficient:

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

I ended up writing a python script to delete the files based on the first 4 characters in the file-name:

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')

This worked very well for me. I was able to clear out over 2 million temp files in a folder in about 15 minutes. I commented the tar out of the little bit of code so anyone with minimal to no python knowledge can manipulate this code.


And another one:

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

printf is a shell builtin, and as far as I know it's always been as such. Now given that printf is not a shell command (but a builtin), it's not subject to "argument list too long ..." fatal error.

So we can safely use it with shell globbing patterns such as *.[Pp][Dd][Ff], then we pipe its output to remove (rm) command, through xargs, which makes sure it fits enough file names in the command line so as not to fail the rm command, which is a shell command.

The \0 in printf serves as a null separator for the file names wich are then processed by xargs command, using it (-0) as a separator, so rm does not fail when there are white spaces or other special characters in the file names.


I only know a way around this. The idea is to export that list of pdf files you have into a file. Then split that file into several parts. Then remove pdf files listed in each part.

ls | grep .pdf > list.txt
wc -l list.txt

wc -l is to count how many line the list.txt contains. When you have the idea of how long it is, you can decide to split it in half, forth or something. Using split -l command For example, split it in 600 lines each.

split -l 600 list.txt

this will create a few file named xaa,xab,xac and so on depends on how you split it. Now to "import" each list in those file into command rm, use this:

rm $(<xaa)
rm $(<xab)
rm $(<xac)

Sorry for my bad english.


Try this also If you wanna delete above 30/90 days (+) or else below 30/90(-) days files/folders then you can use the below ex commands

Ex: For 90days excludes above after 90days files/folders deletes, it means 91,92....100 days

find <path> -type f -mtime +90 -exec rm -rf {} \;

Ex: For only latest 30days files that you wanna delete then use the below command (-)

find <path> -type f -mtime -30 -exec rm -rf {} \;

If you wanna giz the files for more than 2 days files

find <path> -type f -mtime +2 -exec gzip {} \;

If you wanna see the files/folders only from past one month . Ex:

find <path> -type f -mtime -30 -exec ls -lrt {} \;

Above 30days more only then list the files/folders Ex:

find <path> -type f -mtime +30 -exec ls -lrt {} \;

find /opt/app/logs -type f -mtime +30 -exec ls -lrt {} \;

You can create a temp folder, move all the files and sub-folders you want to keep into the temp folder then delete the old folder and rename the temp folder to the old folder try this example until you are confident to do it live:

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

the rm -r big_folder will remove all files in the big_folder no matter how many. You just have to be super careful you first have all the files/folders you want to keep, in this case it was file1.pdf


To delete all *.pdf in a directory /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/

To delete specific files via rsync using wildcard is probably the fastest solution in case you've millions of files. And it will take care of error you're getting.


(Optional Step): DRY RUN. To check what will be deleted without deleting. `

rsync -avhn --delete --include '*.pdf' empty_dir/ /path/to/dir_with_pdf_files/

. . .

Click rsync tips and tricks for more rsync hacks


If you need to keep a server or system responsive while deleting a huge amount of files, sleep between each delete statement can be a good approach.

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

I found that for extremely large lists of files (>1e6), these answers were too slow. Here is a solution using parallel processing in python. I know, I know, this isn't linux... but nothing else here worked.

(This saved me hours)

# 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)

I have faced a similar problem when there were millions of useless log files created by an application which filled up all inodes. I resorted to "locate", got all the files "located"d into a text file and then removed them one by one. Took a while but did the job!


Suppose input directory name is input and output directory name is output. Then you can use simple loop to copy all

for f in input/*
do
cp $f output
done

I had the same problem with a folder full of temporary images that was growing day by day and this command helped me to clear the folder

find . -name "*.png" -mtime +50 -exec rm {} \;

The difference with the other commands is the mtime parameter that will take only the files older than X days (in the example 50 days)

Using that multiple times, decreasing on every execution the day range, I was able to remove all the unnecessary files


If you have similar problems with grep, the easiest solution is stepping one dir back and do a recursive search.

So instead of

grep "something" *

you can use:

cd ..
grep "something" -R search_in_this_dir/

Note it will recursively search subfolders of "search_in_this_dir" directory as well.


A bit safer version than using xargs, also not recursive: ls -p | grep -v '/$' | grep '\.pdf$' | while read file; do rm "$file"; done

Filtering our directories here is a bit unnecessary as 'rm' won't delete it anyway, and it can be removed for simplicity, but why run something that will definitely return error?


Using GNU parallel (sudo apt install parallel) is super easy

It runs the commands multithreaded where '{}' is the argument passed

E.g.

ls /tmp/myfiles* | parallel 'rm {}'


For remove first 100 files:

rm -rf 'ls | head -100'


The below option seems simple to this problem. I got this info from some other thread but it helped me.

for file in /usr/op/data/Software/temp/application/openpages-storage/*; do
    cp "$file" /opt/sw/op-storage/
done

Just run the above one command and it will do the task.

참고URL : https://stackoverflow.com/questions/11289551/argument-list-too-long-error-for-rm-cp-mv-commands

반응형