pseek 2.5.0__tar.gz → 2.5.2__tar.gz
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.
- {pseek-2.5.0 → pseek-2.5.2}/PKG-INFO +30 -27
- {pseek-2.5.0 → pseek-2.5.2}/README.md +29 -26
- {pseek-2.5.0 → pseek-2.5.2}/pseek/cli.py +13 -7
- {pseek-2.5.0 → pseek-2.5.2}/pseek/parser.py +6 -11
- {pseek-2.5.0 → pseek-2.5.2}/pseek/searcher.py +185 -132
- {pseek-2.5.0 → pseek-2.5.2}/pseek/utils.py +4 -0
- {pseek-2.5.0 → pseek-2.5.2}/pseek.egg-info/PKG-INFO +30 -27
- {pseek-2.5.0 → pseek-2.5.2}/pyproject.toml +1 -1
- {pseek-2.5.0 → pseek-2.5.2}/LICENSE +0 -0
- {pseek-2.5.0 → pseek-2.5.2}/pseek/__init__.py +0 -0
- {pseek-2.5.0 → pseek-2.5.2}/pseek.egg-info/SOURCES.txt +0 -0
- {pseek-2.5.0 → pseek-2.5.2}/pseek.egg-info/dependency_links.txt +0 -0
- {pseek-2.5.0 → pseek-2.5.2}/pseek.egg-info/entry_points.txt +0 -0
- {pseek-2.5.0 → pseek-2.5.2}/pseek.egg-info/requires.txt +0 -0
- {pseek-2.5.0 → pseek-2.5.2}/pseek.egg-info/top_level.txt +0 -0
- {pseek-2.5.0 → pseek-2.5.2}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pseek
|
|
3
|
-
Version: 2.5.
|
|
3
|
+
Version: 2.5.2
|
|
4
4
|
Summary: Pseek is a Python library to search files, folders, and text
|
|
5
5
|
Author-email: Arian <ariannasiri86@gmail.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -44,7 +44,9 @@ pip install pseek
|
|
|
44
44
|
```sh
|
|
45
45
|
git clone https://github.com/ArianN8610/pysearch.git
|
|
46
46
|
cd pysearch
|
|
47
|
-
|
|
47
|
+
python -m venv venv
|
|
48
|
+
[Activate venv]
|
|
49
|
+
pip install click==8.1.8 lark==1.2.2 py7zr==1.0.0 rarfile==4.2 rapidfuzz==3.13.0
|
|
48
50
|
```
|
|
49
51
|
|
|
50
52
|
## Usage
|
|
@@ -82,32 +84,33 @@ pseek "error\d+" --regex
|
|
|
82
84
|
|
|
83
85
|
## Command Options
|
|
84
86
|
|
|
85
|
-
| Option | Description
|
|
86
|
-
|
|
87
|
-
| `--path` | Base directory to search in (default: current directory `.`)
|
|
88
|
-
| `--file` | Search only in file names
|
|
89
|
-
| `--directory` | Search only in directory names
|
|
90
|
-
| `--content` | Search inside file contents
|
|
91
|
-
| `--ext`, `--exclude-ext` | Filter by file extension (e.g., `txt`, `log`)
|
|
92
|
-
| `--case-sensitive` | Make the search case-sensitive (except when `--expr` is enabled, in which case you can make it case sensitive by putting `c` before term: `c"foo"`)
|
|
93
|
-
| `--regex` | Use regular expressions to search (except when `--expr` is enabled, in which case you can make it regex by putting `r` before term: `r"foo"`)
|
|
94
|
-
| `--include`, `--exclude` | Limit search results to specific set of directories or files
|
|
95
|
-
| `--re-include`, `--re-exclude` | Limit search results to specific directories or files with regex
|
|
96
|
-
| `--word` | Match the whole word only (except when `--expr` is enabled, in which case you can make it match whole word by putting `w` before term: `w"foo"`)
|
|
97
|
-
| `--expr` | Enable to write conditions in the query. Example: `r"foo.*bar" and ("bar" or "baz") and not "qux"` (To use regex, word,
|
|
98
|
-
| `--timeout` | To stop the search after a specified period of time (Seconds)
|
|
99
|
-
| `--fuzzy` | Enable fuzzy search (Highlighting and counting matches are disabled in this mode if `--word` is not enabled to prevent the program from slowing down)
|
|
100
|
-
| `--fuzzy_level` | Similarity threshold from 0 to 99 for fuzzy search (default: `80`)
|
|
101
|
-
| `--max-size`, `--min-size` | Specify maximum and minimum sizes for files and directories
|
|
102
|
-
| `--archive` | Enable search within archive files (e.g. `zip`, `rar`, `7z`, `gz`, `bz2`, `xz`, `tar`, `tar.gz`, `tar.bz2`, `tar.xz`)
|
|
103
|
-
| `--
|
|
104
|
-
| `--arc-
|
|
105
|
-
| `--arc-
|
|
106
|
-
| `--
|
|
107
|
-
| `--
|
|
108
|
-
| `--
|
|
87
|
+
| Option | Description |
|
|
88
|
+
|--------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
|
89
|
+
| `--path` | Base directory to search in (default: current directory `.`) |
|
|
90
|
+
| `--file` | Search only in file names |
|
|
91
|
+
| `--directory` | Search only in directory names |
|
|
92
|
+
| `--content` | Search inside file contents |
|
|
93
|
+
| `--ext`, `--exclude-ext` | Filter by file extension (e.g., `txt`, `log`) |
|
|
94
|
+
| `--case-sensitive` | Make the search case-sensitive (except when `--expr` is enabled, in which case you can make it case sensitive by putting `c` before term: `c"foo"`) |
|
|
95
|
+
| `--regex` | Use regular expressions to search (except when `--expr` is enabled, in which case you can make it regex by putting `r` before term: `r"foo"`) |
|
|
96
|
+
| `--include`, `--exclude` | Limit search results to specific set of directories or files |
|
|
97
|
+
| `--re-include`, `--re-exclude` | Limit search results to specific directories or files with regex |
|
|
98
|
+
| `--word` | Match the whole word only (except when `--expr` is enabled, in which case you can make it match whole word by putting `w` before term: `w"foo"`) |
|
|
99
|
+
| `--expr` | Enable to write conditions in the query. Example: `r"foo.*bar" and ("bar" or "baz") and not "qux"` (To use regex, word, case-sensitive, and fuzzy features, you can use the prefixes `r`, `w`, `c`, and `f` before terms. Allowed modes: `r`, `c`, `w`, `f`, `rc`, `cr`, `cw`, `wc`, `cf`, `fc`, `wf`, `fw`, `cwf`, `cfw`, `wcf`, `wfc`, `fcw`, `fwc`. Examples: `r"foo.*bar"`, `wcf"Aple"`, `cr".*Foo"`, ...) |
|
|
100
|
+
| `--timeout` | To stop the search after a specified period of time (Seconds) |
|
|
101
|
+
| `--fuzzy` | Enable fuzzy search (Highlighting and counting matches are disabled in this mode if `--word` is not enabled to prevent the program from slowing down). except when `--expr` is enabled, in which case you can make it fuzzy by putting `f` before term: `f"foo"` |
|
|
102
|
+
| `--fuzzy_level` | Similarity threshold from 0 to 99 for fuzzy search (default: `80`) |
|
|
103
|
+
| `--max-size`, `--min-size` | Specify maximum and minimum sizes for files and directories |
|
|
104
|
+
| `--archive` | Enable search within archive files (e.g. `zip`, `rar`, `7z`, `gz`, `bz2`, `xz`, `tar`, `tar.gz`, `tar.bz2`, `tar.xz`) |
|
|
105
|
+
| `--depth` | Maximum archive depth to recurse into (e.g. 2 means only 2 levels) |
|
|
106
|
+
| `--arc-ext`, `--arc-ee` | Filter by file extension inside archive files |
|
|
107
|
+
| `--arc-inc`, `--arc-exc` | Limit search results to specific set of directories or files inside archive files |
|
|
108
|
+
| `--arc-max`, `--arc-min` | Specify maximum and minimum sizes for files inside archive files (It doesn't work for directories because their size is zero in archive files) |
|
|
109
|
+
| `--rarfb` | Path to RAR backend tool (e.g. UnRAR.exe, ...) |
|
|
110
|
+
| `--full-path` | Display full path of files and directories |
|
|
111
|
+
| `--no-content` | Only display files path for content search |
|
|
109
112
|
|
|
110
113
|
## Requirements
|
|
111
114
|
|
|
112
115
|
* Python 3.6 or higher
|
|
113
|
-
* `unrar`, `bsdtar`, `unar` or `7zip` for the rarfile library to support searching inside `.rar` files (optional)
|
|
116
|
+
* `unrar`, `bsdtar`, `unar` or `7zip` for the [rarfile](https://pypi.org/project/rarfile/) library to support searching inside `.rar` files (optional)
|
|
@@ -27,7 +27,9 @@ pip install pseek
|
|
|
27
27
|
```sh
|
|
28
28
|
git clone https://github.com/ArianN8610/pysearch.git
|
|
29
29
|
cd pysearch
|
|
30
|
-
|
|
30
|
+
python -m venv venv
|
|
31
|
+
[Activate venv]
|
|
32
|
+
pip install click==8.1.8 lark==1.2.2 py7zr==1.0.0 rarfile==4.2 rapidfuzz==3.13.0
|
|
31
33
|
```
|
|
32
34
|
|
|
33
35
|
## Usage
|
|
@@ -65,32 +67,33 @@ pseek "error\d+" --regex
|
|
|
65
67
|
|
|
66
68
|
## Command Options
|
|
67
69
|
|
|
68
|
-
| Option | Description
|
|
69
|
-
|
|
70
|
-
| `--path` | Base directory to search in (default: current directory `.`)
|
|
71
|
-
| `--file` | Search only in file names
|
|
72
|
-
| `--directory` | Search only in directory names
|
|
73
|
-
| `--content` | Search inside file contents
|
|
74
|
-
| `--ext`, `--exclude-ext` | Filter by file extension (e.g., `txt`, `log`)
|
|
75
|
-
| `--case-sensitive` | Make the search case-sensitive (except when `--expr` is enabled, in which case you can make it case sensitive by putting `c` before term: `c"foo"`)
|
|
76
|
-
| `--regex` | Use regular expressions to search (except when `--expr` is enabled, in which case you can make it regex by putting `r` before term: `r"foo"`)
|
|
77
|
-
| `--include`, `--exclude` | Limit search results to specific set of directories or files
|
|
78
|
-
| `--re-include`, `--re-exclude` | Limit search results to specific directories or files with regex
|
|
79
|
-
| `--word` | Match the whole word only (except when `--expr` is enabled, in which case you can make it match whole word by putting `w` before term: `w"foo"`)
|
|
80
|
-
| `--expr` | Enable to write conditions in the query. Example: `r"foo.*bar" and ("bar" or "baz") and not "qux"` (To use regex, word,
|
|
81
|
-
| `--timeout` | To stop the search after a specified period of time (Seconds)
|
|
82
|
-
| `--fuzzy` | Enable fuzzy search (Highlighting and counting matches are disabled in this mode if `--word` is not enabled to prevent the program from slowing down)
|
|
83
|
-
| `--fuzzy_level` | Similarity threshold from 0 to 99 for fuzzy search (default: `80`)
|
|
84
|
-
| `--max-size`, `--min-size` | Specify maximum and minimum sizes for files and directories
|
|
85
|
-
| `--archive` | Enable search within archive files (e.g. `zip`, `rar`, `7z`, `gz`, `bz2`, `xz`, `tar`, `tar.gz`, `tar.bz2`, `tar.xz`)
|
|
86
|
-
| `--
|
|
87
|
-
| `--arc-
|
|
88
|
-
| `--arc-
|
|
89
|
-
| `--
|
|
90
|
-
| `--
|
|
91
|
-
| `--
|
|
70
|
+
| Option | Description |
|
|
71
|
+
|--------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
|
72
|
+
| `--path` | Base directory to search in (default: current directory `.`) |
|
|
73
|
+
| `--file` | Search only in file names |
|
|
74
|
+
| `--directory` | Search only in directory names |
|
|
75
|
+
| `--content` | Search inside file contents |
|
|
76
|
+
| `--ext`, `--exclude-ext` | Filter by file extension (e.g., `txt`, `log`) |
|
|
77
|
+
| `--case-sensitive` | Make the search case-sensitive (except when `--expr` is enabled, in which case you can make it case sensitive by putting `c` before term: `c"foo"`) |
|
|
78
|
+
| `--regex` | Use regular expressions to search (except when `--expr` is enabled, in which case you can make it regex by putting `r` before term: `r"foo"`) |
|
|
79
|
+
| `--include`, `--exclude` | Limit search results to specific set of directories or files |
|
|
80
|
+
| `--re-include`, `--re-exclude` | Limit search results to specific directories or files with regex |
|
|
81
|
+
| `--word` | Match the whole word only (except when `--expr` is enabled, in which case you can make it match whole word by putting `w` before term: `w"foo"`) |
|
|
82
|
+
| `--expr` | Enable to write conditions in the query. Example: `r"foo.*bar" and ("bar" or "baz") and not "qux"` (To use regex, word, case-sensitive, and fuzzy features, you can use the prefixes `r`, `w`, `c`, and `f` before terms. Allowed modes: `r`, `c`, `w`, `f`, `rc`, `cr`, `cw`, `wc`, `cf`, `fc`, `wf`, `fw`, `cwf`, `cfw`, `wcf`, `wfc`, `fcw`, `fwc`. Examples: `r"foo.*bar"`, `wcf"Aple"`, `cr".*Foo"`, ...) |
|
|
83
|
+
| `--timeout` | To stop the search after a specified period of time (Seconds) |
|
|
84
|
+
| `--fuzzy` | Enable fuzzy search (Highlighting and counting matches are disabled in this mode if `--word` is not enabled to prevent the program from slowing down). except when `--expr` is enabled, in which case you can make it fuzzy by putting `f` before term: `f"foo"` |
|
|
85
|
+
| `--fuzzy_level` | Similarity threshold from 0 to 99 for fuzzy search (default: `80`) |
|
|
86
|
+
| `--max-size`, `--min-size` | Specify maximum and minimum sizes for files and directories |
|
|
87
|
+
| `--archive` | Enable search within archive files (e.g. `zip`, `rar`, `7z`, `gz`, `bz2`, `xz`, `tar`, `tar.gz`, `tar.bz2`, `tar.xz`) |
|
|
88
|
+
| `--depth` | Maximum archive depth to recurse into (e.g. 2 means only 2 levels) |
|
|
89
|
+
| `--arc-ext`, `--arc-ee` | Filter by file extension inside archive files |
|
|
90
|
+
| `--arc-inc`, `--arc-exc` | Limit search results to specific set of directories or files inside archive files |
|
|
91
|
+
| `--arc-max`, `--arc-min` | Specify maximum and minimum sizes for files inside archive files (It doesn't work for directories because their size is zero in archive files) |
|
|
92
|
+
| `--rarfb` | Path to RAR backend tool (e.g. UnRAR.exe, ...) |
|
|
93
|
+
| `--full-path` | Display full path of files and directories |
|
|
94
|
+
| `--no-content` | Only display files path for content search |
|
|
92
95
|
|
|
93
96
|
## Requirements
|
|
94
97
|
|
|
95
98
|
* Python 3.6 or higher
|
|
96
|
-
* `unrar`, `bsdtar`, `unar` or `7zip` for the rarfile library to support searching inside `.rar` files (optional)
|
|
99
|
+
* `unrar`, `bsdtar`, `unar` or `7zip` for the [rarfile](https://pypi.org/project/rarfile/) library to support searching inside `.rar` files (optional)
|
|
@@ -46,12 +46,15 @@ def run_search_process(file, directory, content, search_instance):
|
|
|
46
46
|
'in which case you can make it match whole word by putting w before term: w"foo")')
|
|
47
47
|
@click.option('--expr', is_flag=True,
|
|
48
48
|
help='Enable to write conditions in the query. Example: r"foo.*bar" and ("bar" or "baz") and not "qux" '
|
|
49
|
-
'(To use regex, word,
|
|
50
|
-
'you can use the prefixes r, w, and
|
|
51
|
-
'
|
|
49
|
+
'(To use regex, word, case-sensitive, and fuzzy features, '
|
|
50
|
+
'you can use the prefixes r, w, c, and f before terms. Allowed modes: '
|
|
51
|
+
'r, c, w, f, rc, cr, cw, wc, cf, fc, wf, fw, cwf, cfw, wcf, wfc, fcw, fwc. '
|
|
52
|
+
'Examples: r"foo.*bar", wcf"Aple", cr".*Foo", ...)')
|
|
52
53
|
@click.option('--timeout', type=click.INT,
|
|
53
54
|
help='To stop the search after a specified period of time (Seconds)')
|
|
54
|
-
@click.option('--fuzzy', is_flag=True, help='Enable fuzzy search (approximate matching).'
|
|
55
|
+
@click.option('--fuzzy', is_flag=True, help='Enable fuzzy search (approximate matching). '
|
|
56
|
+
'except when --expr is enabled, '
|
|
57
|
+
'in which case you can make it fuzzy by putting f before term: f"foo"')
|
|
55
58
|
@click.option('--fuzzy-level', type=click.IntRange(0, 99), default=80, show_default=True,
|
|
56
59
|
help='Similarity threshold from 0 to 99 for fuzzy search.')
|
|
57
60
|
# Extension filters
|
|
@@ -74,6 +77,8 @@ def run_search_process(file, directory, content, search_instance):
|
|
|
74
77
|
# Archive options
|
|
75
78
|
@click.option('--archive', is_flag=True,
|
|
76
79
|
help='Enable search within archive files (e.g. zip, rar, 7z, gz, bz2, xz, tar, tar.gz, tar.bz2, tar.xz)')
|
|
80
|
+
@click.option('--depth', type=click.IntRange(min=0), show_default=True,
|
|
81
|
+
help='Maximum archive depth to recurse into (e.g. 2 means only 2 levels).')
|
|
77
82
|
@click.option('--arc-ext', multiple=True, type=click.STRING,
|
|
78
83
|
help='Include files with these extensions inside archive files. Example: --arc-ext py --arc-ext js')
|
|
79
84
|
@click.option('--arc-ee', multiple=True, type=click.STRING,
|
|
@@ -91,8 +96,8 @@ def run_search_process(file, directory, content, search_instance):
|
|
|
91
96
|
@click.option('--full-path', is_flag=True, help='Display full paths for results.')
|
|
92
97
|
@click.option('--no-content', is_flag=True, help='Only display files path for content search.')
|
|
93
98
|
def search(query, path, file, directory, content, case_sensitive, ext, exclude_ext, regex, include, exclude,
|
|
94
|
-
re_include, re_exclude, word, expr, timeout, fuzzy, fuzzy_level, max_size, min_size, archive,
|
|
95
|
-
arc_ee, arc_inc, arc_exc, arc_max, arc_min, rarfb, full_path, no_content):
|
|
99
|
+
re_include, re_exclude, word, expr, timeout, fuzzy, fuzzy_level, max_size, min_size, archive, depth,
|
|
100
|
+
arc_ext, arc_ee, arc_inc, arc_exc, arc_max, arc_min, rarfb, full_path, no_content):
|
|
96
101
|
"""Search for files, directories, and file content based on the query."""
|
|
97
102
|
|
|
98
103
|
check_rar_backend(archive, rarfb, query)
|
|
@@ -105,7 +110,7 @@ def search(query, path, file, directory, content, case_sensitive, ext, exclude_e
|
|
|
105
110
|
fg="yellow"
|
|
106
111
|
)
|
|
107
112
|
)
|
|
108
|
-
|
|
113
|
+
elif word and " " in query:
|
|
109
114
|
click.echo(
|
|
110
115
|
click.style(
|
|
111
116
|
'Warning: When using "--fuzzy" and "--word", it is better to have the query be a word and '
|
|
@@ -137,6 +142,7 @@ def search(query, path, file, directory, content, case_sensitive, ext, exclude_e
|
|
|
137
142
|
max_size=max_size,
|
|
138
143
|
min_size=min_size,
|
|
139
144
|
archive=archive,
|
|
145
|
+
depth=depth,
|
|
140
146
|
arc_ext=arc_ext,
|
|
141
147
|
arc_ee=arc_ee,
|
|
142
148
|
arc_inc=arc_inc,
|
|
@@ -123,7 +123,7 @@ query_grammar = r"""
|
|
|
123
123
|
| ESCAPED_STRING -> string
|
|
124
124
|
| "(" expr ")"
|
|
125
125
|
|
|
126
|
-
PREFIXED_STRING: /(r|c|w|rc|cr|cw|wc)"([^"\\]|\\.)*"/
|
|
126
|
+
PREFIXED_STRING: /(r|c|w|f|rc|cr|cw|wc|cf|fc|wf|fw|cwf|cfw|wcf|wfc|fcw|fwc)"([^"\\]|\\.)*"/
|
|
127
127
|
|
|
128
128
|
%import common.ESCAPED_STRING
|
|
129
129
|
%import common.WS
|
|
@@ -133,9 +133,8 @@ PREFIXED_STRING: /(r|c|w|rc|cr|cw|wc)"([^"\\]|\\.)*"/
|
|
|
133
133
|
|
|
134
134
|
class TreeToExpr(Transformer):
|
|
135
135
|
"""Transform parsed tree into expression tree (ExprNode subclasses)"""
|
|
136
|
-
def __init__(self,
|
|
136
|
+
def __init__(self, fuzzy_level):
|
|
137
137
|
super().__init__()
|
|
138
|
-
self.fuzzy = fuzzy
|
|
139
138
|
self.fuzzy_level = fuzzy_level
|
|
140
139
|
|
|
141
140
|
def string(self, s):
|
|
@@ -146,8 +145,8 @@ class TreeToExpr(Transformer):
|
|
|
146
145
|
False,
|
|
147
146
|
False,
|
|
148
147
|
False,
|
|
149
|
-
|
|
150
|
-
|
|
148
|
+
False,
|
|
149
|
+
None
|
|
151
150
|
)
|
|
152
151
|
|
|
153
152
|
def prefixed_string(self, s):
|
|
@@ -160,7 +159,7 @@ class TreeToExpr(Transformer):
|
|
|
160
159
|
regex='r' in prefix,
|
|
161
160
|
whole_word='w' in prefix,
|
|
162
161
|
case_sensitive='c' in prefix,
|
|
163
|
-
fuzzy=
|
|
162
|
+
fuzzy='f' in prefix,
|
|
164
163
|
fuzzy_level=self.fuzzy_level
|
|
165
164
|
)
|
|
166
165
|
|
|
@@ -187,7 +186,7 @@ def parse_query_expression(query: str, expr, regex, whole_word, case_sensitive,
|
|
|
187
186
|
parser = Lark(query_grammar, parser="lalr")
|
|
188
187
|
try:
|
|
189
188
|
tree = parser.parse(query)
|
|
190
|
-
return TreeToExpr(
|
|
189
|
+
return TreeToExpr(fuzzy_level).transform(tree)
|
|
191
190
|
except Exception as e:
|
|
192
191
|
click.echo(click.style("Query parser error:\n\n", fg='red') + str(e))
|
|
193
192
|
sys.exit(1)
|
|
@@ -223,10 +222,6 @@ def highlight_text(expr: ExprNode, text: str, fuzzy: bool) -> str:
|
|
|
223
222
|
elif isinstance(node, NotNode):
|
|
224
223
|
collect_matches(node.child)
|
|
225
224
|
|
|
226
|
-
# If fuzzy is enabled but whole_word is False, return plain text
|
|
227
|
-
if fuzzy and isinstance(expr, TermNode) and not expr.whole_word:
|
|
228
|
-
return text
|
|
229
|
-
|
|
230
225
|
collect_matches(expr)
|
|
231
226
|
|
|
232
227
|
# Sort and merge overlapping matches (for example, if one match was inside another match)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import mmap
|
|
2
2
|
import click
|
|
3
3
|
from pathlib import Path
|
|
4
|
-
from .utils import compile_regex, get_archive_path_size, try_decode
|
|
4
|
+
from .utils import compile_regex, get_archive_path_size, try_decode, get_path_suffix
|
|
5
5
|
from .parser import parse_query_expression, TermNode, highlight_text
|
|
6
6
|
from concurrent.futures import ThreadPoolExecutor
|
|
7
7
|
import zipfile
|
|
@@ -11,9 +11,10 @@ import gzip
|
|
|
11
11
|
import bz2
|
|
12
12
|
import lzma
|
|
13
13
|
import rarfile
|
|
14
|
+
import io
|
|
14
15
|
|
|
15
16
|
# Archive extensions that are allowed
|
|
16
|
-
ARCHIVE_EXTS = ('zip', 'rar', '7z', 'tar', 'gz', 'bz2', 'xz', '
|
|
17
|
+
ARCHIVE_EXTS = ('zip', 'rar', '7z', 'tar', 'tar.gz', 'tar.bz2', 'tar.xz', 'gz', 'bz2', 'xz')
|
|
17
18
|
|
|
18
19
|
# Extensions that are not suitable for content search (binary, media, etc.)
|
|
19
20
|
EXCLUDED_EXTENSIONS = (
|
|
@@ -30,7 +31,7 @@ EXCLUDED_EXTENSIONS = (
|
|
|
30
31
|
|
|
31
32
|
class Search:
|
|
32
33
|
def __init__(self, base_path, query, case_sensitive, ext, exclude_ext, regex, include, exclude, re_include,
|
|
33
|
-
re_exclude, whole_word, expr, fuzzy, fuzzy_level, max_size, min_size, archive, arc_ext, arc_ee,
|
|
34
|
+
re_exclude, whole_word, expr, fuzzy, fuzzy_level, max_size, min_size, archive, depth, arc_ext, arc_ee,
|
|
34
35
|
arc_inc, arc_exc, arc_max, arc_min, full_path, no_content):
|
|
35
36
|
"""Initialize search parameters"""
|
|
36
37
|
self.base_path = Path(base_path)
|
|
@@ -50,6 +51,7 @@ class Search:
|
|
|
50
51
|
self.max_size = max_size
|
|
51
52
|
self.min_size = min_size
|
|
52
53
|
self.archive = archive
|
|
54
|
+
self.depth = depth
|
|
53
55
|
self.arc_ext = set(arc_ext)
|
|
54
56
|
self.arc_ee = set(arc_ee) | {''} if arc_ee else set()
|
|
55
57
|
self.arc_inc = {Path(p) for p in arc_inc}
|
|
@@ -71,10 +73,10 @@ class Search:
|
|
|
71
73
|
# If path is inaccessible, skip it.
|
|
72
74
|
return True
|
|
73
75
|
|
|
74
|
-
file_ext =
|
|
76
|
+
file_ext = get_path_suffix(p_resolved)
|
|
75
77
|
|
|
76
78
|
# Ignore some filters for archive files when archive is enabled
|
|
77
|
-
if (not self.archive or not file_ext in
|
|
79
|
+
if (not self.archive or not file_ext in ARCHIVE_EXTS[:-3]) and \
|
|
78
80
|
((search_type in ('file', 'content') and not p_resolved.is_file())
|
|
79
81
|
or (search_type == 'directory' and not p_resolved.is_dir())):
|
|
80
82
|
return True
|
|
@@ -96,11 +98,10 @@ class Search:
|
|
|
96
98
|
|
|
97
99
|
return False
|
|
98
100
|
|
|
99
|
-
|
|
100
101
|
def archive_should_skip(self, path_info: Path, search_type: str, is_file: bool, is_dir: bool, p_size: float):
|
|
101
102
|
"""Check whether the file/directory inside archive files should be skipped based on various filters"""
|
|
102
103
|
|
|
103
|
-
file_ext =
|
|
104
|
+
file_ext = get_path_suffix(path_info)
|
|
104
105
|
|
|
105
106
|
if (search_type in ('file', 'content') and not is_file) \
|
|
106
107
|
or (search_type == 'directory' and not is_dir):
|
|
@@ -122,38 +123,57 @@ class Search:
|
|
|
122
123
|
|
|
123
124
|
return False
|
|
124
125
|
|
|
125
|
-
def extract_names_from_archive(self, file_path: Path, search_type: str
|
|
126
|
-
|
|
126
|
+
def extract_names_from_archive(self, file_path: Path, search_type: str,
|
|
127
|
+
file_bytes: bytes = None, parent_label: str = ''):
|
|
128
|
+
"""
|
|
129
|
+
Recursively extract files and directories name from archive files.
|
|
130
|
+
Supports nested archives like a.zip::b.7z::c.txt.
|
|
131
|
+
|
|
132
|
+
Parameters:
|
|
133
|
+
file_path (Path): the archive file path
|
|
134
|
+
search_type (str): search type ( file / directory )
|
|
135
|
+
file_bytes (bytes | None): optional byte data if already read (for recursion)
|
|
136
|
+
parent_label (str): string for nested archive tracking like a.zip::b.7z::file.txt
|
|
137
|
+
|
|
138
|
+
Yields:
|
|
139
|
+
(str, Path): tuple of parent label and file or directory name
|
|
140
|
+
"""
|
|
127
141
|
|
|
128
|
-
file_ext =
|
|
142
|
+
file_ext = get_path_suffix(file_path)
|
|
143
|
+
label_prefix = (parent_label + str(file_path) + '::') if parent_label else '::'
|
|
129
144
|
|
|
130
145
|
try:
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
146
|
+
# Decide the stream source: from disk or memory
|
|
147
|
+
file_stream = io.BytesIO(file_bytes) if file_bytes is not None else open(file_path, 'rb')
|
|
148
|
+
|
|
149
|
+
# Handle ZIP and RAR archives
|
|
150
|
+
if file_ext in ('zip', 'rar'):
|
|
151
|
+
opener = {'zip': zipfile.ZipFile, 'rar': rarfile.RarFile}[file_ext]
|
|
152
|
+
with opener(file_stream) as f:
|
|
153
|
+
for info in f.infolist():
|
|
134
154
|
name = Path(info.filename)
|
|
135
155
|
if not self.archive_should_skip(
|
|
136
156
|
name,
|
|
137
157
|
search_type,
|
|
138
158
|
not info.is_dir(),
|
|
139
159
|
info.is_dir(),
|
|
140
|
-
get_archive_path_size(info,
|
|
160
|
+
get_archive_path_size(info, file_ext)
|
|
141
161
|
):
|
|
142
|
-
yield name
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
if
|
|
162
|
+
yield label_prefix, name
|
|
163
|
+
|
|
164
|
+
# At each recursion, subtract 1 from depth if it's set
|
|
165
|
+
new_depth = None if self.depth is None else self.depth - 1
|
|
166
|
+
# Check if this is a nested archive
|
|
167
|
+
if get_path_suffix(name) in ARCHIVE_EXTS[:-3] and (new_depth is None or new_depth >= 0):
|
|
168
|
+
yield from self.extract_names_from_archive(
|
|
148
169
|
name,
|
|
149
170
|
search_type,
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
yield name
|
|
171
|
+
f.read(info),
|
|
172
|
+
label_prefix
|
|
173
|
+
)
|
|
174
|
+
# Handle 7Z archives
|
|
155
175
|
elif file_ext == '7z':
|
|
156
|
-
with py7zr.SevenZipFile(
|
|
176
|
+
with py7zr.SevenZipFile(file_stream, mode='r') as z:
|
|
157
177
|
for info in z.list():
|
|
158
178
|
name = Path(info.filename)
|
|
159
179
|
if not self.archive_should_skip(
|
|
@@ -163,18 +183,31 @@ class Search:
|
|
|
163
183
|
info.is_directory,
|
|
164
184
|
get_archive_path_size(info, '7z')
|
|
165
185
|
):
|
|
166
|
-
yield name
|
|
186
|
+
yield label_prefix, name
|
|
187
|
+
|
|
188
|
+
new_depth = None if self.depth is None else self.depth - 1
|
|
189
|
+
if get_path_suffix(name) in ARCHIVE_EXTS[:-3] and (new_depth is None or new_depth >= 0):
|
|
190
|
+
file_data = z.read([info.filename]).get(info.filename)
|
|
191
|
+
if file_data is None:
|
|
192
|
+
continue
|
|
193
|
+
|
|
194
|
+
yield from self.extract_names_from_archive(
|
|
195
|
+
name,
|
|
196
|
+
search_type,
|
|
197
|
+
file_data.read(),
|
|
198
|
+
label_prefix
|
|
199
|
+
)
|
|
200
|
+
# Handle TAR and compressed TAR formats
|
|
167
201
|
elif file_ext in ('tar', 'tar.gz', 'tar.bz2', 'tar.xz'):
|
|
168
202
|
# Specify the mode based on the file ext to open it
|
|
169
|
-
mode =
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
with tarfile.open(file_path, mode) as tf:
|
|
203
|
+
mode = {
|
|
204
|
+
'tar': 'r',
|
|
205
|
+
'tar.gz': 'r:gz',
|
|
206
|
+
'tar.bz2': 'r:bz2',
|
|
207
|
+
'tar.xz': 'r:xz'
|
|
208
|
+
}[file_ext]
|
|
209
|
+
|
|
210
|
+
with tarfile.open(fileobj=file_stream, mode=mode) as tf:
|
|
178
211
|
for member in tf.getmembers():
|
|
179
212
|
name = Path(member.name)
|
|
180
213
|
if not self.archive_should_skip(
|
|
@@ -184,120 +217,139 @@ class Search:
|
|
|
184
217
|
member.isdir(),
|
|
185
218
|
get_archive_path_size(member, file_ext)
|
|
186
219
|
):
|
|
187
|
-
yield name
|
|
220
|
+
yield label_prefix, name
|
|
221
|
+
|
|
222
|
+
new_depth = None if self.depth is None else self.depth - 1
|
|
223
|
+
if get_path_suffix(name) in ARCHIVE_EXTS[:-3] and (new_depth is None or new_depth >= 0):
|
|
224
|
+
f = tf.extractfile(member)
|
|
225
|
+
if f is None:
|
|
226
|
+
continue
|
|
227
|
+
|
|
228
|
+
yield from self.extract_names_from_archive(
|
|
229
|
+
name,
|
|
230
|
+
search_type,
|
|
231
|
+
f.read(),
|
|
232
|
+
label_prefix
|
|
233
|
+
)
|
|
188
234
|
except Exception:
|
|
189
235
|
return # silently skip invalid archives
|
|
190
236
|
|
|
191
|
-
def extract_text_from_archive(self, file_path: Path):
|
|
237
|
+
def extract_text_from_archive(self, file_path: Path, file_bytes: bytes = None, parent_label: str = ''):
|
|
192
238
|
"""
|
|
193
|
-
|
|
194
|
-
Supports
|
|
239
|
+
Recursively extract (path_label, text_content) from any archive file.
|
|
240
|
+
Supports nested archives like a.zip::b.7z::c.txt.
|
|
241
|
+
|
|
242
|
+
Parameters:
|
|
243
|
+
file_path (Path): the archive file path
|
|
244
|
+
file_bytes (bytes | None): optional byte data if already read (for recursion)
|
|
245
|
+
parent_label (str): string for nested archive tracking like a.zip::b.7z::file.txt
|
|
246
|
+
|
|
247
|
+
Yields:
|
|
248
|
+
(str, str): tuple of full virtual path and decoded content text
|
|
195
249
|
"""
|
|
196
250
|
|
|
197
|
-
file_ext =
|
|
251
|
+
file_ext = get_path_suffix(file_path)
|
|
252
|
+
label_prefix = (parent_label + str(file_path) + '::') if parent_label else '::'
|
|
198
253
|
|
|
199
254
|
try:
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
info.is_dir(),
|
|
209
|
-
get_archive_path_size(info, 'zip')
|
|
210
|
-
):
|
|
211
|
-
continue
|
|
212
|
-
|
|
213
|
-
with zf.open(info) as f:
|
|
214
|
-
data = f.read()
|
|
215
|
-
text = try_decode(data)
|
|
216
|
-
if text is not None:
|
|
217
|
-
yield info.filename, text
|
|
218
|
-
elif file_ext == 'rar':
|
|
219
|
-
with rarfile.RarFile(file_path) as rf:
|
|
220
|
-
for info in rf.infolist():
|
|
255
|
+
# Decide the stream source: from disk or memory
|
|
256
|
+
file_stream = io.BytesIO(file_bytes) if file_bytes is not None else open(file_path, 'rb')
|
|
257
|
+
|
|
258
|
+
# Handle ZIP and RAR archives
|
|
259
|
+
if file_ext in ('zip', 'rar'):
|
|
260
|
+
opener = {'zip': zipfile.ZipFile, 'rar': rarfile.RarFile}[file_ext]
|
|
261
|
+
with opener(file_stream) as f:
|
|
262
|
+
for info in f.infolist():
|
|
221
263
|
file_name = Path(info.filename)
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
264
|
+
data = f.read(info)
|
|
265
|
+
# At each recursion, subtract 1 from depth if it's set
|
|
266
|
+
new_depth = None if self.depth is None else self.depth - 1
|
|
267
|
+
|
|
268
|
+
# Check if this is a nested archive
|
|
269
|
+
if get_path_suffix(file_name) in ARCHIVE_EXTS and (new_depth is None or new_depth >= 0):
|
|
270
|
+
yield from self.extract_text_from_archive(file_name, data, label_prefix)
|
|
271
|
+
else:
|
|
272
|
+
if self.archive_should_skip(
|
|
273
|
+
file_name,
|
|
274
|
+
'content',
|
|
275
|
+
not info.is_dir(),
|
|
276
|
+
info.is_dir(),
|
|
277
|
+
get_archive_path_size(info, file_ext)
|
|
278
|
+
):
|
|
279
|
+
continue
|
|
230
280
|
|
|
231
|
-
with rf.open(info) as f:
|
|
232
|
-
data = f.read()
|
|
233
281
|
text = try_decode(data)
|
|
234
282
|
if text is not None:
|
|
235
|
-
yield
|
|
283
|
+
yield label_prefix + str(file_name), text
|
|
284
|
+
# Handle 7Z archives
|
|
236
285
|
elif file_ext == '7z':
|
|
237
|
-
with py7zr.SevenZipFile(
|
|
286
|
+
with py7zr.SevenZipFile(file_stream, mode='r') as archive:
|
|
238
287
|
for info in archive.list():
|
|
239
|
-
|
|
240
|
-
if
|
|
241
|
-
file_name,
|
|
242
|
-
'content',
|
|
243
|
-
not info.is_directory,
|
|
244
|
-
info.is_directory,
|
|
245
|
-
get_archive_path_size(info, '7z')
|
|
246
|
-
):
|
|
288
|
+
file_data = archive.read([info.filename]).get(info.filename)
|
|
289
|
+
if file_data is None:
|
|
247
290
|
continue
|
|
248
291
|
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
if
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
292
|
+
file_name = Path(info.filename)
|
|
293
|
+
data = file_data.read()
|
|
294
|
+
new_depth = None if self.depth is None else self.depth - 1
|
|
295
|
+
|
|
296
|
+
if get_path_suffix(file_name) in ARCHIVE_EXTS and (new_depth is None or new_depth >= 0):
|
|
297
|
+
yield from self.extract_text_from_archive(file_name, data, label_prefix)
|
|
298
|
+
else:
|
|
299
|
+
if self.archive_should_skip(
|
|
300
|
+
file_name,
|
|
301
|
+
'content',
|
|
302
|
+
not info.is_directory,
|
|
303
|
+
info.is_directory,
|
|
304
|
+
get_archive_path_size(info, '7z')
|
|
305
|
+
):
|
|
306
|
+
continue
|
|
307
|
+
|
|
308
|
+
text = try_decode(data)
|
|
309
|
+
if text is not None:
|
|
310
|
+
yield label_prefix + str(file_name), text
|
|
311
|
+
# Handle TAR and compressed TAR formats
|
|
256
312
|
elif file_ext in ('tar', 'tar.gz', 'tar.bz2', 'tar.xz'):
|
|
257
|
-
mode =
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
with tarfile.open(
|
|
313
|
+
mode = {
|
|
314
|
+
'tar': 'r',
|
|
315
|
+
'tar.gz': 'r:gz',
|
|
316
|
+
'tar.bz2': 'r:bz2',
|
|
317
|
+
'tar.xz': 'r:xz'
|
|
318
|
+
}[file_ext]
|
|
319
|
+
|
|
320
|
+
with tarfile.open(fileobj=file_stream, mode=mode) as tf:
|
|
265
321
|
for member in tf.getmembers():
|
|
266
|
-
file_name = Path(member.name)
|
|
267
|
-
if self.archive_should_skip(
|
|
268
|
-
file_name,
|
|
269
|
-
'content',
|
|
270
|
-
member.isfile(),
|
|
271
|
-
member.isdir(),
|
|
272
|
-
get_archive_path_size(member, file_ext)
|
|
273
|
-
):
|
|
274
|
-
continue
|
|
275
|
-
|
|
276
322
|
f = tf.extractfile(member)
|
|
277
323
|
if f is None:
|
|
278
324
|
continue
|
|
325
|
+
|
|
326
|
+
file_name = Path(member.name)
|
|
279
327
|
data = f.read()
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
328
|
+
new_depth = None if self.depth is None else self.depth - 1
|
|
329
|
+
|
|
330
|
+
if get_path_suffix(file_name) in ARCHIVE_EXTS and (new_depth is None or new_depth >= 0):
|
|
331
|
+
yield from self.extract_text_from_archive(file_name, data, label_prefix)
|
|
332
|
+
else:
|
|
333
|
+
if self.archive_should_skip(
|
|
334
|
+
file_name,
|
|
335
|
+
'content',
|
|
336
|
+
member.isfile(),
|
|
337
|
+
member.isdir(),
|
|
338
|
+
get_archive_path_size(member, file_ext)
|
|
339
|
+
):
|
|
340
|
+
continue
|
|
341
|
+
|
|
342
|
+
text = try_decode(data)
|
|
343
|
+
if text is not None:
|
|
344
|
+
yield label_prefix + str(file_name), text
|
|
345
|
+
# Handle single compressed files like .gz, .bz2, .xz
|
|
346
|
+
elif file_ext in ARCHIVE_EXTS[-3:]:
|
|
347
|
+
opener = {'gz': gzip.open, 'bz2': bz2.open, 'xz': lzma.open}[file_ext]
|
|
348
|
+
with opener(file_stream, 'rb') as f:
|
|
297
349
|
data = f.read()
|
|
298
350
|
text = try_decode(data)
|
|
299
351
|
if text is not None:
|
|
300
|
-
yield
|
|
352
|
+
yield file_ext, text
|
|
301
353
|
except Exception:
|
|
302
354
|
return
|
|
303
355
|
|
|
@@ -319,7 +371,7 @@ class Search:
|
|
|
319
371
|
|
|
320
372
|
# Choose parent path based on full_path flag
|
|
321
373
|
p_parent = p_resolved.parent if self.full_path else p.parent
|
|
322
|
-
p_ext =
|
|
374
|
+
p_ext = get_path_suffix(p_resolved)
|
|
323
375
|
|
|
324
376
|
if pattern.evaluate(p.name) and not (search_type == 'directory' and p_resolved.is_file()):
|
|
325
377
|
# Highlight matched query in the name
|
|
@@ -327,11 +379,11 @@ class Search:
|
|
|
327
379
|
matches.append(f'{p_parent}\\{highlighted_name}')
|
|
328
380
|
|
|
329
381
|
# Search for files and directories name inside archive files if archive is active
|
|
330
|
-
if self.archive and p_ext in
|
|
331
|
-
for name in self.extract_names_from_archive(p_resolved, search_type):
|
|
382
|
+
if self.archive and p_ext in ARCHIVE_EXTS[:-3]:
|
|
383
|
+
for label, name in self.extract_names_from_archive(p_resolved, search_type):
|
|
332
384
|
if pattern.evaluate(name.name):
|
|
333
385
|
highlighted_name = highlight_text(pattern, name.name, self.fuzzy)
|
|
334
|
-
matches.append(f'{p_parent}\\{p.name}
|
|
386
|
+
matches.append(f'{p_parent}\\{p.name}{label}{name.parent}\\{highlighted_name}')
|
|
335
387
|
else: # content search
|
|
336
388
|
# Use dictionary: key: file path (colored), value: list of line matches
|
|
337
389
|
matches = {} if not self.no_content else set()
|
|
@@ -355,13 +407,14 @@ class Search:
|
|
|
355
407
|
file_label = str(file_path.resolve()) if self.full_path else str(file_path)
|
|
356
408
|
|
|
357
409
|
# First, check if the file is an archive, extract it from the archive and perform a search
|
|
358
|
-
if self.archive and
|
|
410
|
+
if self.archive and get_path_suffix(file_path) in ARCHIVE_EXTS:
|
|
359
411
|
for fname, content in self.extract_text_from_archive(file_path):
|
|
360
412
|
if not pattern.evaluate(content) and not self.expr:
|
|
361
413
|
continue
|
|
362
414
|
|
|
363
|
-
# Change file_label for archive files
|
|
364
|
-
|
|
415
|
+
# Change file_label for archive files (zip, rar, 7z, tar)
|
|
416
|
+
if fname not in ARCHIVE_EXTS[-3:]:
|
|
417
|
+
file_label += fname
|
|
365
418
|
|
|
366
419
|
if self.no_content and not self.expr:
|
|
367
420
|
matches.add(click.style(file_label, fg='cyan'))
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pseek
|
|
3
|
-
Version: 2.5.
|
|
3
|
+
Version: 2.5.2
|
|
4
4
|
Summary: Pseek is a Python library to search files, folders, and text
|
|
5
5
|
Author-email: Arian <ariannasiri86@gmail.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -44,7 +44,9 @@ pip install pseek
|
|
|
44
44
|
```sh
|
|
45
45
|
git clone https://github.com/ArianN8610/pysearch.git
|
|
46
46
|
cd pysearch
|
|
47
|
-
|
|
47
|
+
python -m venv venv
|
|
48
|
+
[Activate venv]
|
|
49
|
+
pip install click==8.1.8 lark==1.2.2 py7zr==1.0.0 rarfile==4.2 rapidfuzz==3.13.0
|
|
48
50
|
```
|
|
49
51
|
|
|
50
52
|
## Usage
|
|
@@ -82,32 +84,33 @@ pseek "error\d+" --regex
|
|
|
82
84
|
|
|
83
85
|
## Command Options
|
|
84
86
|
|
|
85
|
-
| Option | Description
|
|
86
|
-
|
|
87
|
-
| `--path` | Base directory to search in (default: current directory `.`)
|
|
88
|
-
| `--file` | Search only in file names
|
|
89
|
-
| `--directory` | Search only in directory names
|
|
90
|
-
| `--content` | Search inside file contents
|
|
91
|
-
| `--ext`, `--exclude-ext` | Filter by file extension (e.g., `txt`, `log`)
|
|
92
|
-
| `--case-sensitive` | Make the search case-sensitive (except when `--expr` is enabled, in which case you can make it case sensitive by putting `c` before term: `c"foo"`)
|
|
93
|
-
| `--regex` | Use regular expressions to search (except when `--expr` is enabled, in which case you can make it regex by putting `r` before term: `r"foo"`)
|
|
94
|
-
| `--include`, `--exclude` | Limit search results to specific set of directories or files
|
|
95
|
-
| `--re-include`, `--re-exclude` | Limit search results to specific directories or files with regex
|
|
96
|
-
| `--word` | Match the whole word only (except when `--expr` is enabled, in which case you can make it match whole word by putting `w` before term: `w"foo"`)
|
|
97
|
-
| `--expr` | Enable to write conditions in the query. Example: `r"foo.*bar" and ("bar" or "baz") and not "qux"` (To use regex, word,
|
|
98
|
-
| `--timeout` | To stop the search after a specified period of time (Seconds)
|
|
99
|
-
| `--fuzzy` | Enable fuzzy search (Highlighting and counting matches are disabled in this mode if `--word` is not enabled to prevent the program from slowing down)
|
|
100
|
-
| `--fuzzy_level` | Similarity threshold from 0 to 99 for fuzzy search (default: `80`)
|
|
101
|
-
| `--max-size`, `--min-size` | Specify maximum and minimum sizes for files and directories
|
|
102
|
-
| `--archive` | Enable search within archive files (e.g. `zip`, `rar`, `7z`, `gz`, `bz2`, `xz`, `tar`, `tar.gz`, `tar.bz2`, `tar.xz`)
|
|
103
|
-
| `--
|
|
104
|
-
| `--arc-
|
|
105
|
-
| `--arc-
|
|
106
|
-
| `--
|
|
107
|
-
| `--
|
|
108
|
-
| `--
|
|
87
|
+
| Option | Description |
|
|
88
|
+
|--------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
|
89
|
+
| `--path` | Base directory to search in (default: current directory `.`) |
|
|
90
|
+
| `--file` | Search only in file names |
|
|
91
|
+
| `--directory` | Search only in directory names |
|
|
92
|
+
| `--content` | Search inside file contents |
|
|
93
|
+
| `--ext`, `--exclude-ext` | Filter by file extension (e.g., `txt`, `log`) |
|
|
94
|
+
| `--case-sensitive` | Make the search case-sensitive (except when `--expr` is enabled, in which case you can make it case sensitive by putting `c` before term: `c"foo"`) |
|
|
95
|
+
| `--regex` | Use regular expressions to search (except when `--expr` is enabled, in which case you can make it regex by putting `r` before term: `r"foo"`) |
|
|
96
|
+
| `--include`, `--exclude` | Limit search results to specific set of directories or files |
|
|
97
|
+
| `--re-include`, `--re-exclude` | Limit search results to specific directories or files with regex |
|
|
98
|
+
| `--word` | Match the whole word only (except when `--expr` is enabled, in which case you can make it match whole word by putting `w` before term: `w"foo"`) |
|
|
99
|
+
| `--expr` | Enable to write conditions in the query. Example: `r"foo.*bar" and ("bar" or "baz") and not "qux"` (To use regex, word, case-sensitive, and fuzzy features, you can use the prefixes `r`, `w`, `c`, and `f` before terms. Allowed modes: `r`, `c`, `w`, `f`, `rc`, `cr`, `cw`, `wc`, `cf`, `fc`, `wf`, `fw`, `cwf`, `cfw`, `wcf`, `wfc`, `fcw`, `fwc`. Examples: `r"foo.*bar"`, `wcf"Aple"`, `cr".*Foo"`, ...) |
|
|
100
|
+
| `--timeout` | To stop the search after a specified period of time (Seconds) |
|
|
101
|
+
| `--fuzzy` | Enable fuzzy search (Highlighting and counting matches are disabled in this mode if `--word` is not enabled to prevent the program from slowing down). except when `--expr` is enabled, in which case you can make it fuzzy by putting `f` before term: `f"foo"` |
|
|
102
|
+
| `--fuzzy_level` | Similarity threshold from 0 to 99 for fuzzy search (default: `80`) |
|
|
103
|
+
| `--max-size`, `--min-size` | Specify maximum and minimum sizes for files and directories |
|
|
104
|
+
| `--archive` | Enable search within archive files (e.g. `zip`, `rar`, `7z`, `gz`, `bz2`, `xz`, `tar`, `tar.gz`, `tar.bz2`, `tar.xz`) |
|
|
105
|
+
| `--depth` | Maximum archive depth to recurse into (e.g. 2 means only 2 levels) |
|
|
106
|
+
| `--arc-ext`, `--arc-ee` | Filter by file extension inside archive files |
|
|
107
|
+
| `--arc-inc`, `--arc-exc` | Limit search results to specific set of directories or files inside archive files |
|
|
108
|
+
| `--arc-max`, `--arc-min` | Specify maximum and minimum sizes for files inside archive files (It doesn't work for directories because their size is zero in archive files) |
|
|
109
|
+
| `--rarfb` | Path to RAR backend tool (e.g. UnRAR.exe, ...) |
|
|
110
|
+
| `--full-path` | Display full path of files and directories |
|
|
111
|
+
| `--no-content` | Only display files path for content search |
|
|
109
112
|
|
|
110
113
|
## Requirements
|
|
111
114
|
|
|
112
115
|
* Python 3.6 or higher
|
|
113
|
-
* `unrar`, `bsdtar`, `unar` or `7zip` for the rarfile library to support searching inside `.rar` files (optional)
|
|
116
|
+
* `unrar`, `bsdtar`, `unar` or `7zip` for the [rarfile](https://pypi.org/project/rarfile/) library to support searching inside `.rar` files (optional)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|