"mv : Argument list too long"을 해결 하시겠습니까?


64

정렬이 필요한 백만 개 이상의 파일이있는 폴더가 있지만 mv항상이 메시지를 출력 하기 때문에 실제로 아무것도 할 수 없습니다

-bash: /bin/mv: Argument list too long

이 명령을 사용하여 확장명이없는 파일을 이동합니다.

mv -- !(*.jpg|*.png|*.bmp) targetdir/

답변:


82

xargs작업을위한 도구입니다. 그것으로 또는 find-exec … {} +. 이러한 도구는 명령을 여러 번 실행하며 한 번에 전달할 수있는 많은 인수를 사용합니다.

변수 인수 목록이 끝날 때 두 방법 모두 수행하기가 더 쉽습니다. 여기에는 해당되지 않습니다. 최종 인수 mv는 대상입니다. GNU 유틸리티 (즉, 내장되지 않은 Linux 또는 Cygwin) 에서 대상을 먼저 전달하는 -t옵션 mv이 유용합니다.

파일 이름에 공백이나가 없으면 \"'파일 이름을 입력으로 제공 할 수 있습니다 xargs( echo명령은 bash 내장이므로 명령 행 길이 제한이 적용되지 않습니다).

echo !(*.jpg|*.png|*.bmp) | xargs mv -t targetdir

-0옵션을 xargs사용하여 기본 인용 형식 대신 널로 구분 된 입력을 사용할 수 있습니다 .

printf '%s\0' !(*.jpg|*.png|*.bmp) | xargs -0 mv -t targetdir

또는을 사용하여 파일 이름 목록을 생성 할 수 있습니다 find. 하위 디렉토리로의 재귀를 피하려면을 사용하십시오 -type d -prune. 나열된 이미지 파일에 대한 조치가 지정되지 않았으므로 다른 파일 만 이동됩니다.

find . -name . -o -type d -prune -o \
       -name '*.jpg' -o -name '*.png' -o -name '*.bmp' -o \
       -exec mv -t targetdir/ {} +

쉘 와일드 카드 방법과 달리 도트 파일이 포함됩니다.

GNU 유틸리티가없는 경우 중간 쉘을 사용하여 인수를 올바른 순서로 가져올 수 있습니다. 이 방법은 모든 POSIX 시스템에서 작동합니다.

find . -name . -o -type d -prune -o \
       -name '*.jpg' -o -name '*.png' -o -name '*.bmp' -o \
       -exec sh -c 'mv "$@" "$0"' targetdir/ {} +

zsh에서는 mv내장을 로드 할 수 있습니다 .

setopt extended_glob
zmodload zsh/files
mv -- ^*.(jpg|png|bmp) targetdir/

또는 mv다른 이름이 외부 명령을 계속 참조하도록하려는 경우 :

setopt extended_glob
zmodload -Fm zsh/files b:zf_\*
zf_mv -- ^*.(jpg|png|bmp) targetdir/

또는 ksh 스타일 글로브로 :

setopt ksh_glob
zmodload -Fm zsh/files b:zf_\*
zf_mv -- !(*.jpg|*.png|*.bmp) targetdir/

또는 GNU mvzargs:

autoload -U zargs
setopt extended_glob
zargs -- ./^*.(jpg|png|bmp) -- mv -t targetdir/

1
처음 두 명령은 "-bash :! : event not found"를 반환했으며 다음 두 명령은 파일을 전혀 이동하지 않았습니다. 알아야 할 경우 CentOS 6.5를 사용하고 있습니다
Dominique

1
@Dominique 나는 당신이 당신의 질문에 사용한 것과 같은 globbing 구문을 사용했습니다. 당신은해야 shopt -s extglob활성화 할 수 있습니다. 나는 find명령 에서 한 단계를 놓쳤다 . 나는 그들을 고쳤다.
Gilles

find 명령 "find : invalid expression; 이전에 아무것도없는 이진 연산자 '-o'를 사용했습니다." 나는 이제 다른 것들을 시도 할 것입니다.
Dominique

@Dominique find내가 게시 한 명령이 작동합니다. 복사-붙여 넣기시 부품을 남겨 두어야합니다.
Gilles

찾기 명령에 대해 Gilles는 왜 "not"연산자를 사용하지 !않습니까? 홀수 후행보다 더 명확하고 이해하기 쉽습니다 -o. 예 :! -name '*.jpg' -a ! -name '*.png' -a ! -name '*.bmp'
CivFan

13

리눅스 커널로 작업하는 것만으로 충분하다면 간단하게 할 수 있습니다

ulimit -s 100000

그것은 리눅스 커널이 약 10 년 전에 스택 크기에 따라 인수 제한을 변경 한 패치를 포함했기 때문에 작동합니다 : https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/ commit /? id = b6a2fea39318e43fee84fa7b0b90d68bed92d2ba

업데이트 : 당신이 용감하다고 생각하면 말할 수 있습니다

ulimit -s unlimited

RAM이 충분하다면 쉘 확장에 문제가 없을 것입니다.


그것은 해킹입니다. 스택 제한을 어떻게 설정해야하는지 어떻게 알 수 있습니까? 이는 동일한 세션에서 시작된 다른 프로세스에도 영향을줍니다.
Kusalananda

1
그래, 해킹이야 대부분의 경우 이러한 종류의 핵은 일회성입니다 (어쨌든 수동으로 대량의 파일을 얼마나 자주 이동합니까?). 프로세스가 모든 RAM을 사용하지 않을 것이라고 확신하는 경우 설정할 수 있으며 ulimit -s unlimited사실상 무제한 파일에 대해 작동합니다.
Mikko Rantalainen

하여 ulimit -s unlimited실제 명령 라인 한도 2 ^ 31 2GB입니다. ( MAX_ARG_STRLEN커널 소스에서)
Mikko Rantalainen

9

운영 체제의 인수 전달 한계는 쉘 인터프리터 내에서 발생하는 확장에는 적용되지 않습니다. 그래서 사용하는 것 외에도 xargs또는 find, 우리는 단순히 개별로 처리를 나누는 쉘 루프를 사용할 수있는 mv명령 :

for x in *; do case "$x" in *.jpg|*.png|*.bmp) ;; *) mv -- "$x" target ;; esac ; done

POSIX 셸 명령 언어 기능과 유틸리티 만 사용합니다. 이 하나의 라이너는 들여 쓰기가 필요하고 불필요한 세미콜론이 제거되어 더 명확합니다.

for x in *; do
  case "$x" in
    *.jpg|*.png|*.bmp) 
       ;; # nothing
    *) # catch-all case
       mv -- "$x" target
       ;;
  esac
done

백만 개가 넘는 파일이 있으면 @Gilles가 게시 mv한 POSIX find솔루션을 사용하는 데 필요한 몇 가지 파일 대신 백만 개가 넘는 프로세스 가 생성 됩니다. 다시 말해, 이런 식으로 불필요한 CPU 이탈이 많이 발생합니다.
CivFan

@CivFan 또 다른 문제는 수정 된 버전이 원본과 동일하다는 확신을줍니다. 여러 확장을 필터링하기위한 확장 case결과에 대한 설명 *이 원래 !(*.jpg|*.png|*.bmp)표현식 과 동일 하다는 것을 쉽게 알 수 있습니다 . 그 find대답은 사실 동일하지 않습니다. 하위 디렉토리로 내려갑니다 (나는 -maxdepth술어 가 보이지 않습니다 ).
Kaz

-name . -o -type d -prune -o하위 디렉토리로 내려 가지 않도록 보호합니다. -maxdepth비록 내 find매뉴얼 페이지 에서 언급되지 않았지만 POSIX와 호환되지 않습니다 .
CivFan

개정판 1로 롤백되었습니다. 질문에 소스 또는 대상 변수에 대한 언급이 없으므로 답변에 불필요한 균열이 추가됩니다.
Kaz

5

이전에 제공된 것보다 더 공격적인 솔루션을 얻으려면 커널 소스를 가져 와서 편집하십시오. include/linux/binfmts.h

크기를 MAX_ARG_PAGES32보다 큰 것으로 늘리십시오. 이렇게하면 커널이 프로그램 인수에 허용하는 메모리 양이 증가하여 백만 개의 파일 또는 수행중인 작업에 대해 명령 mv또는 rm명령 을 지정할 수 있습니다 . 재 컴파일, 설치, 재부팅

조심해! 이 값을 시스템 메모리에 너무 크게 설정 한 후 많은 인수를 사용하여 명령을 실행하면 잘못된 결과가 발생합니다! 다중 사용자 시스템에서는이 작업을 신중하게 수행해야합니다. 악의적 인 사용자가 모든 메모리를보다 쉽게 ​​사용할 수 있습니다!

커널을 수동으로 다시 컴파일하고 다시 설치하는 방법을 모르는 경우 현재로서는이 답변이 존재하지 않는다고 가정하는 것이 가장 좋습니다.


5

"$origin"/!(*.jpg|*.png|*.bmp)catch 블록 대신 사용하는보다 간단한 솔루션 :

for file in "$origin"/!(*.jpg|*.png|*.bmp); do mv -- "$file" "$destination" ; done

@Score_Under 덕분에

여러 줄 스크립트의 경우 다음을 수행 할 수 있습니다 (이를 삭제 ;하기 전의 주의 사항 done).

for file in "$origin"/!(*.jpg|*.png|*.bmp); do        # don't copy types *.jpg|*.png|*.bmp
    mv -- "$file" "$destination" 
done 

모든 파일을 이동시키는보다 일반적인 솔루션을 수행하려면 단일 라이너를 사용할 수 있습니다.

for file in "$origin"/*; do mv -- "$file" "$destination" ; done

들여 쓰기를하면 다음과 같이 보입니다.

for file in "$origin"/*; do
    mv -- "$file" "$destination"
done 

원본의 모든 파일을 가져 와서 대상으로 하나씩 이동합니다. $file파일 이름에 공백이나 다른 특수 문자가있는 경우 따옴표 가 필요합니다.

다음은 완벽하게 작동 한이 방법의 예입니다.

for file in "/Users/william/Pictures/export_folder_111210/"*.jpg; do
    mv -- "$file" "/Users/william/Desktop/southland/landingphotos/";
done

for-loop에서 원래 glob와 같은 것을 사용하여 요청되는 것에 더 가까운 솔루션을 얻을 수 있습니다.
Score_Under

오리지널 글로브 란 무엇인가요?
Whitecat

그것이 조금 비밀 스러웠다면 죄송합니다 !(*.jpg|*.png|*.bmp). "$origin"/!(*.jpg|*.png|*.bmp)Kaz의 답변에 사용 된 스위치가 필요하지 않고 for-loop의 간단한 몸체를 유지하는 globbing을 통해 for-loop에 추가 할 수 있습니다 .
Score_Under

최고 점수. 귀하의 의견을 반영하고 답변을 업데이트했습니다.
Whitecat

3

때로는 작은 스크립트를 작성하는 것이 가장 쉬운 방법입니다 (예 : Python).

import glob, shutil

for i in glob.glob('*.jpg'):
  shutil.move(i, 'new_dir/' + i)

1

mv몇 번 실행해도 마음에 들지 않으면 계속 사용하면서 제한을 극복 할 수 있습니다 .

한 번에 부분을 이동할 수 있습니다. 예를 들어 긴 영숫자 파일 이름 목록이 있다고 가정 해 봅시다.

mv ./subdir/a* ./

작동합니다. 그런 다음 또 다른 큰 덩어리를 제거하십시오. 몇 번 움직 인 후에는 다시 사용으로 돌아갈 수 있습니다.mv ./subdir/* ./


0

여기 내 두 센트가 있습니다. .bash_profile

mv() {
  if [[ -d $1 ]]; then #directory mv
    /bin/mv $1 $2
  elif [[ -f $1 ]]; then #file mv
    /bin/mv $1 $2
  else
    for f in $1
    do
      source_path=$f
      #echo $source_path
      source_file=${source_path##*/}
      #echo $source_file
      destination_path=${2%/} #get rid of trailing forward slash

      echo "Moving $f to $destination_path/$source_file"

      /bin/mv $f $destination_path/$source_file
    done
  fi
}
export -f mv

용법

mv '*.jpg' ./destination/
mv '/path/*' ./destination/
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.