답변:
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
파일을 찾을 수 없음).cp * | grep '\.prj$' ../prjshp/
하게 말하면, 두 번째 명령 은 의미가 없지만 *
마지막 명령이 디렉토리 (aka cp SOURCE1 SOURCE2....DEST
) 인 파일 목록으로 확장 되면 구문 적으로 유효 할 수 있습니다 . 파이프는 이해가되지 않지만 셸에 관한 한 구문 적으로 유효 dup()
합니다. 파일 설명자는 괜찮습니다. 파이프의 독자 끝은 데이터를 cp
쓰지 않기 때문에 데이터를 가져 가지 않습니다 .
이 명령은 파일을 하나씩 복사하며 파일이 너무 많아 *
단일 cp
명령 으로 확장 할 수없는 경우에도 작동합니다 .
for i in *; do cp "$i" ../prjshp/; done
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에 의해, bash
danjpreron의에서와 같이 파일 이름의 배열을 구축하고 루프의 반복 당 슬라이스를 사용하므로, 배열을 제한하지 않는이 아니라 할 수있는 대답 :
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 를 사용할 수 있습니다 .
이럴 파일의 무리를 처리하는 최적의 도구입니다 find
및 xargs
. 참조하십시오 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/