존재하지 않는 대상 디렉터리를 사용하여 명령 이름 바꾸기

존재하지 않는 대상 디렉터리를 사용하여 명령 이름 바꾸기

다음과 같은 명령을 사용하여 파일을 하위 디렉터리로 자동 그룹화하려고 합니다.

$ rename 's/(.)(.)(.+)/$1\/$2\/$1$2$3/' *.*

매개변수를 사용한 시험 실행은 -n내가 원하는 것을 보여주었습니다.

test.jpg renamed as t/e/test.jpg

그러나 실제 이름 바꾸기는 실패했습니다.

Can't rename test.jpg t/e/test.jpg: No such file or directory

하위 디렉터리가 아직 존재하지 않기 때문입니다.

먼저 모든 하위 디렉터리를 수동으로 생성하지 않고 이를 달성하려면 어떻게 해야 합니까?

답변1

이름을 바꾸는 것과 다른 곳으로 옮기는 것에는 차이가 있습니다. 이 경우 가장 간단한 방법(현재 bash)은 모든 파일을 반복하는 것입니다.

for f in *.*
do
    d=${f::1}/${f:1:1}
    [ -d "$d" ] || mkdir -p "$d"
    mv "$f" "$d"
done

설명하다

이는 bash의 잘 알려지지 않은 "매개변수 확장/하위 문자열 확장" 기능을 활용합니다.

${var:offset:length}

    최대로 확장하세요.length캐릭터의 가치var 지정된 문자부터 시작offset.

.   [약간 의역함큰 타격(1).]

이 내용이 어디에 기록되어 있는지는 알 수 없지만,offset0에서 시작하고, 0첫 번째 문자도 0에서 시작하고, 1두 번째 문자도 0에서 시작합니다. 만약에 offsetnull(누락)이면 0으로 처리됩니다.

답변2

확장으로코스타스의 대답,

for f in *.*
do
    if [ "${f:0:1}" = . ]  ||  [ "${f:1:1}" = . ]
    then
        printf 'Cannot handle "%s"\n' "$f"
    else
        d=${f:0:1}/${f:1:1}
        if ! { [ -d "$d" ] || mkdir -p "$d"  &&  mv "$f" "$d"; }
        then
            break
        fi
    fi
done
  • 및 같은 디렉토리를 (실제로) 만들 수 없기 때문에 및 같은 파일 이름을 if 처리하기 위해 (첫 번째 항목)을 추가했습니다  . 이번에도 이라는 디렉터리를 생성하려고 시도합니다  . 1M.Butterfly.jpgQ.jpgM/.Q/..Dragon.mp4./D

  • &&mkdir와 사이에 mv)을  추가하여 mkdir이것이 실패하면 파일을 존재하지 않는 디렉토리로 이동하려고 시도하지 않습니다.

  • 두 번째 것을 추가한 if이유는 문제가 발생하면 스크립트를 종료하고 사용자가 문제를 파악(및 수정)할 수 있도록 하는 것이 합리적이기 때문입니다.
    (예를 들어, 쓰기 권한이 없는 디렉토리에서 실수로 스크립트를 실행했거나 파일 시스템이 가득 차서 새 하위 디렉토리를 생성할 수 없는 경우 어떻게 될까요?)

    와 같이 쓸 수 있어요

    [ -d "$d" ] || mkdir -p "$d"  &&  mv "$f" "$d  ||  break
    

    그러나 이러한 &&/ ||순서는 금방 혼란스러워질 수 있습니다.

또 다른 변형:

for f in *.*
do
    if [ "${f:0:1}" = . ]  ||  [ "${f:1:1}" = . ]
    then
        printf 'Cannot handle "%s"\n' "$f"
    else
        d=${f:0:1}/${f:1:1}
        if ! { [ -d "$d" ] || mkdir -p "$d"; }          # Note: no "mv" command in the loop.
        then
            break
        fi
    fi
done
rename 's/(.)(.)(.+)/$1\/$2\/$1$2$3/' *.*

이것의 장점은 수천 번 rename이 아니라 한 번만 호출된다는 것입니다 . mv여기에는 몇 가지 단점이 있습니다.

  • M.Butterfly.jpg및 같은 파일이 Q.jpg로 전달됩니다 rename.
  • renamemkdir실패 하더라도  실행됩니다. (이것은 수정하기 쉬울 수 있습니다. 한 가지 방법은  break으로 바꾸는 것이지만 exit우아해 보이지는 않습니다.)

나는 당신과 같은 버전을 가지고 있지 않지만 rename시도해 보았고 mv M.Butterfly.jpg M/.방금 M.Butterfly.jpg디렉토리로  이동했습니다 M. 따라서 두 번째 솔루션을 사용하는 경우 디렉터리를 나열하면  M하위 디렉터리 , , , aetc( , , , 등과 같은 파일 포함)와 . 그러면 당신은 그들과 함께 원하는 것은 무엇이든 할 수 있습니다. ________________ 1 실제로  이 명령은 성공하지만 , 및 이라는 디렉터리를 생성하게 되는데 이는 아마도 원하는 것이 아닐 것입니다.eioMany.jpgMemorable.jpgMinnesota.jpgMoments.jpgM.Butterfly.jpg

MQD

답변3

이는 스크립트 내에서 완전히 수행될 수 있습니다 rename.rename어느Perl 코드에서는 단순한 연산자에만 국한되지 않습니다 s///.

다음은 이름을 바꿀 모든 파일이 현재 디렉터리에 있고 접두사 rename없이 명령줄에 직접 전달되는 간단한 경우에 작동합니다 . 명령줄 옵션 으로 시작하는 파일 이름 해석이 있을 수 있으므로 이는 일반적으로 좋은 생각이 아닙니다../-rename

또는 를 rename통해 전달되는 경우와 같이 파일 이름에 디렉터리 접두사가 포함되어 있으면 작동하지 않습니다 .find./*.*

$ touch test.jpg test.txt
$ rename -n 'my ($a, $b) = (/(.)(.)/);
             mkdir $a unless -e $a;
             mkdir "$a/$b" unless -e "$a/$b";
             if (-e "$a/$b" && ! -d "$a/$b") {
               print STDERR "$_: $a/$b exists but is not a directory!\n";
             } else {;
               s/(.)(.)(.+)/$1\/$2\/$1$2$3/;
             }' *.*
rename(test.jpg, t/e/test.jpg)
rename(test.txt, t/e/test.txt)

더 나은 버전을 사용할 수 있습니다파일::기본 이름그리고파일 경로모듈은 둘 다 Perl에 포함되어 있습니다.

$ rename -n 'use File::Basename;
             use File::Path qw(make_path);

             my ($bn, $dn) = fileparse($_);
             my ($a, $b) = ($bn =~ /(.)(.)/);
             make_path("$dn$a/$b", { verbose => 1 });
             $_ = "$dn$a/$b/$bn"' ./*.*
mkdir ./t
mkdir ./t/e
rename(./test.jpg, ./t/e/test.jpg)
rename(./test.txt, ./t/e/test.txt)

이렇게 하면 각 파일의 원래 디렉터리 아래에 두 개의 하위 디렉터리가 생성됩니다. 함수 make_path()File::Path대상 디렉토리가 존재하는지 이미 확인합니다(존재하지만 디렉토리가 아닌 경우 치명적인 오류로 처리되지만 경고만 인쇄하고 다음 파일 이름으로 계속 진행하려는 경우 고유한 오류 처리를 구현할 수 있습니다). ).

원하는 경우 $dn이를 하드코딩하여 모든 파일의 이름을 대상 최상위 디렉터리로 설정하여 특정 디렉터리 트리로 바꿀 수 있습니다( File::Basename전체 경로 이름에서 기본 이름을 추출하려면 모듈을 사용해야 합니다).

관련 정보