파일 이름(공백 포함)을 기준으로 재귀적으로 파일 정렬 및 mv 파일

파일 이름(공백 포함)을 기준으로 재귀적으로 파일 정렬 및 mv 파일

실수를 해서 파일을 같은 디렉토리에 함께 덤프했습니다. 다행히 파일 이름을 기준으로 정렬할 수 있습니다. ''' 2019-02-19 20.18.58.ndpi_2_2688_2240.jpg '''#비트 또는2이 특별한 경우에는 위치 정보가 있습니다. 범위는 0-9이고 파일 이름의 길이는 모두 동일하므로 숫자는 항상 파일 이름의 동일한 위치에 있습니다(공백을 제외한 26번째 문자, 옆에 밑줄 표시). 나는 다음과 같은 훌륭한 링크를 찾았습니다. 이름의 일부만 검색하여 파일을 찾는 명령은 무엇입니까?

그러나 출력을 move 명령으로 파이프할 수는 없습니다. 출력을 변수로 반복하려고 시도했지만 그 중 하나도 작동하지 않는 것 같습니다.

for f in find . -name '*_0_*' ; do  mv "$f" /destination/directory ; done

이 링크에 따르면 * 또는 일부 따옴표를 잘못된 위치에 넣었을 수 있습니다.mv: stat할 수 없습니다. 쉘 스크립트에 해당 파일이나 디렉터리가 없습니다..

즉, 나는 많은 디렉토리를 가지고 있고 그것들을 다른 곳에서 동일한 디렉토리 구조로 정렬하고 싶습니다.

-Flowers (to be sorted)          -Flowers-buds               -Flowers-stems
     -Roses                          -Roses                      -Roses
        buds.jpg       ===>             buds.jpg     ===>           stems.jpg
        stems.jpg
        petals.jpg
     -Daisies                       -Daisies                    -Daisies
        buds.jpgs                       buds.jpg                   stems.jpg
        stems.jpg
        petals.jpg
     -Tulips                        -Tulips                     -Tulip
        buds.jpgs                       buds.jpg                  stems.jpg
        stems.jpg
        petals.jpg

...그 숫자에 따라 더 많은 정보(#). 이것이 bash에서 실제로 수행되는 작업입니까? 도구가 darwin(BSD) 대신 GNU Linux처럼 작동하도록 coreutils가 설치된 터미널에서 MacOS를 실행하고 있습니다.

답변1

find및 를 사용하여 이 작업을 수행할 수 있지만 mv여기서는 가장 간단한 접근 방식이 아닙니다. 당신은 macOS를 사용하고 있습니다.zsh사전 설치되어 있습니다. Zsh에는 다음과 같은 깔끔한 파일 대량 이름 바꾸기 도구가 함께 제공됩니다.zmv. zsh터미널에서 실행한 다음 zsh에서 다음과 같이 실행합니다.

autoload zmv
zmv -n '[^_]#_([0-9])_*' '/elsewhere/${1}/${f}'

설명:

  • -nzmv무엇을 할지 표시하라고 지시 하지만 실제로는 아무것도 하지 않습니다. 표시되는 내용이 만족스러우면 -n.
  • 옵션이 아닌 첫 번째 인수는와일드카드 패턴. zmv일치하는 파일에 대해 작업을 수행합니다(일치하지 않는 파일은 무시합니다).
  • [^_]#이외의 문자가 0개 이상 있음을 의미합니다 _. Zsh는 bash 등과 동일한 와일드카드에 대한 액세스를 제공합니다. #"이전 문자의 수에 제한 없음"을 의미하는 zsh 전용 기능(다른 구문으로 bash에서 사용 가능)입니다. 패턴은 [^_]#_[0-9]_*두 밑줄 사이에 한 자리 숫자가 포함되고 해당 숫자 앞에 다른 밑줄이 포함되지 않은 모든 파일 이름과 일치합니다.
  • 주위의 괄호를 사용하면 대체 텍스트에서 [0-9]와 같이 숫자를 사용할 수 있습니다 .$1
  • 대체 텍스트는 ${1}, ${2}등을 사용하여 소스 패턴에서 괄호로 묶인 조각을 참조하고 ${f}전체 원래 이름을 참조할 수 있습니다.

예를 들어 위의 스니펫은 2019-02-19 20.18.58.ndpi_2_2688_2240.jpg으로 이동합니다 /elsewhere/2/2019-02-19 20.18.58.ndpi_2_2688_2240.jpg. 귀하의 질문에는 무엇을 이동해야 하는지, 어디로 이동해야 하는지 매우 정확하게 설명되어 있지 않지만 위의 명령을 조정하면 원하는 것을 얻을 수 있습니다. 알아낼 수 없는 경우 질문을 편집하여 일치해야 하는 항목과 일치하지 않아야 하는 항목에 대한 구체적인 예를 추가하세요.

하위 디렉터리에 파일이 있는 경우 */패턴 시작 부분에 사용할 수 있습니다. 참조할 디렉토리를 와 일치시켜야 하는 경우 , 주위에 괄호를 넣어야 합니다 . 슬래시 주위에는 괄호를 넣을 수 없습니다. 디렉토리를 재귀적으로 탐색하고 부채가 있는 파일을 일치시키려면 다음을 사용하십시오 .${NUM}***/재귀적 글로빙. 예외적으로 (**/)대체 텍스트의 디렉토리 경로에 로 액세스하려면 를 사용해야 합니다 . 이와 같은 경우에는 괄호를 사용하지 않고 대신 괄호를 사용하는 것이 더 쉽습니다.${NUM}수정자${f}원래 경로의 일부를 전체적으로 추출합니다 . 예를 들어, 위와 동일한 이름 바꾸기를 수행하되 파일을 현재 디렉터리에서 아래의 병렬 구조로 이동하되 파일 이름 바로 앞에 /elsewhere추가 수준 0, 등을 사용합니다.1

autoload zmv
zmv -n '**/[^_]#_([0-9])_*' '/elsewhere/${f:h}/${1}/${f:t}'

이름을 기준으로 파일을 일치시키는 것이 어려운 경우 변경 시간 을 기준으로 파일을 일치시키는 다른 접근 방식이 있습니다. 한동안 변경되지 않은 디렉토리에 여러 파일을 복사한 경우 새 파일은 최근 변경 시간을 가진 파일입니다. zsh에서 변경 시간별로 파일을 일치시킬 수 있습니다.글로벌 한정자c. 예를 들어 지난 한 시간 동안 마지막으로 변경된 파일을 나열하려면 다음을 수행하세요.

ls -lctr *(ch-1)

마감 시간을 찾는 대신 다음을 참조할 수 있습니다.Nglob 한정자를 사용하여 가장 최근에 변경된 파일 oc(ctime을 늘려 정렬) 및 (첫 번째 파일만 유지)[1,N]N성냥). 예를 들어, 방금 42개의 파일을 해당 디렉터리로 이동했고 그 이후로 아무것도 변경하지 않았다는 것을 알고 있는 경우:

ls -lctr *(oc[1,42])

와 함께 glob 한정자를 사용하려면 옵션 zmv을 전달해야 합니다 -Q. 예를 들어 위와 같이 이름을 기반으로 파일을 디렉터리로 이동하지만 지난 한 시간 동안 변경되지 않은 모든 파일을 무시하려면 다음을 수행합니다.

zmv -n -Q '[^_]#_([0-9])_*(ch-1)' '/elsewhere/${1}/${f}'

zmv파일을 덮어쓰지 않고 충돌(대상 이름이 같은 두 개의 소스 파일)이 없는지 확인하는 몇 가지 안전성이 있습니다. 파일 수가 많은 경우 이러한 안전 조치를 취하는 데 시간이 걸릴 수 있습니다. zmv너무 느리고 이름 바꾸기 구조상 충돌이 발생하지 않는 경우 루프에서 수행 중인 특정 이름 바꾸기를 하드 코딩할 수 있습니다 . Zsh는 zmv더 좋은 덕분에 사용하지 않더라도 bash를 이기는 경향이 있습니다.글로빙그리고매개변수 확장메커니즘. 성능을 위해 다음을 수행할 수 있습니다.동적으로 로드다음을 포함하는 모듈파일 조작을 위한 내장.

예를 들어, 이름에 다음이 포함된 경우 일반 파일을 현재 디렉터리 아래에서 완전히 새로운 계층 구조로 이동하려면 다음을 수행합니다 _0_.

zmodload -m -F zsh/files 'b:zf_*'
for x in **/*_0_*(.); do
  zf_mkdir -p /destination/directory/$x:h
  zf_mv ./$x /destination/directory/$x
done

( :h또 다른 zsh 기능은 다음과 같습니다.기록 수정자.)

파일 이름에서 두 개의 밑줄 사이에 있는 첫 번째 숫자를 가져와 해당 숫자에 해당하는 디렉터리에 파일을 저장하려면 숫자를 추출해야 합니다. zmv괄호 안에 있는 그룹에 대해 이 작업을 자동으로 수행합니다. 여기서는 직접 수행해야 합니다.

zmodload -m -F zsh/files 'b:zf_*'
for source in **/*_<->_*(.); do
  suffix=${${source:t}#*_<->_}
  n=${${source%$suffix}##*_}
  destination=${source:h}/$n/${source:t}
  zf_mkdir -p /destination/directory/$destination:h
  zf_mv ./$source /destination/directory/$destination
done

파일 이름을 대량으로 바꾸는 다른 방법에 대해 알아보려면 다음을 찾아보세요.rename이 사이트에 태그된 질문.

¹ "변경 시간" 또는 줄여서 "ctime"이라고 하는 파일의 inode 변경 시간은 파일이 마지막으로 이동되었거나 권한과 같은 속성이 마지막으로 변경된 시간입니다. 수정시간(mtime)과는 다릅니다.

답변2

쉘이 공백으로 입력을 분할하고 있습니다. 재귀와 함께 bash globbing을 사용하여 **파일 이름을 올바르게 분할할 수 있습니다.

shopt -s globstar
for f in **/*_0_*; do mv "$f" /dest/; done

모두 같은 위치로 이동하는 경우 루프가 필요하지 않습니다.

shopt -s globstar
mv **/*_0_*  /dest/

... 또는 셸의 개입 없이 find -execfind에서 호출로 직접 파일 이름을 전달하는 를 사용합니다.exec()

find . -name '*_0_*' -exec mv {} /dest/ \;

또는 null로 분할하려면 read더하기를 사용하세요. find -print0이 문제에 대해서는 과잉이며, 글로빙할 때 유용하고 find -exec작업에 적합하지 않습니다.

while IFS=  read -r -d ''; do mv "$REPLY" /dest/; done < <( find . -name '*_0_*' -print0 )

예제에서와 같이 파일 이름을 기반으로 최상위 대상을 변경하려면 ${var#remove_from_start}및 를 사용하여 파일 이름 부분을 잘라냅니다 ${var%remove_from_end}. 제거 패턴의 와일드카드는 가능한 한 적게 제거됩니다. 가능한 한 많이 제거하려면 ${…##…}다음 과 같이 연산 문자를 두 배로 늘리십시오 ${…%%…}.

    shopt -s globstar
    for f in **/*_0_*; do            # Flowers/Roses/stems.jpg 
      file=${f##*/}                  # stems.jpg
      base=${file%.*}                # stems
      path=${f%/*}                   # Flowers/Roses
      toppath=${path%%/*}            # Flowers
      subpath=${path#*/}             # Roses
      dest=$toppath-$base/$subpath/  # Flowers-stems/Roses/
      [[ -d $dest ]] || mkdir -p "$dest"
      mv "$f" "$dest"
    done

관련 정보