1
0
mirror of https://github.com/kellyjonbrazil/jc.git synced 2025-06-17 00:07:37 +02:00
Files
jc/jc/cli.py

307 lines
7.5 KiB
Python
Raw Normal View History

2019-11-07 08:04:07 -08:00
#!/usr/bin/env python3
"""jc - JSON CLI output utility
JC cli module
"""
import sys
import os
import importlib
2019-11-07 08:04:07 -08:00
import textwrap
import signal
import json
2019-11-11 16:16:41 -08:00
import jc.utils
2020-02-05 22:26:47 -08:00
class info():
version = '1.7.3'
2020-02-05 22:26:47 -08:00
description = 'jc cli output JSON conversion tool'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
__version__ = info.version
parsers = [
'arp',
'crontab',
'crontab-u',
'df',
'dig',
'du',
'env',
'free',
'fstab',
'history',
'hosts',
'id',
'ifconfig',
'ini',
'iptables',
'jobs',
'ls',
'lsblk',
'lsmod',
'lsof',
'mount',
'netstat',
'pip-list',
'pip-show',
'ps',
'route',
'ss',
'stat',
'systemctl',
'systemctl-lj',
'systemctl-ls',
'systemctl-luf',
'uname',
'uptime',
'w',
'xml',
'yaml'
]
2020-02-05 22:26:47 -08:00
def ctrlc(signum, frame):
"""exit with error on SIGINT"""
2020-02-05 22:26:47 -08:00
sys.exit(1)
def parser_shortname(parser_argument):
2020-02-05 13:50:12 -08:00
"""short name of the parser with dashes and no -- prefix"""
return parser_argument[2:]
def parser_argument(parser):
2020-02-05 13:50:12 -08:00
"""short name of the parser with dashes and with -- prefix"""
return f'--{parser}'
def parser_mod_shortname(parser):
2020-02-05 13:50:12 -08:00
"""short name of the parser's module name (no -- prefix and dashes converted to underscores)"""
return parser.replace('--', '').replace('-', '_')
def parser_module(parser):
2020-02-13 10:03:11 -05:00
"""import the module just in time and return the module object"""
importlib.import_module('jc.parsers.' + parser_mod_shortname(parser))
return getattr(jc.parsers, parser_mod_shortname(parser))
2019-12-13 20:01:51 -08:00
2020-02-05 13:57:34 -08:00
def parsers_text(indent=0, pad=0):
2019-12-13 20:01:51 -08:00
ptext = ''
for parser in parsers:
parser_arg = parser_argument(parser)
parser_mod = parser_module(parser)
if hasattr(parser_mod, 'info'):
parser_desc = parser_mod.info.description
2020-02-05 11:08:47 -08:00
padding = pad - len(parser_arg)
2019-12-13 20:01:51 -08:00
padding_char = ' '
2020-02-05 13:57:34 -08:00
indent_text = padding_char * indent
2019-12-13 20:01:51 -08:00
padding_text = padding_char * padding
2020-02-05 13:57:34 -08:00
ptext += indent_text + parser_arg + padding_text + parser_desc + '\n'
2019-12-13 20:01:51 -08:00
return ptext
2019-12-14 23:15:15 -08:00
def about_jc():
parser_list = []
for parser in parsers:
parser_mod = parser_module(parser)
if hasattr(parser_mod, 'info'):
2019-12-16 09:00:16 -08:00
info_dict = {}
info_dict['name'] = parser_mod.__name__.split('.')[-1]
info_dict['argument'] = parser_argument(parser)
parser_entry = vars(parser_mod.info)
2019-12-16 09:00:16 -08:00
for k, v in parser_entry.items():
if not k.startswith('__'):
info_dict[k] = v
2019-12-16 09:00:16 -08:00
parser_list.append(info_dict)
2019-12-14 23:15:15 -08:00
2019-12-14 23:56:22 -08:00
return {
2019-12-16 09:08:47 -08:00
'name': 'jc',
2019-12-14 23:15:15 -08:00
'version': info.version,
'description': info.description,
'author': info.author,
'author_email': info.author_email,
2019-12-16 11:52:18 -08:00
'parser_count': len(parser_list),
2019-12-14 23:15:15 -08:00
'parsers': parser_list
}
2019-11-07 08:04:07 -08:00
def helptext(message):
2020-02-05 13:57:34 -08:00
parsers_string = parsers_text(indent=12, pad=17)
2019-12-13 20:01:51 -08:00
2019-11-07 08:04:07 -08:00
helptext_string = f'''
jc: {message}
Usage: COMMAND | jc PARSER [OPTIONS]
2019-11-07 08:04:07 -08:00
2020-02-11 19:14:51 -08:00
or
COMMAND | jc [OPTIONS] PARSER
2020-02-11 19:14:51 -08:00
or magic syntax:
2020-02-11 19:14:51 -08:00
jc [OPTIONS] COMMAND
2019-11-07 08:04:07 -08:00
Parsers:
2019-12-13 20:01:51 -08:00
{parsers_string}
2019-11-07 08:04:07 -08:00
Options:
2020-02-05 11:08:47 -08:00
-a about jc
-d debug - show trace messages
-p pretty print output
-q quiet - suppress warnings
-r raw JSON output
2019-11-07 08:04:07 -08:00
Example:
ls -al | jc --ls -p
or using the magic syntax:
2020-02-11 19:14:51 -08:00
jc -p ls -al
2019-11-07 08:04:07 -08:00
'''
print(textwrap.dedent(helptext_string), file=sys.stderr)
2019-12-14 23:15:15 -08:00
def json_out(data, pretty=False):
if pretty:
print(json.dumps(data, indent=2))
else:
print(json.dumps(data))
def magic():
2020-02-11 19:14:51 -08:00
"""Parse with magic syntax: jc -p ls -al"""
if len(sys.argv) > 1 and not sys.argv[1].startswith('--'):
parser_info = about_jc()['parsers']
# how can i get the literal text of the command entered instead of the argument list?
args_given = sys.argv[1:]
2020-02-11 19:14:51 -08:00
options = []
found_parser = None
2020-02-11 19:14:51 -08:00
# find the options
if args_given[0].startswith('-'):
p = 0
for i, arg in list(enumerate(args_given)):
# parser found
2020-02-11 19:14:51 -08:00
if arg.startswith('--'):
return
# option found
2020-02-11 19:14:51 -08:00
elif arg.startswith('-'):
options.append(args_given.pop(i - p)[1:])
2020-02-11 19:14:51 -08:00
p = p + 1
# command found
2020-02-11 19:14:51 -08:00
else:
break
# find the command and parser
for parser in parser_info:
if 'magic_commands' in parser:
for magic_command in parser['magic_commands']:
try:
if ' '.join(args_given[0:2]) == magic_command:
found_parser = parser['argument']
break
elif ''.join(args_given[0]) == magic_command:
found_parser = parser['argument']
break
except Exception:
return
run_command = ' '.join(args_given)
if found_parser:
if options:
cmd_options = '-' + ''.join(options)
else:
cmd_options = ''
whole_command = ' '.join([run_command, '|', 'jc', found_parser, cmd_options])
2020-02-11 19:14:51 -08:00
os.system(whole_command)
exit()
else:
helptext(f'parser not found for "{run_command}"')
sys.exit(1)
2019-11-07 08:04:07 -08:00
def main():
signal.signal(signal.SIGINT, ctrlc)
2020-02-12 00:16:17 -05:00
# try magic syntax first: jc -p ls -al
magic()
options = []
2019-11-11 16:16:41 -08:00
debug = False
2019-11-07 08:04:07 -08:00
pretty = False
quiet = False
raw = False
# options
for opt in sys.argv:
if opt.startswith('-') and not opt.startswith('--'):
for flag in opt[1:]:
options.append(flag)
if 'd' in options:
2019-11-11 16:16:41 -08:00
debug = True
if 'p' in options:
2019-11-07 08:04:07 -08:00
pretty = True
if 'q' in options:
2019-11-07 08:04:07 -08:00
quiet = True
if 'r' in options:
2019-11-07 08:04:07 -08:00
raw = True
if 'a' in options:
2019-12-16 08:18:37 -08:00
json_out(about_jc(), pretty=pretty)
exit()
2019-12-14 23:15:15 -08:00
2019-12-16 08:18:37 -08:00
if sys.stdin.isatty():
2019-12-14 23:15:15 -08:00
helptext('missing piped data')
2020-02-05 16:18:58 -08:00
sys.exit(1)
2019-12-14 23:15:15 -08:00
2019-12-16 08:18:37 -08:00
data = sys.stdin.read()
2019-12-14 23:15:15 -08:00
2019-11-07 08:23:11 -08:00
found = False
2019-12-16 08:18:37 -08:00
if debug:
2019-11-11 16:16:41 -08:00
for arg in sys.argv:
parser_name = parser_shortname(arg)
if parser_name in parsers:
# load parser module just in time so we don't need to load all modules
parser = parser_module(arg)
result = parser.parse(data, raw=raw, quiet=quiet)
2019-11-11 16:16:41 -08:00
found = True
break
2019-12-16 08:18:37 -08:00
else:
2019-11-11 16:16:41 -08:00
for arg in sys.argv:
parser_name = parser_shortname(arg)
if parser_name in parsers:
# load parser module just in time so we don't need to load all modules
parser = parser_module(arg)
2019-11-11 16:16:41 -08:00
try:
result = parser.parse(data, raw=raw, quiet=quiet)
2019-11-11 16:16:41 -08:00
found = True
break
except Exception:
2019-11-11 16:16:41 -08:00
jc.utils.error_message(f'{parser_name} parser could not parse the input data. Did you use the correct parser?\n For details use the -d option.')
2020-02-05 16:18:58 -08:00
sys.exit(1)
2019-11-07 08:04:07 -08:00
2019-12-16 08:18:37 -08:00
if not found:
2019-11-07 08:04:07 -08:00
helptext('missing or incorrect arguments')
2020-02-05 16:18:58 -08:00
sys.exit(1)
2019-11-07 08:04:07 -08:00
2019-12-14 23:15:15 -08:00
json_out(result, pretty=pretty)
2019-11-07 08:04:07 -08:00
if __name__ == '__main__':
main()