mfd 0.2.0 → 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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5c7b34e31f4606e0a0fa10a4f18d38762b33b38167f0bec54ef4aa5ea8243295
4
- data.tar.gz: d0d5613a4a4b771de2d75fbe6e6c1149661b20b31a2b2398bbbcbfd63e0a4553
3
+ metadata.gz: bc99323cbbaf40588d62f0e6a64558f1fd8b440c655fbfc7bfded97da6b03264
4
+ data.tar.gz: 0b9c5b2023c48dff023c07e3474d0b2d6f997c2de61bcc63b02b7d91a9f3617f
5
5
  SHA512:
6
- metadata.gz: d8ea175b49e34dabe032ddf79284d9b7390455fa7b527113d3cf0add59635a603718aa2fee59e5b23e3770f7fd4ea0affe1009d89e041cb88d981a31f43380b3
7
- data.tar.gz: 54faaac4ca6d86143b4a11162e847de6251043f14d62f63b4487a7be4b1abdc2a53b9c1dd26497edf03730b17e56992317ad8d48b42c4a72654e93110c0b10f2
6
+ metadata.gz: 27e7b9975939f86b5f6588f48ebe2366449286439f638e655896a2b612c374db50e0e9c473501febb277709ed0be01d23795bb9b21c468018d483f50a1625af8
7
+ data.tar.gz: 4b3ca6a54d81e5ebb602522e2aa836bca9dd160660208b1dbf1d953c01e2ade068623fdad7b639514aae47d66708a924d4560e125d641d508ba0e1e3df6bbf0f
data/Gemfile.lock CHANGED
@@ -1,45 +1,45 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- mfd (0.1.0)
4
+ mfd (0.2.2)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
8
8
  specs:
9
- byebug (11.0.1)
10
- coderay (1.1.2)
11
- diff-lcs (1.3)
12
- method_source (0.9.2)
13
- pry (0.12.2)
14
- coderay (~> 1.1.0)
15
- method_source (~> 0.9.0)
16
- pry-byebug (3.7.0)
9
+ byebug (11.1.3)
10
+ coderay (1.1.3)
11
+ diff-lcs (1.5.0)
12
+ method_source (1.0.0)
13
+ pry (0.14.2)
14
+ coderay (~> 1.1)
15
+ method_source (~> 1.0)
16
+ pry-byebug (3.10.1)
17
17
  byebug (~> 11.0)
18
- pry (~> 0.10)
19
- rake (10.5.0)
20
- rspec (3.8.0)
21
- rspec-core (~> 3.8.0)
22
- rspec-expectations (~> 3.8.0)
23
- rspec-mocks (~> 3.8.0)
24
- rspec-core (3.8.0)
25
- rspec-support (~> 3.8.0)
26
- rspec-expectations (3.8.3)
18
+ pry (>= 0.13, < 0.15)
19
+ rake (13.4.2)
20
+ rspec (3.12.0)
21
+ rspec-core (~> 3.12.0)
22
+ rspec-expectations (~> 3.12.0)
23
+ rspec-mocks (~> 3.12.0)
24
+ rspec-core (3.12.1)
25
+ rspec-support (~> 3.12.0)
26
+ rspec-expectations (3.12.2)
27
27
  diff-lcs (>= 1.2.0, < 2.0)
28
- rspec-support (~> 3.8.0)
29
- rspec-mocks (3.8.0)
28
+ rspec-support (~> 3.12.0)
29
+ rspec-mocks (3.12.5)
30
30
  diff-lcs (>= 1.2.0, < 2.0)
31
- rspec-support (~> 3.8.0)
32
- rspec-support (3.8.0)
31
+ rspec-support (~> 3.12.0)
32
+ rspec-support (3.12.0)
33
33
 
34
34
  PLATFORMS
35
35
  ruby
36
36
 
37
37
  DEPENDENCIES
38
- bundler (~> 1.17)
38
+ bundler (>= 1.17)
39
39
  mfd!
40
40
  pry-byebug (>= 3.6.0)
41
- rake (~> 10.0)
42
- rspec (~> 3.0)
41
+ rake (>= 13.2.0)
42
+ rspec (>= 3.0)
43
43
 
44
44
  BUNDLED WITH
45
- 2.4.12
45
+ 4.0.18
data/README.md CHANGED
@@ -59,26 +59,30 @@ options:
59
59
 
60
60
  -q, --query query e.g. --query "关键字"
61
61
 
62
- -c, --content-type contenttype e.g. --contenttype com.omnigroup.omnigraffle.graffle
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
- ContentType 字符串不便记忆,为了方便使用,本程序将常用的
70
- 文件类型的后缀名和 kMDItemKind 字符串建立关联,可以使用本选项
71
- 来指定要搜索的文件的后缀名, 可以使用"-l"选项查看所有支持的后
72
- 缀名以及关联
74
+ 搜索文件扩展名等于指定值的文件
73
75
 
74
- -l, --list-types kMDItemKind 字符串不便记忆,为了方便使用,本程序将常用的
75
- 文件类型的后缀名和 kMDItemKind 字符串建立关联,使用本选项
76
- 看所有支持的后缀名以及关联
77
-
78
- -k, --kind kind e.g. --kind "HTML Document"
76
+ -k, --kind [kind] e.g. --kind "HTML Document"
79
77
  "-k"选项是另外一个用来指定文件类型的选项,它使用
80
78
  "kMDItemKind"属性来搜索文件. 例如, 搜索邮件: "-k 邮件信息"
81
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
82
86
 
83
87
  -b, --bigger-than size e.g. --bigger-than 100000
84
88
  搜索文件大小大于或等于给定值的文件,可以使用 t, g, m, k 等单位,
@@ -108,6 +112,7 @@ options:
108
112
 
109
113
  -d, --downloadfrom url 搜索从URL下载的文件
110
114
  -n, --name name 按文件名称搜索, 自动支持模糊匹配, 无需指定通配符
115
+ -N, --name-exact name 按文件名称精确匹配(区分大小写,需包含扩展名)
111
116
  -0, --null Prints an ASCII NUL character after each result path.
112
117
  This is useful when used in conjunction with
113
118
  xargs -0.
data/lib/mfd/cli.rb CHANGED
@@ -4,12 +4,16 @@ require 'mfd/asset'
4
4
  module Mfd
5
5
  class Cli
6
6
 
7
+ NOW = Time.now
8
+
7
9
  def initialize
8
10
  @predicates = ['(true)']
9
11
  @print_command = false
10
12
  @ascii_null = false
11
13
  @live = false
12
14
  @count = false
15
+ @list_kinds = false
16
+ @list_content_types = false
13
17
 
14
18
  @opt = OptionParser.new
15
19
  declare_options!
@@ -23,6 +27,11 @@ module Mfd
23
27
 
24
28
  @opt.parse!(args)
25
29
 
30
+ if @list_kinds
31
+ print_kind_map
32
+ return unless @list_content_types
33
+ end
34
+
26
35
  if args.size == 1
27
36
  dir = args.first
28
37
  dir = File.expand_path dir unless dir =~ /^\//
@@ -33,6 +42,11 @@ module Mfd
33
42
 
34
43
  query_string = @predicates.join(' && ')
35
44
 
45
+ if @list_content_types
46
+ list_content_types(scope, query_string)
47
+ return
48
+ end
49
+
36
50
  cmd = "mdfind #{scope} \
37
51
  #{@ascii_null ? '-0' : ''} \
38
52
  #{@live ? '-live' : ''} \
@@ -113,21 +127,27 @@ module Mfd
113
127
  @predicates << 'kMDItemTextContent == "%s"cdw' % escape(query)
114
128
  end
115
129
 
116
- @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
117
131
  #{' ' * 35}搜索 \"kMDItemContentType\" 属性等于指定值的文件,可以不
118
132
  #{' ' * 35}指定完整的 Content Type,例如 Ruby 脚本的 Content Type
119
133
  #{' ' * 35}为 \"public.ruby-script\",但是可以使用下面的命令所有所有
120
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
121
140
  ") do |contenttype|
122
- @predicates << 'kMDItemContentType == "%s"cdw' % (contenttype.start_with?('@') ?
123
- prop_value_from_file(contenttype, 'kMDItemContentType') : escape(contenttype))
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
124
147
  end
125
148
 
126
149
  @opt.on('-e file-ext-name', '--type file-ext-name', ".e.g -e 'mp3'
127
- #{' ' * 35}ContentType 字符串不便记忆,为了方便使用,本程序将常用的
128
- #{' ' * 35}文件类型的后缀名和 kMDItemKind 字符串建立关联,可以使用本选项
129
- #{' ' * 35}来指定要搜索的文件的后缀名, 可以使用\"-l\"选项查看所有支持的后
130
- #{' ' * 35}缀名以及关联
150
+ #{' ' * 35}搜索文件扩展名等于指定值的文件
131
151
  ") do |type|
132
152
  if Mfd::Asset::KIND_MAP[type]
133
153
  @predicates << '(kMDItemFSName == "*.%s" || kMDItemKind == "%s"cdw)' % [type, escape(Mfd::Asset::KIND_MAP[type])]
@@ -136,13 +156,26 @@ module Mfd
136
156
  end
137
157
  end
138
158
 
139
- @opt.on('-k kind', '--kind kind', "e.g. --kind \"HTML Document\"
159
+ @opt.on('-k [kind]', '--kind [kind]', "e.g. --kind \"HTML Document\"
140
160
  #{' ' * 35}\"-k\"选项是另外一个用来指定文件类型的选项,它使用
141
161
  #{' ' * 35}\"kMDItemKind\"属性来搜索文件. 例如, 搜索邮件: \"-k 邮件信息\"
142
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),不扫描磁盘。
143
167
  ") do |kind|
144
- @predicates << 'kMDItemKind == "*%s*"cdw' % (kind.start_with?('@') ?
145
- prop_value_from_file(kind, 'kMDItemKind') : escape(kind))
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
146
179
  end
147
180
 
148
181
  @opt.on('-b size', '--bigger-than size', "e.g. --bigger-than 100000
@@ -207,6 +240,10 @@ module Mfd
207
240
  @predicates << 'kMDItemFSName == "%s"cdw' % escape(name)
208
241
  end
209
242
 
243
+ @opt.on('-N name', '--name-exact name', '按文件名称精确匹配(区分大小写,需包含扩展名)') do |name|
244
+ @predicates << 'kMDItemFSName == "%s"' % escape_literal(name)
245
+ end
246
+
210
247
  @opt.on('-0', '--null', "Prints an ASCII NUL character after each result path.
211
248
  #{' ' * 35}This is useful when used in conjunction with
212
249
  #{' ' * 35}xargs -0.") do
@@ -240,6 +277,77 @@ module Mfd
240
277
  str.gsub(/"/, '\\"')
241
278
  end
242
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
+
243
351
  def calculate_size(size_str)
244
352
  case size_str
245
353
  when /(\d+)t$/i
data/lib/mfd/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Mfd
2
- VERSION = "0.2.0"
2
+ VERSION = "0.2.2"
3
3
  end
data/mfd.gemspec CHANGED
@@ -24,8 +24,8 @@ Gem::Specification.new do |spec|
24
24
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
25
25
  spec.require_paths = ["lib"]
26
26
 
27
- spec.add_development_dependency "bundler", "~> 1.17"
28
- spec.add_development_dependency "rake", "~> 10.0"
29
- spec.add_development_dependency "rspec", "~> 3.0"
27
+ spec.add_development_dependency "bundler", ">= 1.17"
28
+ spec.add_development_dependency "rake", ">= 13.2.0"
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,55 +1,54 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mfd
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
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: 2023-04-18 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: bundler
15
14
  requirement: !ruby/object:Gem::Requirement
16
15
  requirements:
17
- - - "~>"
16
+ - - ">="
18
17
  - !ruby/object:Gem::Version
19
18
  version: '1.17'
20
19
  type: :development
21
20
  prerelease: false
22
21
  version_requirements: !ruby/object:Gem::Requirement
23
22
  requirements:
24
- - - "~>"
23
+ - - ">="
25
24
  - !ruby/object:Gem::Version
26
25
  version: '1.17'
27
26
  - !ruby/object:Gem::Dependency
28
27
  name: rake
29
28
  requirement: !ruby/object:Gem::Requirement
30
29
  requirements:
31
- - - "~>"
30
+ - - ">="
32
31
  - !ruby/object:Gem::Version
33
- version: '10.0'
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: '10.0'
39
+ version: 13.2.0
41
40
  - !ruby/object:Gem::Dependency
42
41
  name: rspec
43
42
  requirement: !ruby/object:Gem::Requirement
44
43
  requirements:
45
- - - "~>"
44
+ - - ">="
46
45
  - !ruby/object:Gem::Version
47
46
  version: '3.0'
48
47
  type: :development
49
48
  prerelease: false
50
49
  version_requirements: !ruby/object:Gem::Requirement
51
50
  requirements:
52
- - - "~>"
51
+ - - ">="
53
52
  - !ruby/object:Gem::Version
54
53
  version: '3.0'
55
54
  - !ruby/object:Gem::Dependency
@@ -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: 3.4.1
114
- signing_key:
111
+ rubygems_version: 4.0.3
115
112
  specification_version: 4
116
113
  summary: ''
117
114
  test_files: []