파일을 복사 할 때 인수 목록이 너무 깁니다


26

방금 특정 확장자의 파일을 계산하는 방법과 관련된 질문 을했습니다. 이제이 cp파일들을 새로운 파일로 만들고 싶습니다 dir.

노력하고 있습니다

cp *.prj ../prjshp/

cp * | grep '\.prj$' ../prjshp/

그러나 그들은 같은 오류를주고 있습니다.

bash : / bin / cp : 인수 목록이 너무 깁니다

어떻게 복사합니까?


답변:


36

cp *.prj ../prjshp/올바른 명령이지만 크기 제한이있는 드문 경우입니다. 두 번째로 시도한 명령은 의미가 없습니다.

한 가지 방법은 cp파일을 청크 로 실행 하는 것입니다. find명령은이 작업을 수행하는 방법을 알고있다 :

find -maxdepth 1 -name '*.prj' -exec mv -t ../prjshp {} +
  • find 현재 디렉토리와 그 아래의 디렉토리를 재귀 적으로 순회합니다.
  • -maxdepth 1 깊이 1에서 멈추는 것을 의미합니다. 즉, 하위 디렉토리로 재귀하지 않습니다.
  • -name '*.prj'이름이 지정된 패턴과 일치하는 파일에 대해서만 작동 함을 의미합니다. 패턴 주위의 따옴표를 참고하십시오 find. 셸이 아니라 명령 으로 해석됩니다 .
  • -exec … {} +모든 파일에 대해 지정된 명령을 실행하는 것을 의미합니다. 필요한 경우 명령 행 한계를 초과하지 않도록주의하면서 명령을 여러 번 호출합니다.
  • mv -t ../prjshp지정된 파일을로 이동합니다 ../prjshp. 이 -t옵션은 find명령 의 한계로 인해 여기에서 사용됩니다 . 발견 된 파일 (로 표시 {})은 명령의 마지막 인수로 전달되며 이후에 대상을 추가 할 수 없습니다.

또 다른 방법은를 사용하는 것 rsync입니다.

rsync -r --include='*.prj' --exclude='*' . ../prjshp
  • rsync -r … . ../prjshp현재 디렉토리를 ../prjshp재귀 적 으로 복사합니다 .
  • --include='*.prj' --exclude='*'일치하는 파일을 복사 *.prj하고 다른 모든 항목을 제외 하는 것을 의미합니다 (하위 디렉토리를 포함하여 하위 디렉토리의 .prj파일을 찾을 수 없음).

3
rsync는 여기에서 가장 쉬운 솔루션입니다.
ntk4

다소 말끔 cp * | grep '\.prj$' ../prjshp/ 하게 말하면, 두 번째 명령 은 의미가 없지만 *마지막 명령이 디렉토리 (aka cp SOURCE1 SOURCE2....DEST) 인 파일 목록으로 확장 되면 구문 적으로 유효 할 수 있습니다 . 파이프는 이해가되지 않지만 셸에 관한 한 구문 적으로 유효 dup()합니다. 파일 설명자는 괜찮습니다. 파이프의 독자 끝은 데이터를 cp쓰지 않기 때문에 데이터를 가져 가지 않습니다 .
Sergiy Kolodyazhnyy

find와 rsync 모두 같은 인수 목록을 너무 긴 오류로 생성했습니다. for 루프가 가장 간단한 해결 방법이었습니다.
Meezaan-ud-Din

실제로 rsync는 대량 복사를 수행하는 방법이지만 Linux와 함께 얼마나 멀리 왔는지를 알지 못하고 이와 같은 바보 같은 결함 / 버그가 있으며 결함이라고 생각합니다.
MitchellK

22

이 명령은 파일을 하나씩 복사하며 파일이 너무 많아 *단일 cp명령 으로 확장 할 수없는 경우에도 작동합니다 .

for i in *; do cp "$i" ../prjshp/; done

이것은 나를 위해 작동합니다.
1rq3fea324

1
간단하고 효과적입니다. 나는 프로젝트에서 비디오에서 추출한 ~ 1 / 4 백만 개의 JPEG를 제거하는 비슷한 문제가있었습니다. 이것이 내가 사용한 접근법입니다.
Geek

5

Argument list too long오류가 발생 했을 때 명심해야 할 3 가지 핵심 사항이 있습니다 .

  • 명령 행 인수의 길이는 ARG_MAX변수에 의해 제한되는데 , POSIX 정의 에 따라 "... [m] 환경 데이터를 포함 하는 exec 함수 에 대한 인수의 최대 길이 "(강조 추가) "입니다. 즉, 쉘이 비 실행 -built-it 명령을 사용하면 exec()명령 프로세스를 생성하기 위해 중 하나를 호출 해야하며, 그 위치에서 명령의 ARG_MAX이름 또는 경로 (예 /bin/echo:)가 역할을합니다.

  • 쉘 내장 명령은 쉘에 의해 실행되므로 쉘은 exec()함수 군을 사용하지 않으므로 ARG_MAX변수의 영향을받지 않습니다 .

  • 변수를 인식 xargs하고 find인식하는 특정 명령 은 ARG_MAX해당 제한에서 반복적으로 작업을 수행합니다.

위의 관점에서 Kusalananda의 관련 질문에 대한 훌륭한 답변 에서 볼 수 있듯이Argument list too long 환경이 때 발생할 수도 있습니다. 따라서 각 사용자의 환경이 다를 수 있고 바이트 단위의 인수 크기와 관련이 있다는 점을 고려하면 단일 파일 / 인수를 찾기가 어렵습니다.

이러한 오류를 처리하는 방법은 무엇입니까?

핵심은 파일 수에 초점을 두지 않고 사용하려는 명령이 exec()기능 군과 접선 적으로 스택 공간 인지 여부에 초점을 맞추는 것 입니다.

쉘 내장 사용

앞에서 언급했듯이 셸 내장은 ARG_MAX제한이 없습니다. 즉 for루프, while루프, 내장 echo및 내장과 같은 것들은 printf모두 제대로 수행됩니다.

for i in /path/to/dir/*; do cp "$i" /path/to/other/dir/; done

관련된 질문 파일을 삭제하는 방법에 대한, 같은 솔루션이 있었다 :

printf '%s\0' *.jpg | xargs -0 rm --

이것은 쉘의 내장을 사용합니다 printf. 우리가 external을 호출하면 printf을 포함 exec()하므로 많은 인수로 실패합니다.

$ /usr/bin/printf "%s\0" {1..7000000}> /dev/null
bash: /usr/bin/printf: Argument list too long

배쉬 배열

에 따르면 응답 jlliagre에 의해, bashdanjpreron의에서와 같이 파일 이름의 배열을 구축하고 루프의 반복 당 슬라이스를 사용하므로, 배열을 제한하지 않는이 아니라 할 수있는 대답 :

files=( /path/to/old_dir/*.prj )
for((I=0;I<${#files[*]};I+=1000)); do 
    cp -t /path/to/new_dir/ "${files[@]:I:1000}" 
done

그러나 이것은 bash 특정 및 POSIX가 아닌 제한이 있습니다.

스택 공간 늘리기

때로는 사람들 이 스택 공간늘리라고 제안 하는 것을 볼 수 있습니다 ulimit -s <NUM>. Linux에서 ARG_MAX 값은 각 프로그램에 대해 스택 공간의 1/4입니다. 즉 스택 공간을 늘리면 인수 공간이 비례 적으로 증가합니다.

# getconf reports value in bytes, ulimit -s in kilobytes
$ getconf ARG_MAX
2097152
$ echo $((  $(getconf ARG_MAX)*4 ))
8388608
$ printf "%dK\n" $(ulimit -s) | numfmt --from=iec --to=none
8388608
# Increasing stack space results in increated ARG_MAX value
$ ulimit -s 16384
$ getconf ARG_MAX
4194304

Linux Journal을 인용 하는 Franck Dernoncourt의 답변에 따르면 인수에 대한 최대 메모리 페이지 값으로 더 큰 Linux 커널을 다시 컴파일 할 수는 있지만 필요한 것보다 많은 작업이며 인용 된 Linux Journal 기사에 명시된 바와 같이 악용 가능성이 있습니다.

껍질을 피하십시오

또 다른 방법은 Ubuntu와 함께 제공 python되거나 python3기본적으로 제공되는 것입니다. 아래의 python + here-doc 예제는 개인적으로 40,000 항목 범위의 큰 파일 디렉토리를 복사하는 데 사용하는 것입니다.

$ python <<EOF
> import shutil
> import os
> for f in os.listdir('.'):
>    if os.path.isfile(f):
>         shutil.copy(f,'./newdir/')
> EOF

재귀 순회의 경우 os.walk 를 사용할 수 있습니다 .

참조 :


2

이럴 파일의 무리를 처리하는 최적의 도구입니다 findxargs. 참조하십시오 man find. 참조하십시오 man xargs. find그와 -print0스위치, 생성 NUL파일명의 단락 지어진리스트 (확장자가 임의의 문자를 포함 할 수 execpt NUL또는 /) xargs은 USING, 이해 -0스위치. xargs그런 다음 허용되는 가장 긴 명령 (가장 긴 파일 이름, 끝에 절반 파일 이름 없음)을 빌드하고 실행합니다. xargs이 때까지 이것을 반복find더 이상 파일 이름을 제공하지 않을 . xargs --show-limits </dev/null한계를 보려면 실행 하십시오.

당신의 문제를 해결 (그리고 확인 후합니다 man cp찾을 수 --target-directory=) :

find . -maxdepth 1 -type f -name '*.prj' -print0 | xargs -0 cp --target-directory=../prjshp/
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.