디렉터리에 1000개의 파일이 있고 파일 이름을 기준으로 하위 디렉터리로 정렬하고 싶습니다. 이들은 모두 집합 구조 p-[번호]_n-[번호]_a-[번호].[ext]를 따라 일관되게 이름이 지정됩니다.
이건 작은 샘플인데...
- p-12345_n-987_a-1254.jpg
- p-12345_n-987_a-9856.pdf
- p-12345_n-987_a-926.docx
- p-12345_n-384_a-583.pdf
- p-12345_n-384_a-987.pdf
- p-2089_n-2983_a-2348.gif
- p-2089_n-1982_a-403.jpeg
- p-38422_n-2311_a-126.pdf
- p-38422_n-2311_a-5231.docx
내가 원하는 것은 다음과 같은 폴더 구조입니다.
p-12345
⊢ n-987
⊢ p-12345_n-987_a-1254.jpg
⊢ p-12345_n-987_a-9856.pdf
⊢ p-12345_n-987_a-926.docx
⊢ n-384
⊢ p-12345_n-384_a-583.pdf
⊢ p-12345_n-384_a-987.pdf
p-2089
⊢ n-2983
⊢ p-2089_n-2983_a-2348.gif
⊢ n-1982
⊢ p-2089_n-1982_a-403.jpeg
p-38422
⊢ n-2311
⊢ p-38422_n-2311_a-126.pdf
⊢ p-38422_n-2311_a-5231.docx
이것이 의미가 있기를 바랍니다.
이런 식으로 파일을 구성하는 스크립트를 작성할 수 있습니까?
편집: 명확히 하기 위해: 예, 제 질문은 다음과 같아야 합니다.어떻게파일을 정리하는 스크립트를 작성할 수 있나요? :) 저는 Unix와 명령줄을 처음 접했습니다. 지금까지는 기본 쉘 스크립트만 작성/사용했습니다. 대답에는 정규 표현식이 포함될 수 있을 것 같지만 그 외에는 어디서부터 시작해야 할지 잘 모르겠습니다.
내가 생각해낸 최고의 아이디어는
- 파일 목록을 텍스트 파일로 내보내기
- "_n"과 "_a"를 찾아 "/n"과 "/a"로 바꿉니다.
- 다음에서 일련의 mv 명령을 만듭니다.
- 쉘 스크립트로 저장
나는 이것이 필요한 것보다 훨씬 더 장황하다고 확신합니다. 또한 나중에 더 많은 파일을 사용하여 이 작업을 수행해야 할 경우를 대비하여 재현 가능한 것을 갖고 싶습니다.
답변1
틀림없이:
#!/bin/bash
for i in p-*_n-*.*; do
Ppart=${i/_n-*}
x=${i/${Ppart}_/}
nPart=${x/_a-*}
mkdir -p $Ppart/$nPart
mv $i $Ppart/$nPart
done
제공한 패턴과 일치하는 모든 파일 이름을 반복하여 시작하십시오. 각 루프에서 쉘 대체를 사용하여 _n-
P 부분(첫 번째 수준 디렉터리)을 제공하는 부분으로 시작하는 파일 이름의 마지막 부분을 제거합니다 . 이제 부품 n-
부터 시작하여 N개의 부품이 필요합니다 _a-
. 이 작업은 두 단계로 수행됩니다. 먼저 Ppart를 삭제한 다음 _a-
해당 부분에서 시작하는 마지막 부분을 삭제합니다.
이제 mkdir -p
필요한 디렉토리를 생성하는 데 사용됩니다. mkdir -p
경로가 이미 존재하는 경우 오류가 발생하지 않으므로 mkdir -p
명령 실행을 결정하기 전에 디렉터리가 존재하는지 테스트하는 대신 그냥 수행하는 것이 더 쉽습니다.
마지막으로 파일을 올바른 디렉토리에 mv하십시오.
답변2
이미 지적했듯이 짧은 대답은 "예"입니다.
긴 대답은 다음과 같습니다. awk
디렉터리 구조의 기반이 될 파일 이름 요소를 추출하는 bash 스크립트를 사용하여 이 작업을 수행할 수 있습니다. 다음과 같이 보일 수 있습니다("한 줄"의 간결성보다 가독성이 더 강조되는 경우).
#!/bin/bash
for FILE in p-*
do
if [[ ! -f $FILE ]]; then continue; fi
LVL1="$(awk '{match($1,"^p-([[:digit:]]+)_[[:print:]]*",fields); print fields[1]}' <<< $FILE)"
LVL2="$(awk '{match($1,"^p-([[:digit:]]+)_n-([[:digit:]]+)_[[:print:]]*",fields); print fields[2]}' <<< $FILE)"
echo "move $FILE to p-$LVL1/n-$LVL2"
if [[ ! -d "p-$LVL1" ]]
then
mkdir "p-$LVL1"
fi
if [[ ! -d "p-$LVL1/n-$LVL2" ]]
then
mkdir "p-$LVL1/n-$LVL2"
fi
mv $FILE "p-$LVL1/n-$LVL2"
done
설명하다:
- 현재 디렉토리에서 "p-"로 시작하는 모든 파일에 대해 루프를 수행합니다.
- 루프의 첫 번째 명령은 파일이 존재하는지 확인하고 빈 디렉토리에 대한 해결 방법입니다(이 포럼에서는 항상 다음과 같이 설명하기 때문에 필요합니다).출력을 구문 분석하지 않습니다.
ls
, 따라서 그러한 것은FILES=$(ls p-*); for FILE in $FILES; do ...
금지된 것으로 간주됩니다). - 그런 다음 (의심할 수 있듯이 정규식을 사용하여)
p-
디렉토리 구조의 첫 번째 수준을 생성하는 데 필요한 와 사이의 숫자를 추출하고, 두 번째 수준 사이의 숫자에도 동일하게 적용됩니다. 아이디어는 입력에서 지정된 정규 표현식이 발생하는 위치를 찾을 뿐만 아니라 "필드" 배열에서 괄호 안의 모든 요소에 대한 "완전한" 값을 제공하는 함수를 사용하는 것입니다._n
awk
n-
_a
match
( ... )
- 셋째, 원하는 디렉토리 구조의 첫 번째 및 두 번째 수준의 디렉토리가 이미 존재하는지 확인합니다. 그렇지 않은 경우에는 생성합니다.
- 마지막으로 파일을 대상 디렉터리로 이동합니다.
자세한 내용은 다음을 확인하세요.고급 bash 스크립팅 가이드그리고GNU Awk 사용자 가이드.
스크립트와 정규식을 더 많이 사용하면 더 간결하게 만들 수 있습니다. 예를 들어 위 스크립트에서는 디렉터리/하위 디렉터리 경로 생성을 단일 awk
호출로 쉽게 줄일 수 있습니다.
먼저 디렉터리 이름은 다음과 같으므로실제로
p-<number>
그리고n-<number>
파일 이름과 마찬가지로awk
다음을 입력하여 이러한 문자를 추출할 수도 있습니다.match($1,"(^p-[[:digit:]]+)_(n-[[:digit:]]+)_[[:print:]]*",fields)
awk
적절한 매개변수를 사용하여 디렉터리 하위 디렉터리 경로도 생성하도록 하면 작업 부하를 더욱 줄일 수 있습니다print
.
awk '{match($1,"(^p-[[:digit:]]+)_(n-[[:digit:]]+)_[[:print:]]*",fields); print fields[1] "/" fields[2]}'
p-12345/n-384
파일(예:)에 대해 쉽게 생성됩니다 p-12345_n-384_a-583.pdf
. 이것을 @wurtel이 지정한 사용법과 결합하면 mkdir -p
스크립트는 다음과 같이 보일 수 있습니다.
for FILE in p-*
do
if [[ ! -f $FILE ]]; then continue; fi
TARGET="$(awk '{match($1,"(^p-[[:digit:]]+)_(n-[[:digit:]]+)_[[:print:]]*",fields); print fields[1] "/" fields[2]}' <<< $FILE)"
echo "move $FILE to $TARGET"
mkdir -p "$TARGET"
mv $FILE $TARGET
done
답변3
Python(3)의 다른 버전:
import os
sourcepath='/path/to/source'
destination='/path/to/destination'
(_,_,fnames) = next(os.walk(sourcepath))
for f in fnames:
subpath = '/'.join(f.split('_')[:-1])
print("Moving {} to {}".format(os.path.join(sourcepath, f), os.path.join(destination, subpath , f)))
os.makedirs(os.path.join(destination, subpath), exist_ok=True)
os.rename(os.path.join(sourcepath, f), os.path.join(destination, subpath , f))
답변4
멋진 한 줄짜리는 어떻습니까?
ls | awk -F"_" '{system("mkdir -p " $1 "/" $2 "&& mv " $0 " " $1 "/" $2 "/" $0)}'
_
생성하려는 디렉터리 에 따라 파일 이름 부분을 분리한 다음 변경되지 않은 파일 이름을 새로 생성된 디렉터리로 이동합니다.