확장자를 기준으로 파일을 정렬하는 이 스크립트를 어떻게 개선할 수 있나요?

확장자를 기준으로 파일을 정렬하는 이 스크립트를 어떻게 개선할 수 있나요?

내 다운로드 폴더를 탐색하고 확장자에 따라 파일을 정렬하는 작은 스크립트가 있습니다.

내가 이걸 어떻게 할 수 있지?더 깨끗하고/더 좋음? 확장명을 추가할 때마다 새 줄을 추가할 필요가 없도록 확장명과 해당 디렉터리 목록을 유지하고 for 루프 등을 사용하여 명령을 실행하고 싶습니다.

현재 스크립트:

#!/bin/sh

LOCKFILE=/tmp/.hiddensync.lock

if [ -e $LOCKFILE ]
        then
        echo "Lockfile exists, process currently running."
        echo "If no processes exist, remove $LOCKFILE to clear."
        echo "Exiting..."

        exit
fi

touch $LOCKFILE
timestamp=`date +%Y-%m-%d::%H:%M:%s`
echo "Process started at: $timestamp" >> $LOCKFILE

## Move files to various subfolders based on extensions
find ~/Downloads -maxdepth 1 -name "*.pdf" -print0 | xargs -0 -I % mv % ~/Downloads/PDF/
find ~/Downloads -maxdepth 1 -name "*.opm" -print0 | xargs -0 -I % mv % ~/Downloads/OPM/
find ~/Downloads -maxdepth 1 -name "*.yml" -print0 | xargs -0 -I % mv % ~/Downloads/YML/
find ~/Downloads -maxdepth 1 -name "*.css" -print0 | xargs -0 -I % mv % ~/Downloads/CSS/
find ~/Downloads -maxdepth 1 -name "*.tar.gz" -print0 | xargs -0 -I % mv % ~/Downloads/archives/
find ~/Downloads -maxdepth 1 -name "*.zip" -print0 | xargs -0 -I % mv % ~/Downloads/archives/
find ~/Downloads -maxdepth 1 -name "*.jpg" -print0 | xargs -0 -I % mv % ~/Downloads/Pictures/
find ~/Downloads -maxdepth 1 -name "*.png" -print0 | xargs -0 -I % mv % ~/Downloads/Pictures/
find ~/Downloads -maxdepth 1 -name "*.tiff" -print0 | xargs -0 -I % mv % ~/Downloads/Pictures/
find ~/Downloads -maxdepth 1 -name "*.pm" -print0 | xargs -0 -I % mv % ~/Downloads/Perl/
find ~/Downloads -maxdepth 1 -name "*.xls*" -print0 | xargs -0 -I % mv % ~/Downloads/Excel/
find ~/Downloads -maxdepth 1 -name "*.doc*" -print0 | xargs -0 -I % mv % ~/Downloads/Word/

echo "Task Finished, removing lock file now at `date +%Y-%m-%d::%H:%M:%s`"
rm $LOCKFILE

답변1

대상에 여러 확장이 있는 경우 find지시문에 더 많은 논리를 추가할 수 있습니다.

find ~/Downloads -maxdepth 1 \( -name "*.tar.gz" -o -name "*.zip" \) -print0 | xargs -0 -I % mv % ~/Downloads/archives/

그리고 xargs로 파이프할 필요가 없습니다.

find ~/Downloads -maxdepth 1 \( -name "*.tar.gz" -o -name "*.zip" \) -exec mv -t ~/Downloads/archives/ {} +

이제 그것을 가지게 되었는데 -maxdepth 1, 정말로 그것이 필요합니까 find?

shopt -s nullglob
cd ~/Downloads
mv -t archives/ *.tar.gz *.zip
mv -t Pictures/ *.jpg *.png *.tiff
# etc

이 방법을 사용하면 이동할 파일이 없으면 일부 오류가 발생합니다. 다음과 같이 이 문제를 해결할 수 있습니다.

shopt -s nullglob
movefiles() {
    local dest=$1
    shift
    if (( $# > 0 )); then
        mkdir -p "$dest"
        mv -t "$dest" "$@"
    fi
}
cd ~/Downloads
movefiles PDF/      *.pdf
movefiles OPM/      *.opm
movefiles YML/      *.yml
movefiles CSS/      *.css
movefiles archives/ *.zip *.tar.gz
movefiles Pictures/ *.jpg *.png *.tiff
movefiles Perl/     *.pm
movefiles Excel/    *.xls*
movefiles Word/     *.doc*

노트:

  • nullglob이 없으면 패턴과 일치하는 파일이 없으면 함수는 패턴을 문자열로 수신합니다.
    • 예를 들어 pdf 파일이 없으면 쉘이 실행됩니다.movefiles PDF/ "*.pdf"
  • nullglob의 경우 일치하는 항목이 없으면 쉘은 명령에서 패턴을 제거합니다.movefiles PDF/
  • 이것이 바로 인수 수를 확인하는 이유입니다. 일치하는 파일이 없으면 $#는 시프트 후 0이므로 아무것도 이동할 수 없습니다.

답변2

취향의 문제로, 나는 코드 복제를 싫어하고 데이터를 코드와 별도로 유지하는 경향이 있으므로 확장<->대상 관계를 정의하는 방법이 있습니다(여기서는 키 인덱스 배열에 있지만 구성 파일일 수도 있고 구성 파일일 수도 있음). sqlite 데이터베이스):

#! /bin/bash

declare -A destinations

destinations["jpg"]="images"
destinations["mp4"]="videos"
destinations["mov"]="videos"

shopt -s nullglob
for ext in "${!destinations[@]}"
do
    files=(*.$ext)
    [[ 0 -eq ${#files[*]} ]] && continue
    mv -t ${destinations[$ext]} "${files[@]}"
done

이것은 목적을 위한 것입니다 bash. 스크립트의 shebang은 sh때때로 그것을 사용하고 bash때로는 다른 것을 사용합니다( Ubuntu에서는...). 따라서 코드가 완전히 사소하지 않은 한 shebang에서는 사용 dash하지 않을 것입니다 .sh

답변3

  1. findmv 명령은 단독으로 실행될 수 있습니다. 사용되는 구문은 다음과 같습니다.

    find <filter> -exec mv {} <destination>

    참고: {}파일 이름으로 대체됩니다. 때로는 이 방법으로 이스케이프해야 할 수도 있습니다.\{\}

  2. bash'isms를 허용한다면 bash 배열을 활용하고 다음과 같이 스크립트를 다시 작성할 수 있습니다.

    BASE_PATH="~/Downloads/"
    # 1st element: find filter, 2d element: destination
    EXT_TO_PATH=(
        'a=( "-iname \"*.pdf\" " "PDF" )'
        'a=( "-iname \"*.jpg\" -o -iname \"*.png\" " "Pictures" )'
    )

    for elt in "${EXT_TO_PATH[@]}" ; do
        eval $elt
        echo "Extensions: ${a[0]}"
        echo "Destination: ${a[1]}"
        find $BASE_PATH -maxdepth=1 ${a[0]} -exec mv {} ${BASE_PATH}/${a[1]}
    done

답변4

디렉터리를 따라 이동하고 일치하는 모든 항목을 차례로 각 디렉터리로 이동합니다.

for item in *
do
    [[ -d "$item" ]] && mv -f *."$item" "$item" 2>/dev/null
done

의 오류 출력은 mv삭제되므로 존재하지 않는 파일을 이동하려는 근거 없는 시도는 보고되지 않습니다.

또는 각 파일을 시도하고 해당 디렉터리로 이동할 수 있습니다.

for item in *
do
    if [[ -f "$item" ]]
    then
        ext="${item##*.}"
        [[ -d "$ext" ]] && mv -f "$item" "$ext"
    fi
done

관련 정보