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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8965ee1090d0d44e11e41a7ff131010878ec484968eba9ca432489ffba3e5efe
4
- data.tar.gz: c17bedc8637b53817675c0d66596bbbe67560eb8ec7aa6f5db054fe8a4ac5851
3
+ metadata.gz: bc99323cbbaf40588d62f0e6a64558f1fd8b440c655fbfc7bfded97da6b03264
4
+ data.tar.gz: 0b9c5b2023c48dff023c07e3474d0b2d6f997c2de61bcc63b02b7d91a9f3617f
5
5
  SHA512:
6
- metadata.gz: 41ab4396391573e3f6e4d322f80a66ea85232fca850671c096fa364a7d4863b0ef844c6442775b33ca7cfc652d92f59d1eed8c912fd156792b58fd4361d39f69
7
- data.tar.gz: 5d69482c8bd7cabda89bc24551b2f1318a9b359b9115a7d62dc67925082ecd246ff7355682074b86b2c70c20f76d934ef9d0b2cc31f1985b58c15d13dd2b3630
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.1)
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.0.6)
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 (>= 10.0)
41
+ rake (>= 13.2.0)
42
42
  rspec (>= 3.0)
43
43
 
44
44
  BUNDLED WITH
45
- 2.4.12
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 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
74
  搜索文件扩展名等于指定值的文件
70
75
 
71
- -k, --kind kind e.g. --kind "HTML Document"
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
- @predicates << 'kMDItemContentType == "%s"cdw' % (contenttype.start_with?('@') ?
125
- 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
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
- @predicates << 'kMDItemKind == "*%s*"cdw' % (kind.start_with?('@') ?
144
- 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
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
@@ -1,3 +1,3 @@
1
1
  module Mfd
2
- VERSION = "0.2.1"
2
+ VERSION = "0.2.2"
3
3
  end
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", ">= 10.0"
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.1
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
@@ -30,14 +29,14 @@ dependencies:
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
@@ -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: []