0%

hazel整理神器

hazel 是一款可以自动监控并整理文件夹的工具,其官网的介绍就是简单的一句话:Automated Organization for Your Mac。

它的使用有点类似于网络服务 IFTTT,你可以设定一个 if 条件,如果被监控的文件夹出现符合条件的项,那么对其执行 then 的操作(也可以通过邮箱的收件过滤规则来理解)。

1. hazel

hazel能做什么呢?

例如可以根据文件创建的时间,自动将文件进行颜色标记。自动的用特定软件打开某个特定文件、根据文件的类型自动转移到相应的文件夹中、自动帮你整理照片可以按照「年-月」来分类存储到相应文件夹、自动把文件夹中的内容上传到 FTP 等网络服务中等等。。

再加上hazel 支持 AppleScript、JavaScript、Automator workflow 等代码指令,令其扩展性更上一层楼,可以做到的事情也可以说只剩下想象力这道门槛了。

1.1 界面

左边是监控的文件夹,中间是规则列表,右边是规则的设置。

image-20220505101952417

此时设置的是文件添加时间在最后匹配时间之前(新文件添加后暂未被匹配,所以一定是早于匹配时间),就是新增加文件设置蓝色标签。

1.2 条件的嵌套使用

图中使用了嵌套条件,具体的操作是鼠标长按右侧加号(也可按住 Option 后点击),即可增加嵌套条件组。

可以看到下图有 3个if,像写代码一样。

image-20220505102832037

1.3 查看log

在做所有操作的时候,可以点击 Help->View Logs 看执行步骤。

2. 场景使用

2.1 选择标签来下发指令

添加Tags方式一定要使用快捷键才能完成,我个人把加Tags的快捷设置为:command+e

关于给标签加快捷键的方法,请参考:https://medium.com/innovation-design/how-to-add-a-shortcut-for-finder-tags-on-macos-mojave-65d1502ffd98

image-20220505110632784

通过选择标签下发指令还有个最大优势是:同时操作多个文件。一次性为多个不同类型文件下达指令后,所有文件便能自己找到方向各归其所。

2.2 标签规则

image-20220505114307967

如上图,我们把触发条件设置成只要Tags里包含Dropbox 便会触发下面的执行步骤。

  1. 系统会run shell script,这个脚本的作用使把文件归类到它的“大本营”。
  2. 移除 Tag Dropbox ,目的是避免Hazel条件二次触发。
  3. 规则文件,把_root 换成自己的文件夹。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
_root=~/Dropbox/3_SORT/
_sortIntoSubfolderByKind=1

smartMv(){
if [ -z "$1" ]
then
echo "No argument supplied"
exit
fi


# Variable definitions start
file=$1 # file path
_root=$2 # root directory
sortByKind=$3 # if needed sort by kind in Folders
fileName=$(basename -- "$file")
pwd=$(pwd)
# Variable definitions ended

# function defined
mv_no_override() {
local dir file ext base num
if [ -d "$2" ]; then
dir=$2
file=$(basename "$1")
else
dir=$(dirname "$2")
file=$(basename "$2")
fi
ext="$(sed -r 's/.+(\..+)|.*/\1/' <<<"$file")"
base="$(sed -r 's/(.+)\..+|(.*)/\1\2/' <<<"$file")"
while [ -e "$dir/$base$num$ext" ]; do
(( num++ ))
done
mv "$1" "$dir/$base$num$ext"
echo "move file $pwd/$1 ->" "$dir/$base$num$ext" "success"
}


moveTO(){
if [ ! -d "$2" ];then # if folder exist
mkdir -p "$2"
fi
mv_no_override "$1" "$2/${3}"

}
mdlsFormat(){ # format result for `mdls`
echo `mdls -name $1 $2 | cut -d "=" -f 2 | sed 's/\"//g' | sed -e 's/^[[:space:]]*//'`
}
mdlsFormatUTI(){
echo `mdls -name $1 $2 | cut -d "=" -f 2 | sed 's/,//' | sed 's/)//' | sed 's/(//'`
}
# function defined ended


# Classification according to utiTree start
utiTree=(`mdlsFormatUTI kMDItemContentTypeTree $file`)
for uti in $utiTree
do
_uti=`echo $uti | sed 's/\"//g'`

case $_uti in
public.source-code)
_basePath=${_root}Source-code/
break
;;
public.composite-content)
_basePath=${_root}Documents/
break
;;
public.presentation)
_basePath=${_root}Presentations/
break
;;
public.disk-image)
_basePath=${_root}Disk-images/
break
;;
public.archive)
_basePath=${_root}Archives/
break
;;
public.movie)
_basePath=${_root}Videos/
break
;;
public.audio)
_basePath=${_root}Audios/
break
;;
public.image)
_basePath=${_root}Pictures/
break
;;
public.plain-text)
_basePath=${_root}Documents/
break
;;
public.folder)
_basePath=${_root}Folders/
break
;;
*)
echo other "$_uti"
_basePath=${_root}Others/
;;
esac
done
# Classification according to utiTree ended

# fix basePath by extension
extension="${fileName##*.}"
echo extension "$extension"
case $extension in
docx|xlsx)
_basePath=${_root}Documents/
;;
pptx|ppt|key)
_basePath=${_root}Presentations/
;;
archiver|7z|rar|gz)
_basePath=${_root}Archives/
;;
esac

# rename start
kMDItemContentCreationDate=$(mdlsFormat kMDItemContentCreationDate "$file")

regexp="(^[0-9]{4}\-[0-9]{2}\-)" # check file name
if [[ $fileName =~ $regexp ]]
then
echo "file name has date, don't rename"
else
creationDatesFormat=$(date -j -f " %Y-%m-%d %H:%M:%S %z" "$kMDItemContentCreationDate" +%Y-%m-)
fileName=${creationDatesFormat}$fileName # rename file by adding creationTime
fi
# rename end

# mv to folder start
if [[ $sortByKind == 1 ]]
then
kind=$(mdlsFormat kMDItemKind "$file")
destination=${_basePath}${kind}
moveTO "$file" "${destination}" "${fileName}" # sort to finder by kind
else
moveTO "$file" "$_basePath" "${fileName}" # move to basePath
fi

# mv to folder end

}

smartMv "$1" "$_root" $_sortIntoSubfolderByKind

解释:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 获取类型
mdls -name kMDItemContentTypeTree ~/test.txt | cut -d "=" -f 2 | sed 's/,//' | sed 's/)//' | sed 's/(//'
"public.item"
"public.text"
"public.data"
"public.content"
"public.plain-text"


# 获取日期
mdls -name kMDItemContentCreationDate ~/test.txt | cut -d "=" -f 2 | sed 's/\"//g' | sed -e 's/^[[:space:]]*//'
2021-11-09 15:53:04 +0000

# 获取类型
mdls -name kMDItemKind ~/test.txt | cut -d "=" -f 2 | sed 's/\"//g' | sed -e 's/^[[:space:]]*//'
纯文本文稿

# 获取文件名
mdls -name kMDItemFSName ~/test.txt | cut -d "=" -f 2 | sed 's/\"//g' | sed -e 's/^[[:space:]]*//'
test.txt

3. 实用操作

3.1 递归进入文件夹执行操作

1 2

3.2 自动日期归类到子文件夹

文件夹顺序:扩展 + 年 + 月

注意,两个date modified 修改为特定的年或月。中间加个右三角符号,是子文件夹的意思。

3

4. 参考资料

给作者打赏,可以加首页微信,咨询作者相关问题!