我有一个小脚本,只需浏览我的下载文件夹,然后根据扩展名对文件进行排序。
我怎样才能做到这一点更干净/更好?我想简单地维护扩展名和相应目录的列表,并使用例如 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"
- 例如,如果没有pdf文件,shell将执行
- 对于 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
find
能够单独执行 mv 命令,使用的语法是:find <filter> -exec mv {} <destination>
请注意:
{}
将被文件名替换,有时您需要以这种方式转义它\{\}
如果您容忍 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