Bash: filename에 각 생년월일별로 n개의 파일을 복사합니다.

Bash: filename에 각 생년월일별로 n개의 파일을 복사합니다.

파일 이름에 생년월일이 포함된 사진이 수천 장 있습니다. 각 출생 연도에 대해 최소 100개의 파일을 찾아서 복사해야 합니다. 예를 들어 2000년 출생에 대한 100개 파일, 2001년 출생에 대한 100개 파일... 등이 있습니다.

파일 이름 형식은 다음과 같습니다.

35077502_1995-02-01_2012.jpg

이 사진은 2012년에 찍은 것으로 추정됩니다.

bash 스크립트를 사용하여 수행할 수 있습니까?

감사해요

답변1

#!/bin/bash

IFS=$'\n' years=( $(find . -maxdepth 1 -name '*.jpg' -print0 | 
                    sed -zEn 's/^.*_([0-9][0-9][0-9][0-9])-.*\.jpg/\1/p' | 
                    tr '\0' '\n' | 
                    sort -u)
                )

for year in "${years[@]}" ; do
  mkdir -p "$year"
  find . -iname "*_${year}-*.jpg" -size +1k -print0 |
    head -z -n 100 |
    xargs -0r cp -t "$year"
done

이는 현재 디렉토리의 파일 이름에서 추출된 고유한 4자리 연도 세트를 포함하는 배열( $years)을 구성합니다. 여기서 연도 앞에는 밑줄( _)이 있고 그 뒤에는 대시( -)가 옵니다. 이를 위해서는 sedaka 옵션의 GNU 버전이 필요합니다.-z--null-data

각 연도에 대해 먼저 해당 연도에 대한 디렉터리를 생성한 다음(디렉토리가 아직 존재하지 않는 경우) 이를 사용하여 find필요한 패턴과 일치하고 1KB보다 큰 모든 파일 이름을 나열합니다. 그런 다음 해당 목록을 통해 처음 100줄을 가져오고 head파일 xargs을 적절한 디렉터리에 복사하세요.

파일 이름 목록은 모든 유효한 파일 이름에 대해 작동하도록 파이프 전체에서 NUL로 종료됩니다(즉, 파일 이름에 공백, 탭, 개행 문자 또는 기타 특이하지만 완벽하게 유효한 문자가 포함되어 있어도 중단되지 않습니다).

이것은 또한 필요합니다GNU 버전head-z(이것은 Linux의 표준입니다.) 옵션(  --zero-terminatedNUL 종료 입력이라고도 함)을 사용하기 때문입니다 . 특히 버전이 필요합니다.2016년 1월 13일 업데이트. 또한 cp대상 디렉터리가 마지막 인수가 아닌 첫 번째 인수가 되도록 허용하는 GNU(일명) 옵션도 필요합니다 .-t--target-directory

파일을 정렬해야 하는 경우 sort -z및 명령 사이에 삽입할 수 있습니다. 예를 들어 GNU 버전의 .findheadfind ... -print0 | sort -z ... | head -z ...sort

이것은 귀하의 질문 개정에 표시된 것처럼 파일 이름에 밑줄이 있고 .jpg확장자 앞의 마지막 항목으로 연도가 따른다고 가정합니다.

-iname "*${year}*.jpg"연도가 파일 이름 어디에나 나타날 수 있는 경우 (밑줄 없이, 및 사이에 초 삽입) 을 사용할 수 있지만 *처음 8자리는 하위 문자열을 포함하는 파일의 숫자와 유사하다는 점에 유의하십시오.${year}.jpg604200172001

이는 또한 모든 파일에 , , , 등 대신 (대소문자를 구분하지 않는) 확장자 가 있다고 가정 .jpg합니다 . 여러 파일 확장자가 필요한 경우 대신 이 옵션을 사용할 수 있습니다 ..jpeg.jpe.jfif.gif.png-iregex-iname

답변2

파일 이름에 불쾌한 내용이 없으면 할 수 있습니다

for year in 2000 2001; do
  cp `ls *${year}*.jpg|head -n 100` destination
done

답변3

그리고 zsh:

for y ({1995..2017}) (cp -- **/*_$y.jpg(.LK+1[1,100]) destination)
  • **/: 모든 수준의 하위 디렉터리 내에서 알파벳순으로 정렬
  • .:일반 파일만 해당
  • LK+1: 길이가 1KiB를 초과합니다.
  • [1,100]: 처음 100개.

n(정렬 순서에 따라 복사할 파일이 결정되므로 숫자로 정렬하려면 glob 한정자를 추가해야 할 수도 있습니다 .)

또는 연도 목록을 하드코딩하고 디렉터리를 여러 번 크롤링하지 마세요.

typeset -A files n
for f (**/*_<->.jpg(.LK+1)) {
  y=${${f##*_}%.*}
  ((++n[$y] > 100)) || files[$y]+=$f$'\0'
}
for y (${(k)files}) {
  mkdir -p $y && cp -- ${(0)files[$y]} $y
}

(테스트되지 않음)

관련 정보