dcat 1.0.0
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 +7 -0
- data/LICENSE.txt +21 -0
- data/README.ja.md +132 -0
- data/README.md +132 -0
- data/exe/dcatc +5 -0
- data/exe/dcats +5 -0
- data/exe/dcatz +25 -0
- data/lib/dcat/my_find.rb +61 -0
- data/lib/dcat/utils.rb +32 -0
- data/lib/dcat/version.rb +5 -0
- data/lib/dcat.rb +308 -0
- data/sig/dcat.rbs +4 -0
- metadata +92 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 21aa70d4fd2961f0000538bb28d339c50379aee433f35ff4663a26217a8c58e1
|
|
4
|
+
data.tar.gz: 9f66cab093be3f67b938d4bc6ea3102449c50a390ef6672a8553866baaaf8e51
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: c8af4dd1942c96e0945cca7630e9f0d2e9193a8ceb9e241ec6fa0ae282d8a27b67651567983b2949580391d1d2e838b0f257fba3c929151bf9d7e9dc4c46dc6e
|
|
7
|
+
data.tar.gz: cb88673b172b06d4ee42b4c59774e5a358ef7a8b3ec4423e4f4d86c60974125c4970005067bd1437adf7c384e4e2b883161aaa91772cbced32ce463e220eab30
|
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2023 src
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
|
13
|
+
all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|
data/README.ja.md
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
# Dcat
|
|
2
|
+
|
|
3
|
+
[English](README.md) | **日本語**
|
|
4
|
+
|
|
5
|
+
ディスクカタログを作成・検索するためのRuby gemです。ファイルリストとメタデータ(サイズ、更新日時)を含む検索可能なカタログを作成し、効率的な保存のためにgzipで圧縮できます。
|
|
6
|
+
|
|
7
|
+
## 機能
|
|
8
|
+
|
|
9
|
+
- **カタログ作成** - ディレクトリ構造からファイルメタデータ付きのカタログを作成
|
|
10
|
+
- **カタログ検索** - ripgrepを使用した効率的な検索
|
|
11
|
+
- **カタログ圧縮** - gzip圧縮による省スペース保存
|
|
12
|
+
- **クロスプラットフォーム対応** - Windows互換性
|
|
13
|
+
- **人間が読みやすいファイルサイズ** - 出力での可読性
|
|
14
|
+
|
|
15
|
+
## インストール
|
|
16
|
+
|
|
17
|
+
gemをインストールするには:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
$ gem install dcat
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
または、Gemfileに追加:
|
|
24
|
+
|
|
25
|
+
```ruby
|
|
26
|
+
gem 'dcat'
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## 使用方法
|
|
30
|
+
|
|
31
|
+
### 設定
|
|
32
|
+
|
|
33
|
+
`~/.catalogrc`に設定ファイルを作成:
|
|
34
|
+
|
|
35
|
+
```yaml
|
|
36
|
+
general:
|
|
37
|
+
catalogdir: ./data
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### コマンド
|
|
41
|
+
|
|
42
|
+
#### カタログの作成
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
$ dcatc [ディレクトリパス]
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
指定されたディレクトリ(未指定の場合は現在のディレクトリ)からカタログファイルを作成します。
|
|
49
|
+
|
|
50
|
+
#### カタログの検索
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
$ dcats [検索パターン]
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
パターンに一致するファイルをカタログファイルから検索します。
|
|
57
|
+
|
|
58
|
+
#### 圧縮カタログの読み込み
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
$ dcatz [カタログファイル.gz]
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
圧縮されたカタログファイルを読み込むための`zcat`の代替コマンド。特にWindowsで有用です。
|
|
65
|
+
|
|
66
|
+
### カタログファイル形式
|
|
67
|
+
|
|
68
|
+
カタログファイルはタブ区切り値として保存されます:
|
|
69
|
+
- 列1: フルファイルパス
|
|
70
|
+
- 列2: 更新日時(YYYY-MM-DD形式)
|
|
71
|
+
- 列3: ファイルサイズ(バイト)
|
|
72
|
+
- 列4: KB単位に切り上げたファイルサイズ(例: `1 KB`、`120,564 KB`)
|
|
73
|
+
|
|
74
|
+
ファイルは効率的な保存のためにgzip(`.gz`拡張子)で圧縮できます。
|
|
75
|
+
|
|
76
|
+
### 使用例
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
# 現在のディレクトリのカタログを作成
|
|
80
|
+
$ dcatc
|
|
81
|
+
|
|
82
|
+
# 特定のディレクトリのカタログを作成
|
|
83
|
+
$ dcatc /path/to/directory
|
|
84
|
+
|
|
85
|
+
# "document"を含むファイルを検索
|
|
86
|
+
$ dcats document
|
|
87
|
+
|
|
88
|
+
# 特定の拡張子のファイルを検索
|
|
89
|
+
$ dcats "*.pdf"
|
|
90
|
+
|
|
91
|
+
# 圧縮されたカタログを読み込み
|
|
92
|
+
$ dcatz catalog.gz
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## 開発
|
|
96
|
+
|
|
97
|
+
リポジトリをチェックアウト後、`bin/setup`を実行して依存関係をインストールします。その後、`rake test`でテストを実行できます。
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
# 依存関係のインストール
|
|
101
|
+
$ bundle install
|
|
102
|
+
|
|
103
|
+
# テストの実行
|
|
104
|
+
$ rake test
|
|
105
|
+
|
|
106
|
+
# gemのビルド
|
|
107
|
+
$ bundle exec rake build
|
|
108
|
+
|
|
109
|
+
# ローカルインストール
|
|
110
|
+
$ bundle exec rake install
|
|
111
|
+
|
|
112
|
+
# 対話型コンソール
|
|
113
|
+
$ bin/console
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## 要件
|
|
117
|
+
|
|
118
|
+
- Ruby >= 2.6.0
|
|
119
|
+
- 検索機能用の`ripgrep`(rg)コマンド
|
|
120
|
+
|
|
121
|
+
## 依存関係
|
|
122
|
+
|
|
123
|
+
- `sys-filesystem` - ディスク容量情報の取得
|
|
124
|
+
- `zlib` - gzip圧縮/展開
|
|
125
|
+
|
|
126
|
+
## 貢献
|
|
127
|
+
|
|
128
|
+
バグレポートとプルリクエストは、GitHub https://github.com/src256/dcat で歓迎します。
|
|
129
|
+
|
|
130
|
+
## ライセンス
|
|
131
|
+
|
|
132
|
+
このgemは[MIT License](https://opensource.org/licenses/MIT)の条件の下でオープンソースとして利用できます。
|
data/README.md
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
# Dcat
|
|
2
|
+
|
|
3
|
+
**English** | [日本語](README.ja.md)
|
|
4
|
+
|
|
5
|
+
A Ruby gem for creating and searching disk catalogs. Generate searchable catalogs containing file lists and metadata (size, modification date) with efficient gzip compression for storage.
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- **Create catalogs** from directory structures with file metadata
|
|
10
|
+
- **Search catalogs** efficiently using ripgrep
|
|
11
|
+
- **Compress catalogs** with gzip for space-efficient storage
|
|
12
|
+
- **Cross-platform support** with Windows compatibility
|
|
13
|
+
- **Human-readable file sizes** in output
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
Install the gem by executing:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
$ gem install dcat
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Or add to your Gemfile:
|
|
24
|
+
|
|
25
|
+
```ruby
|
|
26
|
+
gem 'dcat'
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Usage
|
|
30
|
+
|
|
31
|
+
### Configuration
|
|
32
|
+
|
|
33
|
+
Create a configuration file at `~/.catalogrc`:
|
|
34
|
+
|
|
35
|
+
```yaml
|
|
36
|
+
general:
|
|
37
|
+
catalogdir: ./data
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### Commands
|
|
41
|
+
|
|
42
|
+
#### Create a catalog
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
$ dcatc [directory_path]
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Creates a catalog file from the specified directory (or current directory if not specified).
|
|
49
|
+
|
|
50
|
+
#### Search catalogs
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
$ dcats [search_pattern]
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Searches through catalog files for files matching the pattern.
|
|
57
|
+
|
|
58
|
+
#### Read compressed catalogs
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
$ dcatz [catalog_file.gz]
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Alternative to `zcat` for reading compressed catalog files, especially useful on Windows.
|
|
65
|
+
|
|
66
|
+
### Catalog File Format
|
|
67
|
+
|
|
68
|
+
Catalog files are stored as tab-separated values:
|
|
69
|
+
- Column 1: Full file path
|
|
70
|
+
- Column 2: Modification date (YYYY-MM-DD format)
|
|
71
|
+
- Column 3: File size (bytes)
|
|
72
|
+
- Column 4: File size rounded up to KB (e.g. `1 KB`, `120,564 KB`)
|
|
73
|
+
|
|
74
|
+
Files can be compressed with gzip (`.gz` extension) for efficient storage.
|
|
75
|
+
|
|
76
|
+
### Examples
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
# Create a catalog of the current directory
|
|
80
|
+
$ dcatc
|
|
81
|
+
|
|
82
|
+
# Create a catalog of a specific directory
|
|
83
|
+
$ dcatc /path/to/directory
|
|
84
|
+
|
|
85
|
+
# Search for files containing "document"
|
|
86
|
+
$ dcats document
|
|
87
|
+
|
|
88
|
+
# Search for files with specific extension
|
|
89
|
+
$ dcats "*.pdf"
|
|
90
|
+
|
|
91
|
+
# Read a compressed catalog
|
|
92
|
+
$ dcatz catalog.gz
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## Development
|
|
96
|
+
|
|
97
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests.
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
# Install dependencies
|
|
101
|
+
$ bundle install
|
|
102
|
+
|
|
103
|
+
# Run tests
|
|
104
|
+
$ rake test
|
|
105
|
+
|
|
106
|
+
# Build the gem
|
|
107
|
+
$ bundle exec rake build
|
|
108
|
+
|
|
109
|
+
# Install locally
|
|
110
|
+
$ bundle exec rake install
|
|
111
|
+
|
|
112
|
+
# Interactive console
|
|
113
|
+
$ bin/console
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## Requirements
|
|
117
|
+
|
|
118
|
+
- Ruby >= 2.6.0
|
|
119
|
+
- `ripgrep` (rg) command for searching functionality
|
|
120
|
+
|
|
121
|
+
## Dependencies
|
|
122
|
+
|
|
123
|
+
- `sys-filesystem` - For disk space information
|
|
124
|
+
- `zlib` - For gzip compression/decompression
|
|
125
|
+
|
|
126
|
+
## Contributing
|
|
127
|
+
|
|
128
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/src256/dcat.
|
|
129
|
+
|
|
130
|
+
## License
|
|
131
|
+
|
|
132
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/exe/dcatc
ADDED
data/exe/dcats
ADDED
data/exe/dcatz
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
require 'zlib'
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def zcat(filename)
|
|
7
|
+
return unless filename
|
|
8
|
+
return unless FileTest.file?(filename)
|
|
9
|
+
if filename =~ /\.gz$/
|
|
10
|
+
Zlib::GzipReader.open(filename) do |gz|
|
|
11
|
+
print gz.read
|
|
12
|
+
end
|
|
13
|
+
return
|
|
14
|
+
end
|
|
15
|
+
File.open(filename, 'rb') do |f|
|
|
16
|
+
while (line = f.gets)
|
|
17
|
+
print line
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
filename = ARGV[0]
|
|
23
|
+
zcat(filename)
|
|
24
|
+
|
|
25
|
+
|
data/lib/dcat/my_find.rb
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
module MyFind
|
|
2
|
+
|
|
3
|
+
#
|
|
4
|
+
# Calls the associated block with the name of every file and directory listed
|
|
5
|
+
# as arguments, then recursively on their subdirectories, and so on.
|
|
6
|
+
#
|
|
7
|
+
# Returns an enumerator if no block is given.
|
|
8
|
+
#
|
|
9
|
+
# See the +Find+ module documentation for an example.
|
|
10
|
+
#
|
|
11
|
+
def find(*paths, ignore_error: true) # :yield: path
|
|
12
|
+
block_given? or return enum_for(__method__, *paths, ignore_error: ignore_error)
|
|
13
|
+
|
|
14
|
+
fs_encoding = Encoding.find("filesystem")
|
|
15
|
+
|
|
16
|
+
paths.collect!{|d| raise Errno::ENOENT, d unless File.exist?(d); d.dup}.each do |path|
|
|
17
|
+
path = path.to_path if path.respond_to? :to_path
|
|
18
|
+
enc = path.encoding == Encoding::US_ASCII ? fs_encoding : path.encoding
|
|
19
|
+
ps = [path]
|
|
20
|
+
while file = ps.shift
|
|
21
|
+
catch(:prune) do
|
|
22
|
+
yield file.dup
|
|
23
|
+
begin
|
|
24
|
+
s = File.lstat(file)
|
|
25
|
+
rescue Errno::ENOENT, Errno::EACCES, Errno::ENOTDIR, Errno::ELOOP, Errno::ENAMETOOLONG, Errno::EPERM
|
|
26
|
+
raise unless ignore_error
|
|
27
|
+
next
|
|
28
|
+
end
|
|
29
|
+
if s.directory? then
|
|
30
|
+
begin
|
|
31
|
+
fs = Dir.children(file, encoding: enc)
|
|
32
|
+
rescue Errno::ENOENT, Errno::EACCES, Errno::ENOTDIR, Errno::ELOOP, Errno::ENAMETOOLONG, Errno::EPERM
|
|
33
|
+
raise unless ignore_error
|
|
34
|
+
next
|
|
35
|
+
end
|
|
36
|
+
fs.sort!
|
|
37
|
+
fs.reverse_each {|f|
|
|
38
|
+
f = File.join(file, f)
|
|
39
|
+
ps.unshift f
|
|
40
|
+
}
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
nil
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
#
|
|
49
|
+
# Skips the current file or directory, restarting the loop with the next
|
|
50
|
+
# entry. If the current file is a directory, that directory will not be
|
|
51
|
+
# recursively entered. Meaningful only within the block associated with
|
|
52
|
+
# Find::find.
|
|
53
|
+
#
|
|
54
|
+
# See the +Find+ module documentation for an example.
|
|
55
|
+
#
|
|
56
|
+
def prune
|
|
57
|
+
throw :prune
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
module_function :find, :prune
|
|
61
|
+
end
|
data/lib/dcat/utils.rb
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
module Dcat
|
|
2
|
+
class Utils
|
|
3
|
+
def self.number_to_human_size(size)
|
|
4
|
+
kb = 1024
|
|
5
|
+
mb = 1024**2
|
|
6
|
+
gb = 1024**3
|
|
7
|
+
tb = 1024**4
|
|
8
|
+
case
|
|
9
|
+
when size < kb
|
|
10
|
+
'%d Bytes' % size
|
|
11
|
+
when size < mb
|
|
12
|
+
'%.1f KB' % (size.to_f / kb)
|
|
13
|
+
when size < gb
|
|
14
|
+
'%.1f MB' % (size.to_f / mb)
|
|
15
|
+
when size < tb
|
|
16
|
+
'%.1f GB' % (size.to_f / gb)
|
|
17
|
+
else
|
|
18
|
+
'%.1f TB' % (size.to_f / tb)
|
|
19
|
+
end.sub('.0', '')
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def self.windows?
|
|
23
|
+
RUBY_PLATFORM =~ /mswin|mingw|bccwin|cygwin/
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def self.number_to_kb(bytes)
|
|
27
|
+
kb = (bytes.to_f / 1024).ceil
|
|
28
|
+
kb.to_s.reverse.gsub(/(\d{3})(?=\d)/, '\\1,').reverse + " KB"
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
data/lib/dcat/version.rb
ADDED
data/lib/dcat.rb
ADDED
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "dcat/version"
|
|
4
|
+
require "dcat/my_find"
|
|
5
|
+
require "dcat/utils"
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
require 'optparse'
|
|
9
|
+
require 'fileutils'
|
|
10
|
+
require 'yaml'
|
|
11
|
+
require 'sys/filesystem'
|
|
12
|
+
require 'zlib'
|
|
13
|
+
|
|
14
|
+
module Dcat
|
|
15
|
+
DEF_CONFIG = File.expand_path("~/.catalogrc")
|
|
16
|
+
SEARCH_CMD = "rg"
|
|
17
|
+
|
|
18
|
+
class Error < StandardError; end
|
|
19
|
+
|
|
20
|
+
class Config
|
|
21
|
+
CATALOGDIR = File.expand_path("~/.catalog")
|
|
22
|
+
|
|
23
|
+
def self.create_from_yaml(yaml)
|
|
24
|
+
config = Config.new
|
|
25
|
+
catalogdir = config_value(yaml, "general", "catalogdir", true)
|
|
26
|
+
if catalogdir
|
|
27
|
+
catalogdir = File.expand_path(catalogdir)
|
|
28
|
+
end
|
|
29
|
+
config.catalogdir = catalogdir
|
|
30
|
+
|
|
31
|
+
## puts config.catalogdir
|
|
32
|
+
config.init_default
|
|
33
|
+
if config.catalogdir.nil? || !FileTest.directory?(config.catalogdir)
|
|
34
|
+
raise RuntimeError, "catalogdir is not exist: #{config.catalogdir}"
|
|
35
|
+
end
|
|
36
|
+
config
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def self.config_value(yaml, section, key, require)
|
|
40
|
+
return nil unless yaml
|
|
41
|
+
return nil unless yaml[section]
|
|
42
|
+
value = yaml[section][key]
|
|
43
|
+
if require && (value.nil? || value.empty?)
|
|
44
|
+
raise RuntimeError, "#{section}:#{key}: is empty"
|
|
45
|
+
end
|
|
46
|
+
value
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def init_default
|
|
50
|
+
@catalogdir ||= CATALOGDIR
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
attr_accessor :catalogdir
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
class Option
|
|
57
|
+
def initialize(opts, argv, config)
|
|
58
|
+
@opts = opts
|
|
59
|
+
@argv = argv
|
|
60
|
+
@config = config
|
|
61
|
+
@debug = @opts[:d]
|
|
62
|
+
@list = false
|
|
63
|
+
if @opts[:l]
|
|
64
|
+
@list = true
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
attr_reader :debug, :list
|
|
68
|
+
def catalogdir
|
|
69
|
+
@config.catalogdir
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
class CreateOption < Option
|
|
74
|
+
def initialize(opts, argv, config)
|
|
75
|
+
super
|
|
76
|
+
@dir = nil
|
|
77
|
+
if argv.size > 0 && FileTest.directory?(argv[0])
|
|
78
|
+
@dir = argv[0]
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
@debug = @opts[:d]
|
|
82
|
+
@compress = @opts[:z]
|
|
83
|
+
@output = @opts[:o]
|
|
84
|
+
if @output
|
|
85
|
+
@output_files = File.join(catalogdir, @output + '.files')
|
|
86
|
+
@output_info = File.join(catalogdir, @output + '.info')
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
attr_reader :dir, :search, :debug, :output, :output_files, :output_info, :compress, :debug
|
|
90
|
+
|
|
91
|
+
# def output_files
|
|
92
|
+
# if compress
|
|
93
|
+
# @output_files + '.gz'
|
|
94
|
+
# else
|
|
95
|
+
# @output_files
|
|
96
|
+
# end
|
|
97
|
+
# end
|
|
98
|
+
|
|
99
|
+
def compress_output_files
|
|
100
|
+
@output_files + '.gz'
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def real_output_files
|
|
104
|
+
if compress
|
|
105
|
+
compress_output_files
|
|
106
|
+
else
|
|
107
|
+
@output_files
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
class SearchOption < Option
|
|
113
|
+
def initialize(opts, argv, config)
|
|
114
|
+
super
|
|
115
|
+
@search = nil
|
|
116
|
+
if argv.size > 0
|
|
117
|
+
@search = argv[0]
|
|
118
|
+
end
|
|
119
|
+
raise "SEARCH is not specified" unless @search
|
|
120
|
+
end
|
|
121
|
+
attr_reader :search
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
class Command
|
|
125
|
+
def self.load_yaml(config_file)
|
|
126
|
+
config_file ||= DEF_CONFIG
|
|
127
|
+
config_file = File.expand_path(config_file)
|
|
128
|
+
puts "Config: #{config_file}"
|
|
129
|
+
yaml = nil
|
|
130
|
+
if FileTest.file?(config_file)
|
|
131
|
+
yaml = YAML.load_file(config_file)
|
|
132
|
+
end
|
|
133
|
+
Config.create_from_yaml(yaml)
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def list
|
|
137
|
+
puts "List catalog in #{@option.catalogdir}"
|
|
138
|
+
Dir.glob(File.join(@option.catalogdir, "*.info")).each do |path|
|
|
139
|
+
puts File.basename(path)
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
class CreateCommand < Command
|
|
145
|
+
def self.run(argv)
|
|
146
|
+
STDOUT.sync = true
|
|
147
|
+
opts = {}
|
|
148
|
+
opt = OptionParser.new(argv)
|
|
149
|
+
opt.version = VERSION
|
|
150
|
+
opt.banner = "Usage: #{opt.program_name} [options] <directory>"
|
|
151
|
+
opt.separator('Options')
|
|
152
|
+
opt.on_head('-h', '--help', 'show this message') do |v|
|
|
153
|
+
puts opt.help
|
|
154
|
+
exit
|
|
155
|
+
end
|
|
156
|
+
opt.on('-v', '--verbose', 'verbose message') {|v| opts[:v] = v}
|
|
157
|
+
opt.on('-o FILENAME', '--output=FILENAME') {|v| opts[:o] = v}
|
|
158
|
+
opt.on('--config=CONFIGFILE', 'Config file') {|v| opts[:c] = v }
|
|
159
|
+
opt.on('-d', '--debug', 'debug message') {|v| opts[:d] = v}
|
|
160
|
+
opt.on('-z', '--gzip', 'compress with gzip') {|v| opts[:z] = v}
|
|
161
|
+
opt.on('-l', '--list') {|v| opts[:l] = v}
|
|
162
|
+
opt.parse!(argv)
|
|
163
|
+
# if opts[:o].nil?
|
|
164
|
+
# puts opt.help
|
|
165
|
+
# exit
|
|
166
|
+
# end
|
|
167
|
+
config = load_yaml(opts[:c])
|
|
168
|
+
option = CreateOption.new(opts, argv, config)
|
|
169
|
+
begin
|
|
170
|
+
command = CreateCommand.new(option)
|
|
171
|
+
command.run
|
|
172
|
+
rescue => e
|
|
173
|
+
puts e.message
|
|
174
|
+
if option.debug
|
|
175
|
+
puts e.backtrace
|
|
176
|
+
end
|
|
177
|
+
puts opt.help
|
|
178
|
+
end
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
def initialize(option)
|
|
182
|
+
@option = option
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
def run
|
|
186
|
+
if @option.list
|
|
187
|
+
list
|
|
188
|
+
else
|
|
189
|
+
unless @option.dir
|
|
190
|
+
raise "directory is not specified"
|
|
191
|
+
end
|
|
192
|
+
make_files
|
|
193
|
+
make_info
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
private
|
|
198
|
+
def make_files
|
|
199
|
+
puts "Create catalog file for #{@option.dir}: #{@option.real_output_files}"
|
|
200
|
+
unless FileTest.directory?(@option.dir)
|
|
201
|
+
raise RuntimeError
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
File.unlink(@option.output_files) if FileTest.file?(@option.output_files)
|
|
205
|
+
File.unlink(@option.compress_output_files) if FileTest.file?(@option.compress_output_files)
|
|
206
|
+
|
|
207
|
+
if @option.compress
|
|
208
|
+
out = File.open(@option.compress_output_files, "w:utf-8")
|
|
209
|
+
out = Zlib::GzipWriter.open(out)
|
|
210
|
+
else
|
|
211
|
+
out = File.open(@option.output_files, "w:utf-8")
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
begin
|
|
215
|
+
MyFind.find(@option.dir, ignore_error: true) do |f|
|
|
216
|
+
if FileTest.file?(f)
|
|
217
|
+
size = File.size(f)
|
|
218
|
+
mtime = File.mtime(f).strftime("%Y-%m-%d")
|
|
219
|
+
human_readable_size = Utils.number_to_kb(size)
|
|
220
|
+
out.puts "#{f}\t#{mtime}\t#{size}\t#{human_readable_size}"
|
|
221
|
+
elsif FileTest.directory?(f)
|
|
222
|
+
STDERR.puts f
|
|
223
|
+
end
|
|
224
|
+
end
|
|
225
|
+
rescue => e
|
|
226
|
+
puts e.message
|
|
227
|
+
if option.debug
|
|
228
|
+
p e.backtrace
|
|
229
|
+
p e.class
|
|
230
|
+
end
|
|
231
|
+
end
|
|
232
|
+
out.close if out
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
def make_info
|
|
236
|
+
puts "Create info for #{@option.dir}: #{@option.output_info}"
|
|
237
|
+
unless FileTest.directory?(@option.dir)
|
|
238
|
+
raise RuntimeError
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
stat = Sys::Filesystem.stat(@option.dir)
|
|
242
|
+
free_bytes = Utils::number_to_human_size(stat.blocks_available * stat.block_size)
|
|
243
|
+
# puts number_to_human_size(free_bytes)
|
|
244
|
+
puts "total=#{stat.blocks * stat.block_size}"
|
|
245
|
+
total_bytes = Utils::number_to_human_size(stat.blocks * stat.block_size)
|
|
246
|
+
puts "total_bytes=#{total_bytes}"
|
|
247
|
+
#puts number_to_human_size(total_bytes)
|
|
248
|
+
|
|
249
|
+
out = File.open(@option.output_info, "w:utf-8")
|
|
250
|
+
created = Time.now.strftime("%Y-%m-%dT%H:%M:%S")
|
|
251
|
+
out.puts "Created: #{created}"
|
|
252
|
+
out.puts "Volume: #{@option.dir}"
|
|
253
|
+
out.puts "Total: #{total_bytes}"
|
|
254
|
+
out.puts "Free: #{free_bytes}"
|
|
255
|
+
out.close if out
|
|
256
|
+
end
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
class SearchCommand < Command
|
|
260
|
+
def self.run(argv)
|
|
261
|
+
STDOUT.sync = true
|
|
262
|
+
opts = {}
|
|
263
|
+
opt = OptionParser.new(argv)
|
|
264
|
+
opt.version = VERSION
|
|
265
|
+
opt.banner = "Usage: #{opt.program_name} [options] SEARCH <directory>"
|
|
266
|
+
opt.separator('Options')
|
|
267
|
+
opt.on_head('-h', '--help', 'show this message') do |v|
|
|
268
|
+
puts opt.help
|
|
269
|
+
exit
|
|
270
|
+
end
|
|
271
|
+
opt.on('-v', '--verbose', 'verbose message') {|v| opts[:v] = v}
|
|
272
|
+
opt.on('--config=CONFIGFILE', 'Config file') {|v| opts[:c] = v }
|
|
273
|
+
opt.on('-d', '--debug', 'debug message') {|v| opts[:d] = v}
|
|
274
|
+
opt.parse!(argv)
|
|
275
|
+
config = load_yaml(opts[:c])
|
|
276
|
+
begin
|
|
277
|
+
option = SearchOption.new(opts, argv, config)
|
|
278
|
+
command = SearchCommand.new(option)
|
|
279
|
+
command.run
|
|
280
|
+
rescue => e
|
|
281
|
+
puts e.message
|
|
282
|
+
if opts[:d]
|
|
283
|
+
puts e.backtrace
|
|
284
|
+
end
|
|
285
|
+
puts opt.help
|
|
286
|
+
end
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
def initialize(option)
|
|
290
|
+
@option = option
|
|
291
|
+
end
|
|
292
|
+
|
|
293
|
+
def run
|
|
294
|
+
if @option.list
|
|
295
|
+
list
|
|
296
|
+
else
|
|
297
|
+
search
|
|
298
|
+
end
|
|
299
|
+
end
|
|
300
|
+
|
|
301
|
+
def search
|
|
302
|
+
# rg の -z オプションが gzip 圧縮されたカタログを展開しながら検索する
|
|
303
|
+
cmd = "#{SEARCH_CMD} -z #{@option.search} #{@option.catalogdir}"
|
|
304
|
+
puts cmd if @option.debug
|
|
305
|
+
system(cmd)
|
|
306
|
+
end
|
|
307
|
+
end
|
|
308
|
+
end
|
data/sig/dcat.rbs
ADDED
metadata
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: dcat
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 1.0.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- src
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: exe
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2026-09-09 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: sys-filesystem
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - ">="
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '0'
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - ">="
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '0'
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: zlib
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - ">="
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: '0'
|
|
34
|
+
type: :runtime
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - ">="
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '0'
|
|
41
|
+
description: |
|
|
42
|
+
Dcat builds searchable catalogs of directory trees, recording each file's
|
|
43
|
+
full path, modification date and size as tab-separated text. Catalogs can be
|
|
44
|
+
gzip-compressed for compact storage and searched with ripgrep. It ships three
|
|
45
|
+
commands: dcatc to create a catalog, dcats to search catalogs, and dcatz to
|
|
46
|
+
read compressed catalogs as an alternative to zcat.
|
|
47
|
+
email:
|
|
48
|
+
- src@srcw.net
|
|
49
|
+
executables:
|
|
50
|
+
- dcatc
|
|
51
|
+
- dcats
|
|
52
|
+
- dcatz
|
|
53
|
+
extensions: []
|
|
54
|
+
extra_rdoc_files: []
|
|
55
|
+
files:
|
|
56
|
+
- LICENSE.txt
|
|
57
|
+
- README.ja.md
|
|
58
|
+
- README.md
|
|
59
|
+
- exe/dcatc
|
|
60
|
+
- exe/dcats
|
|
61
|
+
- exe/dcatz
|
|
62
|
+
- lib/dcat.rb
|
|
63
|
+
- lib/dcat/my_find.rb
|
|
64
|
+
- lib/dcat/utils.rb
|
|
65
|
+
- lib/dcat/version.rb
|
|
66
|
+
- sig/dcat.rbs
|
|
67
|
+
homepage: https://github.com/src256/dcat
|
|
68
|
+
licenses:
|
|
69
|
+
- MIT
|
|
70
|
+
metadata:
|
|
71
|
+
homepage_uri: https://github.com/src256/dcat
|
|
72
|
+
source_code_uri: https://github.com/src256/dcat
|
|
73
|
+
post_install_message:
|
|
74
|
+
rdoc_options: []
|
|
75
|
+
require_paths:
|
|
76
|
+
- lib
|
|
77
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
78
|
+
requirements:
|
|
79
|
+
- - ">="
|
|
80
|
+
- !ruby/object:Gem::Version
|
|
81
|
+
version: 2.6.0
|
|
82
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
83
|
+
requirements:
|
|
84
|
+
- - ">="
|
|
85
|
+
- !ruby/object:Gem::Version
|
|
86
|
+
version: '0'
|
|
87
|
+
requirements: []
|
|
88
|
+
rubygems_version: 3.5.22
|
|
89
|
+
signing_key:
|
|
90
|
+
specification_version: 4
|
|
91
|
+
summary: Create and search disk catalogs of files and their metadata.
|
|
92
|
+
test_files: []
|