mfd 0.2.1 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Gemfile.lock +4 -4
- data/README.md +14 -2
- data/lib/mfd/cli.rb +115 -6
- data/lib/mfd/version.rb +1 -1
- data/mfd.gemspec +1 -1
- metadata +5 -8
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: bc99323cbbaf40588d62f0e6a64558f1fd8b440c655fbfc7bfded97da6b03264
|
|
4
|
+
data.tar.gz: 0b9c5b2023c48dff023c07e3474d0b2d6f997c2de61bcc63b02b7d91a9f3617f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 27e7b9975939f86b5f6588f48ebe2366449286439f638e655896a2b612c374db50e0e9c473501febb277709ed0be01d23795bb9b21c468018d483f50a1625af8
|
|
7
|
+
data.tar.gz: 4b3ca6a54d81e5ebb602522e2aa836bca9dd160660208b1dbf1d953c01e2ade068623fdad7b639514aae47d66708a924d4560e125d641d508ba0e1e3df6bbf0f
|
data/Gemfile.lock
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
mfd (0.2.
|
|
4
|
+
mfd (0.2.2)
|
|
5
5
|
|
|
6
6
|
GEM
|
|
7
7
|
remote: https://rubygems.org/
|
|
@@ -16,7 +16,7 @@ GEM
|
|
|
16
16
|
pry-byebug (3.10.1)
|
|
17
17
|
byebug (~> 11.0)
|
|
18
18
|
pry (>= 0.13, < 0.15)
|
|
19
|
-
rake (13.
|
|
19
|
+
rake (13.4.2)
|
|
20
20
|
rspec (3.12.0)
|
|
21
21
|
rspec-core (~> 3.12.0)
|
|
22
22
|
rspec-expectations (~> 3.12.0)
|
|
@@ -38,8 +38,8 @@ DEPENDENCIES
|
|
|
38
38
|
bundler (>= 1.17)
|
|
39
39
|
mfd!
|
|
40
40
|
pry-byebug (>= 3.6.0)
|
|
41
|
-
rake (>=
|
|
41
|
+
rake (>= 13.2.0)
|
|
42
42
|
rspec (>= 3.0)
|
|
43
43
|
|
|
44
44
|
BUNDLED WITH
|
|
45
|
-
|
|
45
|
+
4.0.18
|
data/README.md
CHANGED
|
@@ -59,19 +59,30 @@ options:
|
|
|
59
59
|
|
|
60
60
|
-q, --query query e.g. --query "关键字"
|
|
61
61
|
|
|
62
|
-
-c, --content-type contenttype
|
|
62
|
+
-c, --content-type [contenttype] e.g. --contenttype com.omnigroup.omnigraffle.graffle
|
|
63
63
|
搜索 "kMDItemContentType" 属性等于指定值的文件,可以不
|
|
64
64
|
指定完整的 Content Type,例如 Ruby 脚本的 Content Type
|
|
65
65
|
为 "public.ruby-script",但是可以使用下面的命令所有所有
|
|
66
66
|
Ruby 脚本文件:mdfind4 -c ruby
|
|
67
|
+
搜索文件夹(目录)而非普通文件: -c public.folder 或 -c folder
|
|
68
|
+
也可从样例目录提取: -c @~/Documents
|
|
69
|
+
若不指定选项值(mfd -c),则列出搜索范围内所有
|
|
70
|
+
kMDItemContentType 取值,便于确认可用的 Content Type。
|
|
71
|
+
限定目录时请写成: mfd -c -- ~/Documents
|
|
67
72
|
|
|
68
73
|
-e, --type file-ext-name .e.g -e 'mp3'
|
|
69
74
|
搜索文件扩展名等于指定值的文件
|
|
70
75
|
|
|
71
|
-
-k, --kind kind
|
|
76
|
+
-k, --kind [kind] e.g. --kind "HTML Document"
|
|
72
77
|
"-k"选项是另外一个用来指定文件类型的选项,它使用
|
|
73
78
|
"kMDItemKind"属性来搜索文件. 例如, 搜索邮件: "-k 邮件信息"
|
|
74
79
|
搜索 Safari 历史记录: "-k 'Safari 历史记录项目'"
|
|
80
|
+
搜索文件夹(目录): -k 文件夹 (英文系统下为 -k Folder)
|
|
81
|
+
也可从样例目录提取: -k @~/Documents
|
|
82
|
+
若不指定选项值(mfd -k 或 mfd -l),则列出内置 KIND_MAP
|
|
83
|
+
(扩展名及其对应的 kMDItemKind),不扫描磁盘。
|
|
84
|
+
|
|
85
|
+
-l, --list-kinds 列出内置 KIND_MAP(扩展名 → kMDItemKind),等价于不带选项值的 -k
|
|
75
86
|
|
|
76
87
|
-b, --bigger-than size e.g. --bigger-than 100000
|
|
77
88
|
搜索文件大小大于或等于给定值的文件,可以使用 t, g, m, k 等单位,
|
|
@@ -101,6 +112,7 @@ options:
|
|
|
101
112
|
|
|
102
113
|
-d, --downloadfrom url 搜索从URL下载的文件
|
|
103
114
|
-n, --name name 按文件名称搜索, 自动支持模糊匹配, 无需指定通配符
|
|
115
|
+
-N, --name-exact name 按文件名称精确匹配(区分大小写,需包含扩展名)
|
|
104
116
|
-0, --null Prints an ASCII NUL character after each result path.
|
|
105
117
|
This is useful when used in conjunction with
|
|
106
118
|
xargs -0.
|
data/lib/mfd/cli.rb
CHANGED
|
@@ -12,6 +12,8 @@ module Mfd
|
|
|
12
12
|
@ascii_null = false
|
|
13
13
|
@live = false
|
|
14
14
|
@count = false
|
|
15
|
+
@list_kinds = false
|
|
16
|
+
@list_content_types = false
|
|
15
17
|
|
|
16
18
|
@opt = OptionParser.new
|
|
17
19
|
declare_options!
|
|
@@ -25,6 +27,11 @@ module Mfd
|
|
|
25
27
|
|
|
26
28
|
@opt.parse!(args)
|
|
27
29
|
|
|
30
|
+
if @list_kinds
|
|
31
|
+
print_kind_map
|
|
32
|
+
return unless @list_content_types
|
|
33
|
+
end
|
|
34
|
+
|
|
28
35
|
if args.size == 1
|
|
29
36
|
dir = args.first
|
|
30
37
|
dir = File.expand_path dir unless dir =~ /^\//
|
|
@@ -35,6 +42,11 @@ module Mfd
|
|
|
35
42
|
|
|
36
43
|
query_string = @predicates.join(' && ')
|
|
37
44
|
|
|
45
|
+
if @list_content_types
|
|
46
|
+
list_content_types(scope, query_string)
|
|
47
|
+
return
|
|
48
|
+
end
|
|
49
|
+
|
|
38
50
|
cmd = "mdfind #{scope} \
|
|
39
51
|
#{@ascii_null ? '-0' : ''} \
|
|
40
52
|
#{@live ? '-live' : ''} \
|
|
@@ -115,14 +127,23 @@ module Mfd
|
|
|
115
127
|
@predicates << 'kMDItemTextContent == "%s"cdw' % escape(query)
|
|
116
128
|
end
|
|
117
129
|
|
|
118
|
-
@opt.on('-c contenttype', '--content-type contenttype', "e.g. --contenttype com.omnigroup.omnigraffle.graffle
|
|
130
|
+
@opt.on('-c [contenttype]', '--content-type [contenttype]', "e.g. --contenttype com.omnigroup.omnigraffle.graffle
|
|
119
131
|
#{' ' * 35}搜索 \"kMDItemContentType\" 属性等于指定值的文件,可以不
|
|
120
132
|
#{' ' * 35}指定完整的 Content Type,例如 Ruby 脚本的 Content Type
|
|
121
133
|
#{' ' * 35}为 \"public.ruby-script\",但是可以使用下面的命令所有所有
|
|
122
134
|
#{' ' * 35}Ruby 脚本文件:mdfind4 -c ruby
|
|
135
|
+
#{' ' * 35}搜索文件夹(目录)而非普通文件: -c public.folder 或 -c folder
|
|
136
|
+
#{' ' * 35}也可从样例目录提取: -c @~/Documents
|
|
137
|
+
#{' ' * 35}若不指定选项值(mfd -c),则列出搜索范围内所有
|
|
138
|
+
#{' ' * 35}kMDItemContentType 取值,便于确认可用的 Content Type。
|
|
139
|
+
#{' ' * 35}限定目录时请写成: mfd -c -- ~/Documents
|
|
123
140
|
") do |contenttype|
|
|
124
|
-
|
|
125
|
-
|
|
141
|
+
if contenttype.nil?
|
|
142
|
+
@list_content_types = true
|
|
143
|
+
else
|
|
144
|
+
@predicates << 'kMDItemContentType == "%s"cdw' % (contenttype.start_with?('@') ?
|
|
145
|
+
prop_value_from_file(contenttype, 'kMDItemContentType') : escape(contenttype))
|
|
146
|
+
end
|
|
126
147
|
end
|
|
127
148
|
|
|
128
149
|
@opt.on('-e file-ext-name', '--type file-ext-name', ".e.g -e 'mp3'
|
|
@@ -135,13 +156,26 @@ module Mfd
|
|
|
135
156
|
end
|
|
136
157
|
end
|
|
137
158
|
|
|
138
|
-
@opt.on('-k kind', '--kind kind', "e.g. --kind \"HTML Document\"
|
|
159
|
+
@opt.on('-k [kind]', '--kind [kind]', "e.g. --kind \"HTML Document\"
|
|
139
160
|
#{' ' * 35}\"-k\"选项是另外一个用来指定文件类型的选项,它使用
|
|
140
161
|
#{' ' * 35}\"kMDItemKind\"属性来搜索文件. 例如, 搜索邮件: \"-k 邮件信息\"
|
|
141
162
|
#{' ' * 35}搜索 Safari 历史记录: \"-k 'Safari 历史记录项目'\"
|
|
163
|
+
#{' ' * 35}搜索文件夹(目录): -k 文件夹 (英文系统下为 -k Folder)
|
|
164
|
+
#{' ' * 35}也可从样例目录提取: -k @~/Documents
|
|
165
|
+
#{' ' * 35}若不指定选项值(mfd -k 或 mfd -l),则列出内置 KIND_MAP
|
|
166
|
+
#{' ' * 35}(扩展名及其对应的 kMDItemKind),不扫描磁盘。
|
|
142
167
|
") do |kind|
|
|
143
|
-
|
|
144
|
-
|
|
168
|
+
if kind.nil?
|
|
169
|
+
@list_kinds = true
|
|
170
|
+
else
|
|
171
|
+
@predicates << 'kMDItemKind == "*%s*"cdw' % (kind.start_with?('@') ?
|
|
172
|
+
prop_value_from_file(kind, 'kMDItemKind') : escape(kind))
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
@opt.on('-l', '--list-kinds', "列出内置 KIND_MAP(扩展名 → kMDItemKind),等价于不带选项值的 -k
|
|
177
|
+
") do
|
|
178
|
+
@list_kinds = true
|
|
145
179
|
end
|
|
146
180
|
|
|
147
181
|
@opt.on('-b size', '--bigger-than size', "e.g. --bigger-than 100000
|
|
@@ -206,6 +240,10 @@ module Mfd
|
|
|
206
240
|
@predicates << 'kMDItemFSName == "%s"cdw' % escape(name)
|
|
207
241
|
end
|
|
208
242
|
|
|
243
|
+
@opt.on('-N name', '--name-exact name', '按文件名称精确匹配(区分大小写,需包含扩展名)') do |name|
|
|
244
|
+
@predicates << 'kMDItemFSName == "%s"' % escape_literal(name)
|
|
245
|
+
end
|
|
246
|
+
|
|
209
247
|
@opt.on('-0', '--null', "Prints an ASCII NUL character after each result path.
|
|
210
248
|
#{' ' * 35}This is useful when used in conjunction with
|
|
211
249
|
#{' ' * 35}xargs -0.") do
|
|
@@ -239,6 +277,77 @@ module Mfd
|
|
|
239
277
|
str.gsub(/"/, '\\"')
|
|
240
278
|
end
|
|
241
279
|
|
|
280
|
+
def escape_literal(str)
|
|
281
|
+
str.gsub(/[\\*?"]/) { |char| '\\' + char }
|
|
282
|
+
end
|
|
283
|
+
|
|
284
|
+
def print_kind_map
|
|
285
|
+
width = Mfd::Asset::KIND_MAP.keys.map(&:size).max
|
|
286
|
+
Mfd::Asset::KIND_MAP.sort.each do |ext, kind|
|
|
287
|
+
printf "%-*s %s\n", width, ext, kind
|
|
288
|
+
end
|
|
289
|
+
end
|
|
290
|
+
|
|
291
|
+
def list_content_types(scope, query_string)
|
|
292
|
+
if scope.empty?
|
|
293
|
+
$stderr.puts '未指定搜索目录,将遍历整个 Spotlight 索引(可能非常慢)。建议加上目录,例如: mfd -c -- ~/Documents'
|
|
294
|
+
end
|
|
295
|
+
|
|
296
|
+
emit_unique_md_values('kMDItemContentType', scope, query_string)
|
|
297
|
+
end
|
|
298
|
+
|
|
299
|
+
def emit_unique_md_values(attr, scope, query_string)
|
|
300
|
+
unique = {}
|
|
301
|
+
batch = []
|
|
302
|
+
|
|
303
|
+
flush = lambda do
|
|
304
|
+
unless batch.empty?
|
|
305
|
+
md_values_for_paths(attr, batch).each do |value|
|
|
306
|
+
next if value.empty? || unique[value]
|
|
307
|
+
unique[value] = true
|
|
308
|
+
puts value
|
|
309
|
+
$stdout.flush
|
|
310
|
+
end
|
|
311
|
+
batch.clear
|
|
312
|
+
end
|
|
313
|
+
end
|
|
314
|
+
|
|
315
|
+
each_mdfind_path(scope, query_string) do |path|
|
|
316
|
+
next unless usable_fs_path?(path)
|
|
317
|
+
batch << path
|
|
318
|
+
flush.call if batch.size >= 100
|
|
319
|
+
end
|
|
320
|
+
flush.call
|
|
321
|
+
end
|
|
322
|
+
|
|
323
|
+
def md_values_for_paths(attr, paths)
|
|
324
|
+
IO.popen(['mdls', '-name', attr, '-raw', '-nullMarker', '', *paths], 'r', err: File::NULL) do |io|
|
|
325
|
+
io.read.split("\0")
|
|
326
|
+
end
|
|
327
|
+
end
|
|
328
|
+
|
|
329
|
+
def usable_fs_path?(path)
|
|
330
|
+
path.start_with?('/') && File.exist?(path)
|
|
331
|
+
end
|
|
332
|
+
|
|
333
|
+
def each_mdfind_path(scope, query_string)
|
|
334
|
+
cmd = "mdfind #{scope} -literal -0 '#{query_string}'"
|
|
335
|
+
puts cmd if @print_command
|
|
336
|
+
|
|
337
|
+
IO.popen(cmd, 'r') do |io|
|
|
338
|
+
buf = ''
|
|
339
|
+
while (chunk = io.read(65_536))
|
|
340
|
+
buf << chunk
|
|
341
|
+
while (idx = buf.index("\0"))
|
|
342
|
+
path = buf.slice!(0, idx)
|
|
343
|
+
buf.slice!(0)
|
|
344
|
+
yield path unless path.empty?
|
|
345
|
+
end
|
|
346
|
+
end
|
|
347
|
+
yield buf unless buf.empty?
|
|
348
|
+
end
|
|
349
|
+
end
|
|
350
|
+
|
|
242
351
|
def calculate_size(size_str)
|
|
243
352
|
case size_str
|
|
244
353
|
when /(\d+)t$/i
|
data/lib/mfd/version.rb
CHANGED
data/mfd.gemspec
CHANGED
|
@@ -25,7 +25,7 @@ Gem::Specification.new do |spec|
|
|
|
25
25
|
spec.require_paths = ["lib"]
|
|
26
26
|
|
|
27
27
|
spec.add_development_dependency "bundler", ">= 1.17"
|
|
28
|
-
spec.add_development_dependency "rake", ">=
|
|
28
|
+
spec.add_development_dependency "rake", ">= 13.2.0"
|
|
29
29
|
spec.add_development_dependency "rspec", ">= 3.0"
|
|
30
30
|
spec.add_development_dependency "pry-byebug", ">= 3.6.0"
|
|
31
31
|
end
|
metadata
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: mfd
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.2.
|
|
4
|
+
version: 0.2.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Liu Xiang
|
|
8
|
-
autorequire:
|
|
9
8
|
bindir: exe
|
|
10
9
|
cert_chain: []
|
|
11
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
12
11
|
dependencies:
|
|
13
12
|
- !ruby/object:Gem::Dependency
|
|
14
13
|
name: bundler
|
|
@@ -30,14 +29,14 @@ dependencies:
|
|
|
30
29
|
requirements:
|
|
31
30
|
- - ">="
|
|
32
31
|
- !ruby/object:Gem::Version
|
|
33
|
-
version:
|
|
32
|
+
version: 13.2.0
|
|
34
33
|
type: :development
|
|
35
34
|
prerelease: false
|
|
36
35
|
version_requirements: !ruby/object:Gem::Requirement
|
|
37
36
|
requirements:
|
|
38
37
|
- - ">="
|
|
39
38
|
- !ruby/object:Gem::Version
|
|
40
|
-
version:
|
|
39
|
+
version: 13.2.0
|
|
41
40
|
- !ruby/object:Gem::Dependency
|
|
42
41
|
name: rspec
|
|
43
42
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -95,7 +94,6 @@ homepage: https://github.com/lululau/mfd
|
|
|
95
94
|
licenses:
|
|
96
95
|
- MIT
|
|
97
96
|
metadata: {}
|
|
98
|
-
post_install_message:
|
|
99
97
|
rdoc_options: []
|
|
100
98
|
require_paths:
|
|
101
99
|
- lib
|
|
@@ -110,8 +108,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
110
108
|
- !ruby/object:Gem::Version
|
|
111
109
|
version: '0'
|
|
112
110
|
requirements: []
|
|
113
|
-
rubygems_version:
|
|
114
|
-
signing_key:
|
|
111
|
+
rubygems_version: 4.0.3
|
|
115
112
|
specification_version: 4
|
|
116
113
|
summary: ''
|
|
117
114
|
test_files: []
|