1
0
mirror of https://github.com/kellyjonbrazil/jc.git synced 2025-06-17 00:07:37 +02:00
Files
jc/jc/parsers/finger.py
2021-11-29 16:29:23 -08:00

215 lines
5.9 KiB
Python

"""jc - JSON CLI output utility `finger` command output parser
Supports `-s` output option. Does not support the `-l` detail option.
Usage (cli):
$ finger | jc --finger
or
$ jc finger
Usage (module):
import jc.parsers.finger
result = jc.parsers.finger.parse(finger_command_output)
Schema:
[
{
"login": string,
"name": string,
"tty": string,
"idle": string, # null if empty
"login_time": string,
"details": string,
"tty_writeable": boolean,
"idle_minutes": integer,
"idle_hours": integer,
"idle_days": integer,
"total_idle_minutes": integer
}
]
Examples:
$ finger | jc --finger -p
[
{
"login": "jdoe",
"name": "John Doe",
"tty": "tty1",
"idle": "14d",
"login_time": "Mar 22 21:14",
"tty_writeable": false,
"idle_minutes": 0,
"idle_hours": 0,
"idle_days": 14,
"total_idle_minutes": 20160
},
{
"login": "jdoe",
"name": "John Doe",
"tty": "pts/0",
"idle": null,
"login_time": "Apr 5 15:33",
"details": "(192.168.1.22)",
"tty_writeable": true,
"idle_minutes": 0,
"idle_hours": 0,
"idle_days": 0,
"total_idle_minutes": 0
},
...
]
$ finger | jc --finger -p -r
[
{
"login": "jdoe",
"name": "John Doe",
"tty": "*tty1",
"idle": "14d",
"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():
"""Provides parser metadata (version, author, etc.)"""
version = '1.2'
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', '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 to conform to the schema.
"""
for entry in proc_data:
if 'tty' in entry:
entry['tty_writeable'] = True
if '*' in entry['tty']:
entry['tty'] = entry['tty'].replace('*', '')
entry['tty_writeable'] = False
if 'idle' in entry:
entry['idle_minutes'] = 0
entry['idle_hours'] = 0
entry['idle_days'] = 0
if entry['idle'] == '-':
entry['idle'] = None
if entry['idle'] and entry['idle'].isnumeric():
entry['idle_minutes'] = int(entry['idle'])
if entry['idle'] and ':' in entry['idle']:
entry['idle_hours'] = int(entry['idle'].split(':')[0])
entry['idle_minutes'] = int(entry['idle'].split(':')[1])
if entry['idle'] and 'd' in entry['idle']:
entry['idle_days'] = int(entry['idle'].replace('d', ''))
entry['total_idle_minutes'] = (entry['idle_days'] * 1440) + \
(entry['idle_hours'] * 60) + \
entry['idle_minutes']
if 'details' in entry:
if not entry['details']:
del entry['details']
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)
if not isinstance(data, str): raise TypeError("Input data must be a 'str' object.")
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|\d{4})(\s?.+)?$')
# remove header row from list
second_half.pop(0)
for index, line in enumerate(second_half):
dt = re.search(pattern, line)
if dt:
if dt.group(1) and dt.group(2):
raw_output[index]['login_time'] = dt.group(1).strip() + ' ' + dt.group(2).strip()
if dt.group(3):
raw_output[index]['details'] = dt.group(3).strip()
if raw:
return raw_output
else:
return _process(raw_output)