From fd1ca82d86649adeb84968142665e5e4fae3fa0f Mon Sep 17 00:00:00 2001 From: Kelly Brazil Date: Mon, 5 Apr 2021 17:09:22 -0700 Subject: [PATCH] add finger parser --- README.md | 1 + docs/parsers/finger.md | 102 +++++++++++++++++++++++++++ jc/cli.py | 1 + jc/parsers/finger.py | 154 +++++++++++++++++++++++++++++++++++++++++ man/jc.1 | 5 ++ 5 files changed, 263 insertions(+) create mode 100644 docs/parsers/finger.md create mode 100644 jc/parsers/finger.py diff --git a/README.md b/README.md index cc58e48f..e66f6333 100644 --- a/README.md +++ b/README.md @@ -143,6 +143,7 @@ The JSON output can be compact (default) or pretty formatted with the `-p` optio - `--du` enables the `du` command parser ([documentation](https://kellyjonbrazil.github.io/jc/docs/parsers/du)) - `--env` enables the `env` command parser ([documentation](https://kellyjonbrazil.github.io/jc/docs/parsers/env)) - `--file` enables the `file` command parser ([documentation](https://kellyjonbrazil.github.io/jc/docs/parsers/file)) +- `--finger` enables the `finger` command parser ([documentation](https://kellyjonbrazil.github.io/jc/docs/parsers/finger)) - `--free` enables the `free` command parser ([documentation](https://kellyjonbrazil.github.io/jc/docs/parsers/free)) - `--fstab` enables the `/etc/fstab` file parser ([documentation](https://kellyjonbrazil.github.io/jc/docs/parsers/fstab)) - `--group` enables the `/etc/group` file parser ([documentation](https://kellyjonbrazil.github.io/jc/docs/parsers/group)) diff --git a/docs/parsers/finger.md b/docs/parsers/finger.md new file mode 100644 index 00000000..7ea3df05 --- /dev/null +++ b/docs/parsers/finger.md @@ -0,0 +1,102 @@ + +# jc.parsers.finger +jc - JSON CLI output utility `finger` command output parser + +<> + +Usage (cli): + + $ finger | jc --finger + + or + + $ jc finger + +Usage (module): + + import jc.parsers.finger + result = jc.parsers.finger.parse(finger_command_output) + +Compatibility: + + 'linux', 'darwin', 'cygwin', 'aix', 'freebsd' + +Examples: + + $ finger | jc --finger -p + [ + { + "login": "jdoe", + "name": "John Doe", + "tty": "*tty1", + "idle": "13d", + "login_time": "Mar 22 21:14" + }, + { + "login": "jdoe", + "name": "John Doe", + "tty": "pts/0", + "idle": null, + "login_time": "Apr 5 15:33", + "details": "(192.168.1.22)" + } + ] + + +## info +```python +info() +``` + + +## process +```python +process(proc_data) +``` + +Final processing to conform to the schema. + +Parameters: + + proc_data: (List of Dictionaries) raw structured data to process + +Returns: + + List of Dictionaries. Structured data with the following schema: + + [ + { + "login": "kbrazil", + "name": "Kelly Brazil", + "tty": "*tty1", + "idle": "13d", + "login_time": "Mar 22 21:14" + }, + { + "login": "kbrazil", + "name": "Kelly Brazil", + "tty": "pts/0", + "idle": null, + "login_time": "Apr 5 15:33", + "details": "(192.168.1.221)" + } + ] + + +## parse +```python +parse(data, raw=False, quiet=False) +``` + +Main text parsing function + +Parameters: + + data: (string) text data to parse + raw: (boolean) output preprocessed JSON if True + quiet: (boolean) suppress warning messages if True + +Returns: + + List of Dictionaries. Raw or processed structured data. + diff --git a/jc/cli.py b/jc/cli.py index 0ef787f7..2bff8b3f 100644 --- a/jc/cli.py +++ b/jc/cli.py @@ -57,6 +57,7 @@ parsers = [ 'du', 'env', 'file', + 'finger', 'free', 'fstab', 'group', diff --git a/jc/parsers/finger.py b/jc/parsers/finger.py new file mode 100644 index 00000000..651f3b65 --- /dev/null +++ b/jc/parsers/finger.py @@ -0,0 +1,154 @@ +"""jc - JSON CLI output utility `finger` command output parser + +<> + +Usage (cli): + + $ finger | jc --finger + + or + + $ jc finger + +Usage (module): + + import jc.parsers.finger + result = jc.parsers.finger.parse(finger_command_output) + +Compatibility: + + 'linux', 'darwin', 'cygwin', 'aix', 'freebsd' + +Examples: + + $ finger | jc --finger -p + [ + { + "login": "jdoe", + "name": "John Doe", + "tty": "*tty1", + "idle": "13d", + "login_time": "Mar 22 21:14" + }, + { + "login": "jdoe", + "name": "John Doe", + "tty": "pts/0", + "idle": null, + "login_time": "Apr 5 15:33", + "details": "(192.168.1.22)" + } + ] +""" +import re +import jc.utils +import jc.parsers.universal + + +class info(): + version = '1.0' + description = '`finger` command parser' + author = 'Kelly Brazil' + author_email = 'kellyjonbrazil@gmail.com' + # details = 'enter any other details here' + + # compatible options: linux, darwin, cygwin, win32, aix, freebsd + compatible = ['linux', 'darwin', 'cygwin', 'aix', 'freebsd'] + magic_commands = ['finger'] + + +__version__ = info.version + + +def process(proc_data): + """ + Final processing to conform to the schema. + + Parameters: + + proc_data: (List of Dictionaries) raw structured data to process + + Returns: + + List of Dictionaries. Structured data with the following schema: + + [ + { + "login": "kbrazil", + "name": "Kelly Brazil", + "tty": "*tty1", + "idle": "13d", + "login_time": "Mar 22 21:14" + }, + { + "login": "kbrazil", + "name": "Kelly Brazil", + "tty": "pts/0", + "idle": null, + "login_time": "Apr 5 15:33", + "details": "(192.168.1.221)" + } + ] + """ + + # rebuild output for added semantic information + return proc_data + + +def parse(data, raw=False, quiet=False): + """ + Main text parsing function + + Parameters: + + data: (string) text data to parse + raw: (boolean) output preprocessed JSON if True + quiet: (boolean) suppress warning messages if True + + Returns: + + List of Dictionaries. Raw or processed structured data. + """ + if not quiet: + jc.utils.compatibility(__name__, info.compatible) + + raw_output = [] + + if jc.utils.has_data(data): + # Finger output is an abomination that is nearly unparsable. But there is a way: + # First find the location of the last character of 'Idle' in the table and cut + # all lines at that spot. Data before that spot can use the unviversal.sparse_table_parse function. + # All data after that spot can be run through regex to find the login datetime and possibly + # other fields. + + data_lines = list(filter(None, data.splitlines())) + sep_col = data_lines[0].find('Idle') + 4 + first_half = [] + second_half = [] + + for line in data_lines: + first_half.append(line[:sep_col]) + second_half.append(line[sep_col:]) + + first_half[0] = first_half[0].lower() + + # parse the first half + raw_output = jc.parsers.universal.sparse_table_parse(first_half) + + # use regex to get login datetime and 'other' data + pattern = re.compile(r'([A-Z][a-z]{2}\s+\d{1,2}\s+\d\d:\d\d)(\s+\S+)?') + + # remove header row from list + second_half.pop(0) + + for index, line in enumerate(second_half): + dt = re.search(pattern, line) + if dt.group(1): + raw_output[index]['login_time'] = dt.group(1).strip() + if dt.group(2): + raw_output[index]['details'] = dt.group(2).strip() + + if raw: + return raw_output + else: + return process(raw_output) diff --git a/man/jc.1 b/man/jc.1 index ae4a2324..1d2d4b17 100644 --- a/man/jc.1 +++ b/man/jc.1 @@ -107,6 +107,11 @@ CSV file parser \fB--file\fP `file` command parser +.TP +.B +\fB--finger\fP +`finger` command parser + .TP .B \fB--free\fP