파일을 같은 디렉토리에 함께 덤프하는 실수를 저질렀습니다. 다행히 파일 이름을 기준으로 정렬할 수 있습니다. ''' 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: 셀 스크립트에 해당 파일이나 디렉터리가 없습니다..
즉, 많은 디렉토리가 있고 이를 다른 곳에서도 동일한 디렉토리 구조로 정렬하고 싶습니다.
-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에서 수행되는 작업입니까? 저는 coreutils가 설치된 터미널에서 MacOS를 실행하고 있으므로 도구는 darwin(BSD)이 아닌 GNU linux처럼 작동해야 합니다.
답변1
and 를 사용하여 이 작업을 수행할 수 있지만 find
가장 mv
쉬운 방법은 아닙니다. macOS를 사용하고 계시므로다루기 힘든이미 사전 설치되어 있습니다. Zsh에는 다음과 같은 깔끔한 파일 일괄 이름 바꾸기 도구가 함께 제공됩니다.zmv
. zsh
터미널에서 실행한 후 zsh에서 다음 명령을 실행합니다.
autoload zmv
zmv -n '[^_]#_([0-9])_*' '/elsewhere/${1}/${f}'
설명하다:
-n
zmv
무엇을 할 것인지 보여주라고 말하지만 실제로는 아무것도 하지 않습니다. 표시되는 내용에 만족하면 명령을 다시 실행하십시오(-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)
당신은 참조 할 수 있습니다질소glob 한정자를 사용하여 최근에 변경된 파일 oc
(ctime을 늘려 정렬) 및 (첫 번째 파일만 유지)[1,N]
질소성냥). 예를 들어, 방금 42개의 파일을 이 디렉터리로 이동했고 그 이후로 아무것도 변경하지 않았다는 것을 알고 있는 경우:
ls -lctr *(oc[1,42])
와 함께 glob 한정자를 사용하려면 zmv
이 옵션을 전달해야 합니다 -Q
. 예를 들어, 파일 이름(위에 표시됨)을 기반으로 디렉터리로 파일을 이동하되 지난 한 시간 동안 변경되지 않은 파일은 무시합니다.
zmv -n -Q '[^_]#_([0-9])_*(ch-1)' '/elsewhere/${1}/${f}'
zmv
파일을 덮어쓰지 않고 충돌이 없는지 확인하는 몇 가지 안전 장치가 있습니다(두 소스 파일의 대상 이름이 동일함). 파일 수가 많은 경우 이러한 보안 조치에 시간이 걸릴 수 있습니다. 속도 zmv
가 너무 느리고 이름이 변경된 구조로 인해 충돌이 발생하지 않는 경우 루프에서 수행되는 특정 이름 변경을 하드코딩할 수 있습니다. Zsh를 사용하지 않더라도 bash가 zmv
더 좋기 때문에 이기는 경향이 있습니다.와일드카드그리고매개변수 확장기구. 성능을 위해 다음을 수행할 수 있습니다.동적 로딩모듈에는 다음이 포함됩니다.파일 작업을 위한 내장 기능.
예를 들어 이름에 다음이 포함된 경우 일반 파일을 현재 디렉터리에서 완전히 새로운 계층 구조로 이동합니다 _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
이 사이트에 태그된 질문.
¹ 파일의 inode 변경 시간("변경 시간" 또는 줄여서 "ctime"이라고도 함)은 파일이 마지막으로 이동된 시간 또는 해당 속성(예: 권한)이 마지막으로 변경된 시간입니다. 수정시간(mtime)과는 다릅니다.
답변2
쉘은 입력을 공백으로 분할합니다. bash globbing과 재귀를 사용하여 **
파일 이름을 올바르게 분할할 수 있습니다.
shopt -s globstar
for f in **/*_0_*; do mv "$f" /dest/; done
모두 같은 위치로 이동하면 루프가 필요하지 않습니다.
shopt -s globstar
mv **/*_0_* /dest/
...또는 셸 개입 없이 find -exec
find에서 호출로 직접 파일 이름을 전달하는 를 사용합니다.exec()
find . -name '*_0_*' -exec mv {} /dest/ \;
또는 read
더하기 기호를 사용하여 find -print0
Null 값을 구분합니다. 이 질문은 너무 지나쳐서 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