이 질문은 처음 질문했을 때 몰랐던 처음 두 답변과 의견을 통해 학습하여 완전히 다시 작성되었습니다.
사진을 찍은 뒤, 그렇게 생긴 서류를 가지고 집에 갔습니다 _DSC1234.NEF
. NEF
Nikon의 Camera Raw 파일 형식이므로 EXIF
데이터가 파일에 존재합니다.
세 부분으로 자동으로 이름을 바꾸고 싶습니다.
- 생성 날짜 형식
YYYYMMDD
- 촬영명
- 사진 수
따라서 최종 파일 이름은 다음과 같아야합니다
20140707_NameOfShoot_0001.NEF
몇 가지 질문이 있습니다:
광고 1. 작성일자
때로는 파일을 촬영한 지 며칠 후에만 파일을 복사하고 이름을 바꿀 수 있으므로 날짜는 복사한 날짜가 아니라 사진을 찍은 날짜를 반영해야 합니다. mtime
최선의 선택인 것 같으며, 가능하다면 생성 날짜를 알려주세요 EXIF
.
광고 2: 촬영명 이상적으로 이는 스크립트를 호출할 때 매개변수로 설정할 수 있는 변수입니다.
광고 3. 사진 매수
이는 이미지의 연령을 반영해야 하며 가장 오래된 것부터 번호가 매겨져 있습니다. 문제는 카메라가 일반적으로 0000
적중 후 번호 매기기를 다시 시작한다는 것입니다 9999
. 그럼 9995-9999
아마도 그보다 더 오래되었을 겁니다 0000-0004
. 파일의 수명을 반영하는 솔루션을 찾고 있습니다. 이 특별한 경우에는 이름 바꾸기
- _DSC0000.NEF -> 20140707_FOO_0004.NEF
- _DSC0001.NEF -> 20140707_FOO_0005.NEF
- _DSC0002.NEF -> 20140707_FOO_0006.NEF
- ...
- _DSC9997.NEF -> 20140707_FOO_0001.NEF
- _DSC9998.NEF -> 20140707_FOO_0002.NEF
- _DSC9999.NEF -> 20140707_FOO_0003.NEF
다시 말씀드리지만, mtime
가능하다면 생성 날짜가 EXIF
정확한 것 같습니다.
~에서여기날짜별로 폴더의 모든 .NEF 파일 이름을 바꾸는 작업 솔루션이 있습니다.
find -name '*.NEF' |
gawk 'BEGIN{ a=1 }{ printf "mv %s %04d.NEF\n", $0, a++ }' |
bash
파일 수정 날짜와 샷 이름을 하드코딩하는 것은 잘 작동하지만 자동화할 수 있다면 좋을 것입니다.
타임스탬프의 경우 에 대한 기사를 찾았 strftime()
지만 이를 사용하여 파일 수정 날짜를 반환하는 방법을 이해하지 못합니다. 또한 gawk-line에 추가 및 추가를 시도했는데 , 이로 인해 현재 폴더의 모든 파일이 삭제됩니다(시간을 변경하는 대신 아마도 systime도 삭제될 것입니다).mktime()
systime()
DATE=$(date +"%Y%m%d")
$DATE
내가 시도한 변수에 대해
gawk 'BEGIN{ a=1 }{ printf "mv %s $1_%04d.NEF\n", $0, a++ }' |
호출 스크립트를 사용 ./rename FOO
하지만 이름을 바꿀 때 FOO는 무시됩니다.
답변1
대부분의 유닉스는 파일 생성 날짜를 추적하지 않습니다. 어쨌든 "생성 날짜"가 잘 정의되어 있지 않습니다. (파일을 복사하면 새 파일이 생성됩니까?) 합리적인 해석에 따르면 최신 버전의 데이터가 생성된 날짜인 파일 수정 시간을 사용할 수 있습니다. 파일을 복사하는 경우 수정 시간을 보존해야 합니다(예: 해당 명령을 사용하는 cp -p
경우 bare를 사용하지 마세요 ).cp -a
cp
cp
일부 파일 형식에는 작성자 응용 프로그램이 생성 날짜를 입력하는 파일 내에 필드가 있습니다. 이는 사진의 경우인 경우가 많으며 카메라가 일부 내용을 채웁니다.Exf생성 시간을 포함한 JPEG 또는 TIFF 이미지의 데이터입니다. Nikon의 NEF 이미지 형식은 TIFF를 래핑하고 Exif를 지원합니다.
파일 이름에 생성 날짜를 포함하도록 Exif 데이터가 포함된 이미지 파일의 이름을 바꾸는 데 사용할 수 있는 도구가 있습니다.이름에 생성 날짜를 포함하도록 이미지 이름 바꾸기두 가지 솔루션이 표시됩니다.내보내기 도구그리고exiv2.
두 도구 모두 파일 이름에 카운터를 포함하는 것을 허용하지 않는다고 생각합니다. 두 단계로 이름을 바꿀 수 있습니다. 먼저 파일 이름에 날짜를 포함하고(순서를 유지하기 위해 가능한 고해상도로) 해당 날짜 부분을 기준으로 파일 번호를 매깁니다(시간은 삭제합니다). 최신 DSLR은 연속적으로 이미지를 촬영할 수 있으므로(Nikon의 D4는 11fps로 촬영) 첫 번째 단계에서 원본 파일 이름을 유지하는 것이 좋습니다. 그렇지 않으면 동일한 파일 이름을 가진 여러 파일이 생길 수 있습니다.
exiv2 mv -r %Y%m%d-%H%M%S:basename: *.NEF
# exiv2 uses `strftime(3)`, so `%Y%m%d-%H%M%S` returns YYYYMMDD-hhmmss
# :basename: is a naming variable exiv2's `-r`-handle provides. See `exiv2 -h` for more
# Now you have files with names like 20140630-235958_DSCC1234.NEF.
# Note that chronological order and lexicographic order agree with this naming format.
i=10000
for x in *.NEF; do
i=$((i+1))
mv "$x" "${x%-*}_FOO_${i#1}.NEF"
done
${x%-*}
문자 뒤의 부분을 삭제합니다 -
. 카운터 변수는 i
10000부터 계산을 시작하고 1
선행 숫자를 제거하여 사용됩니다. 이는 모든 카운터 값이 동일한 숫자를 갖도록 선행 0을 얻는 트릭입니다.
파일 이름의 숫자를 늘려 파일 이름 바꾸기카운터를 포함하도록 여러 파일의 이름을 바꾸는 다른 솔루션이 있습니다.
Exif 데이터 대신 파일의 타임스탬프를 사용하려면 다음을 참조하세요.파일 이름 끝에 수정된 날짜 타임스탬프를 사용하여 여러 파일의 이름을 바꾸시겠습니까?
일반적으로 셸 코드를 생성한 다음 이를 셸에 파이프하지 마십시오. 이것은 불필요하게 복잡합니다. 예를 들어, 대신
find -name '*.NEF' |
gawk 'BEGIN{ a=1 }{ printf "mv %s %04d.NEF\n", $0, a++ }' |
bash
당신은 쓸 수 있습니다
find -name '*.NEF' |
gawk 'BEGIN{ a=1 }{ system(sprintf("mv %s %04d.NEF\n", $0, a++)) }'
'
파일 이름이 쉘 코드로 해석되기 때문에 파일 이름에 쉘 특수 문자(예: 공백, , , 등)가 포함되어 있으면 두 버전 모두 비참한 결과를 초래할 수 있습니다 . 이를 강력한 코드로 바꾸는 방법이 있지만 가장 쉬운 방법은 아니므로 해당 접근 방식을 사용하지 않겠습니다.$
`
1 "ctime"이라는 것이 있지만 c
작동하지 않습니다.만들다, 이것은변화. ctime은 파일 내용이나 메타데이터(이름, 권한 등)가 변경될 때마다 변경됩니다. ctime은 생성 시간과 거의 반대입니다.
답변2
stat --printf='}" "%z_${SN}_${LINENO}"\n' -- * |
nl -nln -w1 -s '' |
sort -k2,3 |
sed 's| ..:[^_]*||;s|-||g;s|^|echo mv "${|' |
SN=SHOOTNAME sh -s -- *
위 명령은 사용자의 요구 사항을 충족해야 합니다. 아마도 귀하가 요구하는 것보다 더 일반적일 것입니다. 그러나 보다 구체적인 예를 보려면 이 답변의 하단을 참조하십시오.
*
현재 디렉터리의 모든 파일을 반복하고 변경 시간을 인쇄하는 방식 으로 작동합니다 . 접미사가 포함된 현재 디렉터리의 파일의 경우 줄 1과 5 끝에 있는 globstar를 로 변경 .NEF
해야 합니다 . 그러면 끝에 일부 셸 변수와 따옴표가 추가됩니다. 이러한 이름은 파이프의 다른 쪽 끝에 만 존재합니다. 서브쉘.*
*.NEF
sh
또한 ${1}
전역 순서나 쉘 유형 매개변수를 통해서만 파일 이름을 지정하기 때문에 파일 이름에 어떤 이상한 문자가 포함되어 있든 관계없이 모든 파일 이름에 대해 작동합니다.
현재 명령에는 다음이 포함되어 있습니다 . 이 명령 echo
은 약화되었습니다. 실행은 기본적으로 작동하지 않습니다. 단지 수행하려는 작업을 보여줄 뿐입니다. 이것은 내 홈 디렉토리의 출력이며 다음에 공급됩니다 sh
.
echo mv "${2}" "20140611_${SN}_${LINENO}"
echo mv "${4}" "20140614_${SN}_${LINENO}"
echo mv "${11}" "20140617_${SN}_${LINENO}"
echo mv "${7}" "20140622_${SN}_${LINENO}"
echo mv "${8}" "20140622_${SN}_${LINENO}"
echo mv "${1}" "20140624_${SN}_${LINENO}"
echo mv "${10}" "20140704_${SN}_${LINENO}"
echo mv "${5}" "20140704_${SN}_${LINENO}"
echo mv "${9}" "20140704_${SN}_${LINENO}"
echo mv "${12}" "20140705_${SN}_${LINENO}"
echo mv "${3}" "20140705_${SN}_${LINENO}"
echo mv "${13}" "20140706_${SN}_${LINENO}"
echo mv "${6}" "20140706_${SN}_${LINENO}"
다음은 다음과 같습니다 sh
.
mv Desktop-1 20140611_SHOOTNAME_1
mv Library 20140614_SHOOTNAME_2
mv target.txt 20140617_SHOOTNAME_3
mv script.sh 20140622_SHOOTNAME_4
mv script.sh~ 20140622_SHOOTNAME_5
mv Desktop 20140624_SHOOTNAME_6
mv shot-2014-06-22_17-11-06.jpg 20140704_SHOOTNAME_7
mv Terminology.log 20140704_SHOOTNAME_8
mv shot-2014-06-22_17-10-16.jpg 20140704_SHOOTNAME_9
mv test 20140705_SHOOTNAME_10
mv Downloads 20140705_SHOOTNAME_11
mv test.tar 20140706_SHOOTNAME_12
mv new
file 20140706_SHOOTNAME_13
내 출력에는 생성된 시간에 따라 이름이 지정된 일부 이미지 파일이 표시되지만 새로 할당된 이름이 일치하지 않는 것을 볼 수 있습니다. 이는 sort
지정된 대로 작업한 결과가 아니라 해당 파일이 해당 날짜에 마지막으로 상태가 변경되었다는 사실입니다. 그럼에도 불구하고 이 질문에 대한 의견에서 지정한 대로 ctime
찾고 있는 속성은 여기에 제공된 정렬 및 이름 속성입니다. 그럼에도 불구하고 stat
파일 이름이 추가된 출력은 다음과 같습니다 .
stat -c '%z %n' -- *
2014-06-24 16:50:09.110283839 -0700 Desktop
2014-06-11 23:34:02.981981145 -0700 Desktop-1
2014-07-05 01:00:43.213344635 -0700 Downloads
2014-06-14 10:32:13.537014418 -0700 Library
2014-07-04 23:02:25.079690701 -0700 Terminology.log
2014-07-06 11:24:05.398936386 -0700 new
file
2014-06-22 11:26:53.658004123 -0700 script.sh
2014-06-22 11:26:53.658004123 -0700 script.sh~
2014-07-04 13:34:00.063296353 -0700 shot-2014-06-22_17-10-16.jpg
2014-07-04 13:34:00.066629687 -0700 shot-2014-06-22_17-11-06.jpg
2014-06-17 19:59:38.475358571 -0700 target.txt
2014-07-05 23:53:39.097065292 -0700 test
2014-07-06 00:38:57.060521397 -0700 test.tar
위의 출력은 전체 파이프라인이 수행하는 작업을 보여주는 데도 도움이 됩니다.
따라서
stat --printf='}" "%z_${SN}_${LINENO}"\n'
다음과 같은 줄이 인쇄됩니다.}" "YYYY-MM-DD HH:MM:SS.NS -TZ_${SN}_${LINENO}"
... 그들이 나타내는 YMDHMS.NS -TZ
개별 구성 요소는 어디에 있습니까 ? 출력 형식은 파일 생성 시간 - 마지막 액세스 시간 - 또는 마지막 수정 시간과 동일하므로 위 명령문에서 이들 중 하나를 바꾸면 해당 값으로 확장됩니다. 주석에서 이미 논의한 것처럼 파일 생성 시간은 신뢰할 수 있는 속성이 아니며 이를 지원하지 않으면 .strftime
ctime
%w
%x
%y
%z
%w
0
이는 쉘 glob의 모든 파일 또는 사용자가 제공하는 모든 쉘 glob *
에 대해 이 작업을 수행합니다 . 예를 들어 *.NEF
현재 디렉토리에 .NEF
접미사가 있는 파일 에만 해당됩니다.
목록에는
nl
각 행에 대해 1씩 증가하는 숫자가 전달됩니다. 행-n
번호는ln
왼쪽 정렬되고 0으로 채워지지 않으며 최소-w
ID는 1이고 행의 내용과''
빈 문자열 만 구분됩니다.-s
다음과 같이 출력됩니다.I}" "YYYY-MM-DD HH:MM:SS.NS -TZ_${SN}_${LINENO}"
... I
각 행의 번호는 어디에 있습니까?
sort
-k2,3
두 번째 필드의 입력을 세 번째 필드 또는 에서 정렬합니다YYYY-MM-DD HH:MM:SS.NS
. 이 시점에서 모든 행의 유일한 고유 품질은 날짜I
이고I
건너뛰기 때문에 그보다 더 구체적일 필요는 없습니다. 이는 또한 날짜가 아닌 숫자로 이름이 지정되는 파일에 대한 귀하의 의견을 해결합니다. 나~해야 한다우선 이전 작업은 다 해놨는데sort
분, 초 등으로 정렬할 생각은 못했어요.sed
내 테스트 기반은 다음과 같이 생성됩니다.
for s in 9 8 7 6 5 4 3 2 1; do touch $s && sleep 1; done
만약 내가sort
뒤쪽에 sed
- 이 편집 이전에 했던 것처럼, 그러면 필드가 각 파일에 대해 동일 9
하고 행 순서에 영향을 주지 않으므로 파일 이름이 변경됩니다. 하지만 이 조정을 통해 명령은 나노초에만 적용되므로 이름이 바뀌고 그 반대의 경우도 마찬가지입니다 . 이 문제에 관심을 가져주신 @Seul에게 감사드립니다.${DATE}.SHOOTNAME.9
YMD
sort
sort
9
${DATE}.SHOOTNAME.1
1
sed
그런 다음 밑줄<space><any char><any char>:<colon>
이 아닌 순서대로 다음과 같은 첫 번째 문자열과 문자를 제거합니다 . 따라서 이 시점에서 라인은 다음과 같습니다:^
_
I}" "YYYY-MM-DD_${SN}_${LINENO}"
...다음으로 모든 -
대시를 제거합니다. 마지막으로 각 행의 머리 부분 echo mv "${
에 삽입되므로 ^
다음과 같습니다.
echo mv "${I}" "YYYYMMDD_${SN}_${LINENO}"
- 마지막으로
sh
ell이 호출되고 환경 변수가 선언됩니다$SN
. 여기서 해당 값은 입니다SHOOTNAME
. POSIX는 쉘이 읽는 각 라인에 대해 var를 증가시키도록 지정하므로$LINENO
우리가 제공하는 각 라인에 대해 파일 이름의 값은 마지막 라인보다 1 더 많이 확장되어야 합니다. 귀하의 의견에서 알 수 있듯이 어떤 이유로 이것이 발생하지 않는 경우 완벽하게 유효한 대안은 아래 특정 예에서 제공한 것처럼$((i=i+1))
먼저stat
파이프의 라인 1로 인쇄하는 것입니다.
쉘은 또한 glob에 설정된 위치 인수를 사용하여 호출됩니다. 여기서는 *
현재 디렉토리의 모든 파일에 대한 globstar입니다. 이미 언급했듯이 *.NEF
이 줄과 첫 번째 줄에는 현재 디렉터리의 파일 이름에만 .NEF
.
glob이 첫 번째 행의 glob과 동일하다면 nl
번호가 매겨진 순서대로 glob됩니다. 따라서 어떤 줄에 나타나든 출력을 기반으로 할당한 동일한 파일 이름 "${1}"
으로 확장됩니다 . nl
이렇게 하면 날짜별로 올바른 순서로 파일 이름을 빠르고 안전하게 바꿀 수 있습니다.
echo
이미 언급했듯이 여기서는 명령을 약화시켰습니다 . 그러나 이를 실행하여echo
효과가 있다고 판단되면 제거하는 것이 좋습니다echo
.
이와 같이:
stat --printf='}" "%z_${SN}_${LINENO}"\n' -- * |
nl -nln -w1 -s '' |
sort -k2,3 |
sed 's| ..:[^_]*||;s|-||g;s|^|mv "${|' |
SN=SHOOTNAME sh -s -- *
아니면:
export SN=SHOOTNAME SUFX=.NEF
stat --printf='}" "%z_${SN}_$((i=i+1))${SUFX}"\n' -- *$SUFX |
nl -nln -w1 -s '' |
sort -k2,3 |
sed 's| ..:[^_]*||;s|-||g;s|^|mv "${|' |
sh -s -- *$SUFX
여기서는 쉘 함수로 처리됩니다.
_batch_date_rename () ( # a big one
ERR= # for error reporting
export "DIR=$1" "SUFX=$2" \ # args 1,2 must be dirname and file suffix
"NAME=${3-${ERR:?no rename string specified}}" \ # need name string
"TIME=${4-%y}" INT=$((${INT:-25}*3)) ${NOCONFIRM+NOCONFIRM=}
#all above vars are exported to all points below
_path_chk () { #run once at start - fn quits if any below test fails
[ -d "$1" ] && [ -w "$1" ] && set -- "$1"/*"$2" && [ -e "$1" ]
} # chks for user writable dirname and resolvable $1/*$2 glob
_print_fmt () { #shell printf now not stat - last field zero padded
printf 'mv "${%d}" "${DIR}/%d_${NAME}_%04d${SUFX}"\n' "$@"
}
_print_mv () { #prints copy of mv action before attempting
echo '(set -x' #uses shells debug printer to show expanded vals
printf ': ${0+%s}\n' "$@" \
${NOCONFIRM-'Key "ENTER" to accept or "CTRL+C" to quit'}
echo \) #above can be disabled by declaring NOCONFIRM at invocation
} #by default fn batches 25 mvs at a time, displays them, and confirms
_read_loop () { #parses piped in with IFS, batches in INTerval of 25
argc=${1-$argc} ; ${1+shift} #total globbed files - quit point
while IFS=' -' read nl y m d na ; do #split on -
set -- "$@" "$nl" "$y$m$d" "$((i=i+1))" #build array until
[ "$#" -ge "$INT" ] && break #hit interval
done ; IFS='
'; set -- $(_print_fmt "$@") && unset IFS #finalize array in _print_fmt
_print_mv "$@" #do the debug out
${NOCONFIRM+:} read < /dev/tty #if $NOCONFIRM not set confirm
printf '%s\n' "$@" #now print the actual command
[ $((argc>i)) -eq 1 ] || echo 'exit 0' #check if quit point
_read_loop #if not quit repeat
}
_pipeline () { #this is mostly same - no sed though
stat -c "$TIME" -- "$@" | nl -nln -w1 -s ' ' | sort -k2,3 | {
_read_loop $# || echo 'exit 1' #read loop stands in for sed
} | sh -s -- "$@" #sh still evaluates on args
} #only two calls from main function below
_path_chk "$1" "$2" || ${ERR:?Invalid pathname parameters specified}
_pipeline "$DIR"/*"$SUFX" #if _path_chk do _pipeline
) #that's all folks
이는 쉘을 사용하여 다른 유틸리티로 수행하는 일부 작업을 수행합니다. 개념은 동일합니다. 파일의 전체 목록이 다르게 정렬되고 정렬 순서가 저장됩니다. 무엇인가요진짜이 아이디어의 차이점은 간격을 두고 이동 작업을 일괄 처리하고, 사용자에게 수행할 작업을 보여주고, 계속하기 전에 프롬프트를 기다린다는 것입니다. 내 상황을 기록하는 데 사용했어요여기이렇게 하면 터미널 세션을 관찰하여 작동 방식을 확인할 수 있습니다.