![존재하지 않는 대상 디렉터리를 사용하여 명령 이름 바꾸기](https://linux55.com/image/61379/%EC%A1%B4%EC%9E%AC%ED%95%98%EC%A7%80%20%EC%95%8A%EB%8A%94%20%EB%8C%80%EC%83%81%20%EB%94%94%EB%A0%89%ED%84%B0%EB%A6%AC%EB%A5%BC%20%EC%82%AC%EC%9A%A9%ED%95%98%EC%97%AC%20%EB%AA%85%EB%A0%B9%20%EC%9D%B4%EB%A6%84%20%EB%B0%94%EA%BE%B8%EA%B8%B0.png)
다음과 같은 명령을 사용하여 파일을 하위 디렉터리로 자동 그룹화하려고 합니다.
$ 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).]
이 내용이 어디에 기록되어 있는지는 알 수 없지만,offset
0에서 시작하고, 0
첫 번째 문자도 0에서 시작하고, 1
두 번째 문자도 0에서 시작합니다. 만약에 offset
null(누락)이면 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.jpg
Q.jpg
M/.
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
.rename
mkdir
실패 하더라도 실행됩니다. (이것은 수정하기 쉬울 수 있습니다. 한 가지 방법은break
으로 바꾸는 것이지만exit
우아해 보이지는 않습니다.)
나는 당신과 같은 버전을 가지고 있지 않지만 rename
시도해 보았고 mv M.Butterfly.jpg M/.
방금 M.Butterfly.jpg
디렉토리로 이동했습니다 M
. 따라서 두 번째 솔루션을 사용하는 경우 디렉터리를 나열하면 M
하위 디렉터리 , , , a
etc( , , , 등과 같은 파일 포함)와 . 그러면 당신은 그들과 함께 원하는 것은 무엇이든 할 수 있습니다. ________________ 1 실제로 이 명령은 성공하지만 , 및 이라는 디렉터리를 생성하게 되는데 이는 아마도 원하는 것이 아닐 것입니다.e
i
o
Many.jpg
Memorable.jpg
Minnesota.jpg
Moments.jpg
M.Butterfly.jpg
M
Q
D
답변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
전체 경로 이름에서 기본 이름을 추출하려면 모듈을 사용해야 합니다).