我如何改进这个根据扩展名对文件进行排序的脚本?

我如何改进这个根据扩展名对文件进行排序的脚本?

我有一个小脚本,只需浏览我的下载文件夹,然后根据扩展名对文件进行排序。

我怎样才能做到这一点更干净/更好?我想简单地维护扩展名和相应目录的列表,并使用例如 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文件,shell将执行movefiles PDF/ "*.pdf"
  • 对于 nullglob,如果没有匹配项,则 shell 会从命令中删除该模式:movefiles PDF/
  • 这就是我检查参数数量的原因:如果没有文件匹配,则移位后 $# 为零,因此没有任何内容可以移动。

答案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.你的脚本的 shebangsh有时使用它,bash有时使用其他东西(dash在 Ubuntu 上......),所以除非代码完全微不足道,否则我会避免sh在 shebangs 中使用。

答案3

  1. find能够单独执行 mv 命令,使用的语法是:

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

    请注意:{}将被文件名替换,有时您需要以这种方式转义它\{\}

  2. 如果您容忍 bash'ism,您可以利用 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

相关内容