디렉터리 이름 끝에 있는 연도를 앞으로 이동합니다. YYYY-MM-DD 형식으로 변환할 수 있습니다.

디렉터리 이름 끝에 있는 연도를 앞으로 이동합니다. YYYY-MM-DD 형식으로 변환할 수 있습니다.

Mac에서 PC로 모든 사진을 내보냈습니다(Windows 11, PowerShell을 통해 Ubuntu 실행). 내보내기는 원래 생성 날짜가 제거된 다음 형식으로 각 디렉터리(수천 개의 폴더)를 생성했습니다.

April 1, 2004         
Amsterdam, September 18, 2018    
Anderson Arbor, March 24, 2011   

날짜별로 정렬하고 싶습니다. 이러한 형식을 다음과 같이 변경하는 방법에 대한 아이디어가 있습니까?

2004, April 1   
2018, Amsterdam, September 18    
2011, Anderson Arbor, March 24

아니면 더 나은 방법은 다음과 같습니다.

2004-04-01    
2018-09-18 Amsterdam     
2011-03-24 Anderson Arbor

답변1

그리고 zsh:

$ autoload -Uz zmv
$ zmodload zsh/datetime
$ zmv -n '(|(*), )([^ ]## <1-31>, <->)' \
         '$(strftime %F $(strftime -r "%B %d, %Y" $3))${2:+ $2}'
mv -- 'Amsterdam, September 18, 2018' '2018-09-18 Amsterdam'
mv -- 'Anderson Arbor, March 24, 2011' '2011-03-24 Anderson Arbor'
mv -- 'April 1, 2004' 2004-04-01

( -n만족하면 삭제(테스트 실행)).

zmv일괄 이름 바꾸기를 위한 자동 로드 가능한 zsh 함수입니다.

두 가지 매개변수가 필요합니다.

  1. 전역 모드(확장이 자체적으로 수행되므로 따옴표로 묶어야 함 zmv).
  2. 일치하는 각 파일을 대체하기 위해 문자열이 평가됩니다.

대체에서 일치하는 각 쌍은 , ... (...)에서 찾을 수 있습니다 .$1$2

여기서 전역 패턴은 (|(*), )([^ ]## <1-31>, <->)다음과 같습니다.

  • (x|y)또는 와 일치하므로 x무엇 y이든 (|(*), )일치하거나( 두 번째 쌍에 있으므로 대체 항목으로 *끝날 것임 ) 로 끝납니다 .$2(...)", "
  • [^ ]x##하나 이상의 xs 와 일치하는 공백을 제외한 모든 단일 문자와 일치하므로 [^ ]##공백이 아닌 하나 이상의 문자로 구성된 모든 시퀀스와 일치합니다.
  • <->, 경계 가 없는 <x-y>모든 숫자 및 일련의 10진수와 일치합니다 .

대체에서는 모듈 strftime에 내장된 함수를 사용하여 에서 캡처된 날짜를 zsh/datetime구문 분석(소비 )하고 형식을 다시 지정합니다 . 이 내장 기능은 간단한 표준 인터페이스를 제공하며 각각 format 및 ass 기능을 제공합니다 .-r$3strftime()strptime()fptimestr

%F%Y-%m-%d날짜 형식을 YYYY-MM-DD로 표시하는 약어 로 , %B전체 월 이름, %d특정 날짜, %Y연도를 나타냅니다.


명령 대체에 사용하는 대신 langinfo에서 월 이름 목록을 가져온 다음 수동으로 월 번호 및 형식으로 변환할 수 있습니다. strftime이는 출력을 얻을 수 있도록 매번 프로세스를 포크하는 것을 의미합니다.

zmodload zsh/langinfo
autoload -Uz zmv
months=( ${(v)langinfo[(I)MON_<1-12>]} )
#       1 2     3                 4         5
zmv -n "(|(*), )(${(j[|])months}) (<1-31>), (<->)" \
       '$5-${(l[2][0])months[(I)$3]}-${(l[2][0])4}${2:+ $2}'

어디:

  • ${(v)langinfo[(I)MON_<1-12>]}키가 일치하는 연관 배열의 값으로 확장됩니다 v.$langinfoMON_<1-12>
  • ${(j[|])months} joins 배열의 요소입니다 |.
  • $array[(I)$3]에 저장된 패턴과 일치하는 배열 요소의 최대 인덱스를 반환합니다 $3.
  • ${(l[2][0])string} l2eft는 s로 문자열의 길이를 채웁니다 0.

답변2

findPerl rename유틸리티( 사용 중인 배포판 에 따라 라고도 prename함 )를 사용하고file-renameperl-rename날짜::분석그리고날짜 형식라이브러리 모듈.

이러한 모듈은 Perl에 포함되어 있지 않으므로 별도로 설치하거나 Perl을 통해 설치해야 합니다.명령줄 도구 또는 배포 패키지. 다들 여기 있어요시간 날짜데비안에서는 .bundle 을 통해 설치할 수 있습니다 sudo apt-get install libtimedate-perl. 다른 배포판에서는 이 패키지의 이름이 무엇인지 모르지만 아마도 매우 유사할 것입니다.

그런데 Perl 유틸리티와 완전히 다르고 호환되지 않는 기능 및 명령줄 옵션을 가진 유틸리티를 혼동하지 마십시오 rename.renameutil-linux

그래도:

$ mkdir 'April 1, 2004' 'Amsterdam, September 18, 2018' 'Anderson Arbor, March 24, 2011'

$ find . -type d -print0 |
  rename -n -0 'BEGIN {
                  use Date::Parse;
                  use Date::Format;
                  our $date_pattern = qr/[[:alpha:]]+ \d+, \d\d\d\d/;
                };
                our $date_pattern;

                if (/^(.*\/)(.*)\b($date_pattern)$/) {
                  my ($p1, $p2, $d) = ($1, $2, $3);
                  my $t = str2time($d);

                  s/\b$date_pattern//;
                  $_ = sprintf("%s%s%s", $p1, time2str("%Y-%m-%d ",$t), $p2);
                  s/[, ]*$//;
                }'
rename(./April 1, 2004, ./2004-04-01)
rename(./Amsterdam, September 18, 2018, ./2018-09-18 Amsterdam)
rename(./Anderson Arbor, March 24, 2011, ./2011-03-24 Anderson Arbor)

참고: -n이는 이 옵션으로 인한 시험 실행입니다. 원하는 대로 작동한다고 확신하면 -n이를 제거하거나 -v자세한 출력으로 교체하세요.

Date::Parse 및 Date::Format 모듈을 로드하고 $date_pattern블록( )에 변수를 정의한 후 BEGIN {}이 이름 바꾸기 스크립트는 각 디렉터리 이름을 반복하고 "월, 연도" 패턴을 일치시키려고 시도합니다.

성공하면 경로 이름의 마지막 요소를 제외한 모든 요소가 변수로 캡처/추출되며 $p1, 날짜 패턴 앞에 있는 최종 디렉터리 요소의 "접두사" 부분이 변수로 들어갑니다 $p2(이는 선택 사항이며 누락된 경우 빈 문자열) 날짜 패턴을 변수에 넣습니다 $d.

그런 다음 일치하는 날짜 는 값(1970년 1월 1일 자정 시점 이후 초) $d으로 변환되고 이 함수를 사용하여 변수에 할당됩니다.time_t$tstr2time()

그 다음에:

  1. 디렉터리 이름에서 날짜 패턴이 제거되었습니다.

  2. sprintf및 함수는 를 time2str()변경하여 전체 파일 이름을 새로운 패턴에 맞게 수정하는 데 사용됩니다 $_.

    이 간단한 할당은 $_Perl이 어떻게 작동하는지에 대한 매우 간단하지만 중요한 사실을 강조합니다 rename.

    Perl에서 다른 변수 이름(참조 및 검색)이 지정되지 않은 경우(연산자 포함) 해당 변수는 $_루프의 기본 변수로 사용되며 많은 함수 및 연산자의 기본 인수/피연산자로 사용됩니다.man perlvar$_s///

    Perl 이름 바꾸기의 경우 $_현재 경로/파일 이름을 변경하면 $_이름이 바뀌게 됩니다. 변경 되면 $_이름도 변경됩니다. 변하지 않으면 아닙니다.

    알아채다rename 에 익숙해-f이 옵션을 사용하여 이 작업을 강제로 적용 하지 않는 한 기존 파일 이름을 덮어쓰도록 파일이나 디렉터리의 이름을 바꿉니다 .

  3. 그런 다음 최종 디렉터리 이름에서 모든 후행 공백과 쉼표(있는 경우)를 제거합니다.


기타 참고사항:

  1. 변수 는 스크립트가 시작될 때 한 번만 실행되도록 $date_pattern블록에 정의됩니다 (stdin의 각 디렉터리 이름에 대해 한 번이 아니라). BEGIN{}두 번 사용되기 때문에 변수로 정의됩니다. 변경해야 하는 경우 한 곳에서만 변경하면 되는 것이 가장 좋습니다. "하나 이상의 영문자, 하나의 공백, 하나 이상의 숫자, 또 하나의 공백 및 4개의 숫자"로 정의됩니다. 이는 예제 디렉터리 이름과 일치하지만 다른 변형이 가능한 경우 조정해야 할 수도 있습니다.

    이름 바꾸기 스크립트의 전체 범위 내에서 사용할 수 있도록 로 선언됩니다 our(그렇지 않으면 BEGIN 블록 내에서만 사용 가능). Perl 이름 바꾸기 스크립트는 strictPerl 모드에서 실행됩니다( perldoc strict자세한 내용 참조).

    qr정규식을 인용하는(아마도 성능 향상을 위해 미리 컴파일하는) Perl 인용 연산자입니다. 여기서 성능은 아마도 문제가 되지 않을 것입니다. 여기서는 주로 작은 따옴표를 사용하는 것이 한 줄 내에서 고통스럽다는 사실을 해결하기 위해 사용하고 있습니다. 스크립트. perldoc -f qr자세히보다.

  2. find ... -print0and 옵션 rename을 사용하면 NUL이 파일 이름 구분 기호로 사용되므로 이는 모든 디렉터리 이름, -0개행 및 기타 공백 또는 셸 메타 문자(예: 등)가 포함된 ;디렉터리 이름 에도 적용됩니다 &. NUL >오직경로 이름에 나타날 수 없는 문자입니다.

답변3

사용진주의rename:

Time::PiecePerl의 핵심에 위치하며 100% 신뢰할 수 있는 Date::Parse모듈이라기보다는 "마법"보다 더 깔끔한 방법으로 간주됩니다.

rename -n '
    BEGIN{use Time::Piece}
    my $d = localtime;
    my $months_re = join "|", map { "$_\\w+" } $d->mon_list;
    s/
        ($months_re)
        \s+(\d+),\s+(\d{4})
    /
        my $d = Time::Piece->strptime("$1 $2, $3", "%B %d, %Y");
        $d->strftime("%F")
    /xe;
    s/(.*),\s+(\d{4}-\d{2}-\d{2})/$2 $1/;
' ./*/

출력이 만족스러우면 제거하십시오 -n(일명).dry-run

산출

rename(Amsterdam, September 18, 2018/, 2018-09-18 Amsterdam/)
rename(Anderson Arbor, March 24, 2011/, 2011-03-24 Anderson Arbor/)
rename(April 1, 2004/, 2004-04-01/)

관련 정보