Compare commits
7 Commits
0d0cfac860
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 5381381081 | |||
| 47e6ee9ca8 | |||
| 33b91986a1 | |||
| ba159e8021 | |||
| 6b2bedefe5 | |||
| d0dd2cff17 | |||
| 3a75db3b49 |
21
LICENSE
Normal file
21
LICENSE
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2022 Nicolò Paraciani
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
27
README.md
27
README.md
@@ -6,30 +6,31 @@ Then this is for you...
|
|||||||
|
|
||||||
## What is this?
|
## What is this?
|
||||||
|
|
||||||
`crapgrep` is a poor imitation of the Unix utility `grep` written in Python.
|
`crapgrep` is a poor imitation of the Unix utility `grep`, written in Python.
|
||||||
|
|
||||||
More specifically, it's a command line tool to search for a pattern or a substring in each line of one or more text files.
|
More specifically, it's a command line tool to search for a pattern or a substring in each line of one or more text files.
|
||||||
|
|
||||||
The command synopsis is very similar to `grep`, although it needs the Python interpreter to be invoked explicitly (at the moment, at least):
|
The command synopsis is very similar to `grep`'s, although it needs the Python interpreter to be invoked explicitly (at the moment, at least):
|
||||||
|
|
||||||
```
|
```
|
||||||
python crapgrep.py [OPTIONS] [PATTERN] [FILE[...]]
|
python crapgrep.py [OPTION...] [PATTERN] [FILE...]
|
||||||
```
|
```
|
||||||
|
|
||||||
_**Note**: obviously, `crapgrep` doesn't read from `stdin`, which makes it even more useless..._
|
> _**Note**: obviously, `crapgrep` doesn't read from `stdin`, which makes it even more useless..._
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
If you like wasting time because apparently you have nothing better to do, you can try `crapgrep` by cloning the repository with `git`:
|
If you like wasting time (because apparently you have nothing better to do), you can try `crapgrep` by cloning the repository with `git`:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git clone https://baltig.cnr.it//nicolo.paraciani//crapgrep.git
|
git clone https://baltig.cnr.it/nicolo.paraciani/crapgrep.git
|
||||||
```
|
```
|
||||||
|
|
||||||
then
|
then
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd crapgrep
|
cd crapgrep/crapgrep
|
||||||
python crapgrep.py <option> <file(s)>
|
python crapgrep.py <options> <files>
|
||||||
```
|
```
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
@@ -44,15 +45,15 @@ Searching for the simple string `'hola'` in file `garbage.txt` in the current di
|
|||||||
python crapgrep.py hola garbage.txt
|
python crapgrep.py hola garbage.txt
|
||||||
```
|
```
|
||||||
|
|
||||||
To make the search case-insensitive, you could do:
|
To make the search case-insensitive:
|
||||||
|
|
||||||
```
|
```
|
||||||
python crapgrep.py -i hOlA garbage.txt
|
python crapgrep.py -i hola garbage.txt
|
||||||
```
|
```
|
||||||
|
|
||||||
### Regexp
|
### Regexp
|
||||||
|
|
||||||
_**Note**: unlike `grep`, `crapgrep` doesn't treat the string as a regular expression pattern by default, it must be specified by passing the `-E` option explicitly._
|
_**Note**: unlike `grep`, `crapgrep` doesn't treat the pattern as a regular expression by default, it must be specified by passing the `-E` option explicitly._
|
||||||
|
|
||||||
Searching for pattern `'^urka[0-9]'` in files `garbage1.txt` and `garbage2.txt` in the parent directory:
|
Searching for pattern `'^urka[0-9]'` in files `garbage1.txt` and `garbage2.txt` in the parent directory:
|
||||||
|
|
||||||
@@ -96,7 +97,9 @@ If you're asking yourself:
|
|||||||
|
|
||||||
> _Why would you inflict something like this upon the world?_
|
> _Why would you inflict something like this upon the world?_
|
||||||
|
|
||||||
the answer is... _because it's fun!_
|
the answer is...
|
||||||
|
|
||||||
|
> _because it's fun!_
|
||||||
|
|
||||||
## TODO
|
## TODO
|
||||||
|
|
||||||
|
|||||||
@@ -2,17 +2,19 @@ import exceptions
|
|||||||
import re
|
import re
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import sty
|
|
||||||
|
|
||||||
|
|
||||||
# TODO Avoid extra fname argument??
|
def find_line(line: str, pattern: str, i_case: bool, is_regexp=False) -> bool:
|
||||||
def find_line(line: str, pattern: str, is_regexp=False) -> bool:
|
|
||||||
"""
|
"""
|
||||||
Distinguish between regexp or not (default is False)
|
Check if line contains matches for pattern, with case and regexp flags.
|
||||||
|
Returns True if pattern is found in line.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
found = False
|
found = False
|
||||||
|
|
||||||
|
if i_case:
|
||||||
|
line = line.lower()
|
||||||
|
pattern = pattern.lower()
|
||||||
|
|
||||||
if is_regexp:
|
if is_regexp:
|
||||||
matches = re.findall(pattern, line)
|
matches = re.findall(pattern, line)
|
||||||
FIND_COND = len(matches) > 0
|
FIND_COND = len(matches) > 0
|
||||||
@@ -71,12 +73,12 @@ def parse_args(args: list) -> dict:
|
|||||||
Parse cli arguments and return the tree as a dict
|
Parse cli arguments and return the tree as a dict
|
||||||
"""
|
"""
|
||||||
# TODO move to class
|
# TODO move to class
|
||||||
OPTS = [
|
OPTS = (
|
||||||
'i', # Case-insensitive search
|
'i', # Case-insensitive search
|
||||||
'E', # Pattern is a full regexp
|
'E', # Pattern is a full regexp
|
||||||
'r', # Search recursively in dir
|
'r', # Search recursively in dir
|
||||||
'n', # Print line numbers
|
'n', # Print line numbers
|
||||||
]
|
)
|
||||||
# TODO Handle extended options? (e.g. --ignore-case)
|
# TODO Handle extended options? (e.g. --ignore-case)
|
||||||
LONG_OPTS = [
|
LONG_OPTS = [
|
||||||
'--ignore-case',
|
'--ignore-case',
|
||||||
@@ -93,7 +95,7 @@ def parse_args(args: list) -> dict:
|
|||||||
# TODO does it make sense?
|
# TODO does it make sense?
|
||||||
for opt in options:
|
for opt in options:
|
||||||
if opt not in OPTS:
|
if opt not in OPTS:
|
||||||
raise exceptions.InvalidOption(opt[1:])
|
raise exceptions.InvalidOptionError(opt)
|
||||||
|
|
||||||
args_tree["options"] = options
|
args_tree["options"] = options
|
||||||
|
|
||||||
@@ -113,22 +115,13 @@ def parse_args(args: list) -> dict:
|
|||||||
return args_tree
|
return args_tree
|
||||||
|
|
||||||
|
|
||||||
def match_regexp(pattern: str, line: str) -> object:
|
|
||||||
"""
|
|
||||||
Search for pattern in the given line and return
|
|
||||||
a match object or None if no match found.
|
|
||||||
|
|
||||||
Flags??
|
|
||||||
"""
|
|
||||||
return re.search(pattern, line)
|
|
||||||
|
|
||||||
|
|
||||||
def process_grep(args_tree: dict) -> list:
|
def process_grep(args_tree: dict) -> list:
|
||||||
lines = []
|
lines = []
|
||||||
found_lines = []
|
found_lines = []
|
||||||
options = args_tree["options"]
|
options = args_tree["options"]
|
||||||
pattern = args_tree["pattern"]
|
pattern = args_tree["pattern"]
|
||||||
files = args_tree["files"]
|
files = args_tree["files"]
|
||||||
|
i_case = False
|
||||||
|
|
||||||
try:
|
try:
|
||||||
for f in files:
|
for f in files:
|
||||||
@@ -139,19 +132,19 @@ def process_grep(args_tree: dict) -> list:
|
|||||||
|
|
||||||
n = 0
|
n = 0
|
||||||
|
|
||||||
# Check if case insensitive
|
|
||||||
if 'i' in options:
|
|
||||||
pattern = pattern.lower()
|
|
||||||
for line in lines:
|
for line in lines:
|
||||||
n = n + 1
|
n = n + 1
|
||||||
|
# Check if case insensitive
|
||||||
|
if 'i' in options:
|
||||||
|
i_case = True
|
||||||
# Check if regexp flag
|
# Check if regexp flag
|
||||||
if 'E' in options:
|
if 'E' in options:
|
||||||
# pattern is regexp
|
# pattern is regexp
|
||||||
if not check_pattern(pattern):
|
if not check_pattern(pattern):
|
||||||
raise exceptions.InvalidPattern(pattern)
|
raise exceptions.InvalidPatternError(pattern)
|
||||||
found = find_line(line, pattern, True)
|
found = find_line(line, pattern, i_case, True)
|
||||||
else:
|
else:
|
||||||
found = find_line(line, pattern)
|
found = find_line(line, pattern, i_case)
|
||||||
|
|
||||||
lnum = str(n) + ':' if 'n' in options else ''
|
lnum = str(n) + ':' if 'n' in options else ''
|
||||||
|
|
||||||
@@ -165,8 +158,7 @@ def process_grep(args_tree: dict) -> list:
|
|||||||
return found_lines
|
return found_lines
|
||||||
|
|
||||||
|
|
||||||
# When the script is executed directly...
|
def main():
|
||||||
if __name__ == "__main__":
|
|
||||||
args = sys.argv[1:]
|
args = sys.argv[1:]
|
||||||
|
|
||||||
if not check_args(args):
|
if not check_args(args):
|
||||||
@@ -175,7 +167,7 @@ if __name__ == "__main__":
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
args_tree = parse_args(args)
|
args_tree = parse_args(args)
|
||||||
except exceptions.InvalidOption as e:
|
except exceptions.InvalidOptionError as e:
|
||||||
print(e.get_message())
|
print(e.get_message())
|
||||||
print_usage()
|
print_usage()
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
@@ -183,6 +175,13 @@ if __name__ == "__main__":
|
|||||||
try:
|
try:
|
||||||
for line in process_grep(args_tree):
|
for line in process_grep(args_tree):
|
||||||
print(line)
|
print(line)
|
||||||
except exceptions.InvalidPattern as e:
|
except exceptions.InvalidPatternError as e:
|
||||||
print(e.get_message())
|
print(e.get_message())
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
|
||||||
|
# When the script is executed directly...
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
class InvalidOption(Exception):
|
class InvalidOptionError(Exception):
|
||||||
def __init__(self, invalid_opt):
|
def __init__(self, invalid_opt):
|
||||||
self.invalid_opt = invalid_opt
|
self.invalid_opt = invalid_opt
|
||||||
|
|
||||||
@@ -6,7 +6,7 @@ class InvalidOption(Exception):
|
|||||||
return f"crapgrep: invalid option -- '{self.invalid_opt}'"
|
return f"crapgrep: invalid option -- '{self.invalid_opt}'"
|
||||||
|
|
||||||
|
|
||||||
class InvalidPattern(Exception):
|
class InvalidPatternError(Exception):
|
||||||
def __init__(self, pattern):
|
def __init__(self, pattern):
|
||||||
self.invalid = pattern
|
self.invalid = pattern
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user