From fe27dcdb8f72b5a288f978b87be2eb930099543e Mon Sep 17 00:00:00 2001 From: Kelly Brazil Date: Tue, 11 Feb 2020 12:16:23 -0800 Subject: [PATCH 01/27] proof of concept for magic syntax (e.g. jc ls -al) --- jc/cli.py | 134 +++++++++++++++++++---------- jc/cli.py.old | 230 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 319 insertions(+), 45 deletions(-) create mode 100644 jc/cli.py.old diff --git a/jc/cli.py b/jc/cli.py index 15f3774a..a95d0ddb 100644 --- a/jc/cli.py +++ b/jc/cli.py @@ -3,6 +3,7 @@ JC cli module """ import sys +import os import importlib import textwrap import signal @@ -11,7 +12,7 @@ import jc.utils class info(): - version = '1.7.2' + version = '1.8.0' description = 'jc cli output JSON conversion tool' author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' @@ -19,48 +20,51 @@ class info(): __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' -] +# map of parser real-name -> matching command name for magic syntax +# use lists as the value, then when reversing, loop through the list to create the new dict +parsers = { + 'arp': 'arp', + 'crontab': 'crontab', + 'crontab-u': None, + 'df': 'df', + 'dig': 'dig', + 'du': 'du', + 'env': 'env', + 'free': 'free', + 'fstab': None, # might need this command for linux + 'history': 'history', + 'hosts': None, + 'id': 'id', + 'ifconfig': 'ifconfig', + 'ini': None, + 'iptables': 'iptables', + 'jobs': 'jobs', + 'ls': 'ls', + 'lsblk': 'lsblk', + 'lsmod': 'lsmod', + 'lsof': 'lsof', + 'mount': 'mount', + 'netstat': 'netstat', + 'pip-list': 'pip3 list', + 'pip-show': 'pip3 show', + 'ps': 'ps', + 'route': 'route', + 'ss': 'ss', + 'stat': 'stat', + 'systemctl': 'systemctl', + 'systemctl-lj': 'systemctl list-jobs', + 'systemctl-ls': 'systemctl list-sockets', + 'systemctl-luf': 'systemctl list-unit-files', + 'uname': 'uname -a', + 'uptime': 'uptime', + 'w': 'w', + 'xml': None, + 'yaml': None +} def ctrlc(signum, frame): + """exit with error on SIGINT""" sys.exit(1) @@ -87,7 +91,7 @@ def parser_module(parser): def parsers_text(indent=0, pad=0): ptext = '' - for parser in parsers: + for parser in parsers.keys(): parser_arg = parser_argument(parser) parser_mod = parser_module(parser) @@ -105,7 +109,7 @@ def parsers_text(indent=0, pad=0): def about_jc(): parser_list = [] - for parser in parsers: + for parser in parsers.keys(): parser_mod = parser_module(parser) if hasattr(parser_mod, 'info'): @@ -161,9 +165,49 @@ def json_out(data, pretty=False): print(json.dumps(data)) +def magic(): + """Parse with magic syntax: jc ls -al""" + if len(sys.argv) > 1 and not sys.argv[1].startswith('-'): + # reverse the parser dictionary keys and values + commands = {v: k for k, v in parsers.items() if v is not None} + args_given = sys.argv[1:] + options = [] + found_parser = None + + # first create a list of options from the commands dict based on the arguments passed + for comm, pars in commands.items(): + if args_given[0] == comm.split()[0]: + options.append([comm, pars]) + + if len(options) > 1: + for comm2, pars2 in options: + if args_given[1] == comm2.split()[1]: + found_parser = pars2 + else: + try: + found_parser = options[0][1] + except Exception: + found_parser = None + + # run the command through the parser + run_command = ' '.join(sys.argv[1:]) + whole_command = [run_command, '|', 'jc', parser_argument(found_parser), '-p'] + + if found_parser is not None: + os.system(' '.join(whole_command)) + exit() + else: + args_given_pretty = ' '.join(args_given) + helptext(f'parser not found for "{args_given_pretty}"') + sys.exit(1) + + def main(): signal.signal(signal.SIGINT, ctrlc) + # try magic syntax first + magic() + debug = False pretty = False quiet = False @@ -198,7 +242,7 @@ def main(): for arg in sys.argv: parser_name = parser_shortname(arg) - if parser_name in parsers: + if parser_name in parsers.keys(): # 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) @@ -208,14 +252,14 @@ def main(): for arg in sys.argv: parser_name = parser_shortname(arg) - if parser_name in parsers: + if parser_name in parsers.keys(): # load parser module just in time so we don't need to load all modules parser = parser_module(arg) try: result = parser.parse(data, raw=raw, quiet=quiet) found = True break - except: + except Exception: 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.') sys.exit(1) diff --git a/jc/cli.py.old b/jc/cli.py.old new file mode 100644 index 00000000..15f3774a --- /dev/null +++ b/jc/cli.py.old @@ -0,0 +1,230 @@ +#!/usr/bin/env python3 +"""jc - JSON CLI output utility +JC cli module +""" +import sys +import importlib +import textwrap +import signal +import json +import jc.utils + + +class info(): + version = '1.7.2' + 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' +] + + +def ctrlc(signum, frame): + sys.exit(1) + + +def parser_shortname(parser_argument): + """short name of the parser with dashes and no -- prefix""" + return parser_argument[2:] + + +def parser_argument(parser): + """short name of the parser with dashes and with -- prefix""" + return f'--{parser}' + + +def parser_mod_shortname(parser): + """short name of the parser's module name (no -- prefix and dashes converted to underscores)""" + return parser.replace('--', '').replace('-', '_') + + +def parser_module(parser): + """import the module just in time and present the module object""" + importlib.import_module('jc.parsers.' + parser_mod_shortname(parser)) + return getattr(jc.parsers, parser_mod_shortname(parser)) + + +def parsers_text(indent=0, pad=0): + 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 + padding = pad - len(parser_arg) + padding_char = ' ' + indent_text = padding_char * indent + padding_text = padding_char * padding + ptext += indent_text + parser_arg + padding_text + parser_desc + '\n' + + return ptext + + +def about_jc(): + parser_list = [] + + for parser in parsers: + parser_mod = parser_module(parser) + + if hasattr(parser_mod, 'info'): + info_dict = {} + info_dict['name'] = parser_mod.__name__.split('.')[-1] + info_dict['argument'] = parser_argument(parser) + parser_entry = vars(parser_mod.info) + + for k, v in parser_entry.items(): + if not k.startswith('__'): + info_dict[k] = v + + parser_list.append(info_dict) + + return { + 'name': 'jc', + 'version': info.version, + 'description': info.description, + 'author': info.author, + 'author_email': info.author_email, + 'parser_count': len(parser_list), + 'parsers': parser_list + } + + +def helptext(message): + parsers_string = parsers_text(indent=12, pad=17) + + helptext_string = f''' + jc: {message} + + Usage: jc PARSER [OPTIONS] + + Parsers: +{parsers_string} + Options: + -a about jc + -d debug - show trace messages + -p pretty print output + -q quiet - suppress warnings + -r raw JSON output + + Example: + ls -al | jc --ls -p + ''' + print(textwrap.dedent(helptext_string), file=sys.stderr) + + +def json_out(data, pretty=False): + if pretty: + print(json.dumps(data, indent=2)) + else: + print(json.dumps(data)) + + +def main(): + signal.signal(signal.SIGINT, ctrlc) + + debug = False + pretty = False + quiet = False + raw = False + + # options + if '-d' in sys.argv: + debug = True + + if '-p' in sys.argv: + pretty = True + + if '-q' in sys.argv: + quiet = True + + if '-r' in sys.argv: + raw = True + + if '-a' in sys.argv: + json_out(about_jc(), pretty=pretty) + exit() + + if sys.stdin.isatty(): + helptext('missing piped data') + sys.exit(1) + + data = sys.stdin.read() + + found = False + + if debug: + 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) + found = True + break + else: + 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) + try: + result = parser.parse(data, raw=raw, quiet=quiet) + found = True + break + except: + 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.') + sys.exit(1) + + if not found: + helptext('missing or incorrect arguments') + sys.exit(1) + + json_out(result, pretty=pretty) + + +if __name__ == '__main__': + main() From a2c137df2e6d3b133df5df0bb3f9b1ca69990557 Mon Sep 17 00:00:00 2001 From: Kelly Brazil Date: Tue, 11 Feb 2020 18:08:37 -0800 Subject: [PATCH 02/27] better magic command syntax logic using introspection information from parser modules --- jc/cli.py | 133 ++++++++++++++--------------- jc/cli.py.old | 230 -------------------------------------------------- 2 files changed, 65 insertions(+), 298 deletions(-) delete mode 100644 jc/cli.py.old diff --git a/jc/cli.py b/jc/cli.py index a95d0ddb..2f1f52df 100644 --- a/jc/cli.py +++ b/jc/cli.py @@ -12,7 +12,7 @@ import jc.utils class info(): - version = '1.8.0' + version = '1.7.3' description = 'jc cli output JSON conversion tool' author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' @@ -20,47 +20,45 @@ class info(): __version__ = info.version -# map of parser real-name -> matching command name for magic syntax -# use lists as the value, then when reversing, loop through the list to create the new dict -parsers = { - 'arp': 'arp', - 'crontab': 'crontab', - 'crontab-u': None, - 'df': 'df', - 'dig': 'dig', - 'du': 'du', - 'env': 'env', - 'free': 'free', - 'fstab': None, # might need this command for linux - 'history': 'history', - 'hosts': None, - 'id': 'id', - 'ifconfig': 'ifconfig', - 'ini': None, - 'iptables': 'iptables', - 'jobs': 'jobs', - 'ls': 'ls', - 'lsblk': 'lsblk', - 'lsmod': 'lsmod', - 'lsof': 'lsof', - 'mount': 'mount', - 'netstat': 'netstat', - 'pip-list': 'pip3 list', - 'pip-show': 'pip3 show', - 'ps': 'ps', - 'route': 'route', - 'ss': 'ss', - 'stat': 'stat', - 'systemctl': 'systemctl', - 'systemctl-lj': 'systemctl list-jobs', - 'systemctl-ls': 'systemctl list-sockets', - 'systemctl-luf': 'systemctl list-unit-files', - 'uname': 'uname -a', - 'uptime': 'uptime', - 'w': 'w', - 'xml': None, - 'yaml': None -} +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' +] def ctrlc(signum, frame): @@ -91,7 +89,7 @@ def parser_module(parser): def parsers_text(indent=0, pad=0): ptext = '' - for parser in parsers.keys(): + for parser in parsers: parser_arg = parser_argument(parser) parser_mod = parser_module(parser) @@ -109,7 +107,7 @@ def parsers_text(indent=0, pad=0): def about_jc(): parser_list = [] - for parser in parsers.keys(): + for parser in parsers: parser_mod = parser_module(parser) if hasattr(parser_mod, 'info'): @@ -143,6 +141,10 @@ def helptext(message): Usage: jc PARSER [OPTIONS] + or magic syntax: + + jc COMMAND + Parsers: {parsers_string} Options: @@ -154,6 +156,10 @@ def helptext(message): Example: ls -al | jc --ls -p + + or using the magic syntax: + + jc ls -al ''' print(textwrap.dedent(helptext_string), file=sys.stderr) @@ -168,32 +174,23 @@ def json_out(data, pretty=False): def magic(): """Parse with magic syntax: jc ls -al""" if len(sys.argv) > 1 and not sys.argv[1].startswith('-'): - # reverse the parser dictionary keys and values - commands = {v: k for k, v in parsers.items() if v is not None} + parser_info = about_jc()['parsers'] args_given = sys.argv[1:] - options = [] found_parser = None - # first create a list of options from the commands dict based on the arguments passed - for comm, pars in commands.items(): - if args_given[0] == comm.split()[0]: - options.append([comm, pars]) + for parser in parser_info: + if 'magic_commands' in parser: + for magic_command in parser['magic_commands']: + 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 - if len(options) > 1: - for comm2, pars2 in options: - if args_given[1] == comm2.split()[1]: - found_parser = pars2 - else: - try: - found_parser = options[0][1] - except Exception: - found_parser = None - - # run the command through the parser - run_command = ' '.join(sys.argv[1:]) - whole_command = [run_command, '|', 'jc', parser_argument(found_parser), '-p'] - - if found_parser is not None: + if found_parser: + run_command = ' '.join(sys.argv[1:]) + whole_command = [run_command, '|', 'jc', found_parser, '-p'] os.system(' '.join(whole_command)) exit() else: @@ -242,7 +239,7 @@ def main(): for arg in sys.argv: parser_name = parser_shortname(arg) - if parser_name in parsers.keys(): + 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) @@ -252,7 +249,7 @@ def main(): for arg in sys.argv: parser_name = parser_shortname(arg) - if parser_name in parsers.keys(): + if parser_name in parsers: # load parser module just in time so we don't need to load all modules parser = parser_module(arg) try: diff --git a/jc/cli.py.old b/jc/cli.py.old deleted file mode 100644 index 15f3774a..00000000 --- a/jc/cli.py.old +++ /dev/null @@ -1,230 +0,0 @@ -#!/usr/bin/env python3 -"""jc - JSON CLI output utility -JC cli module -""" -import sys -import importlib -import textwrap -import signal -import json -import jc.utils - - -class info(): - version = '1.7.2' - 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' -] - - -def ctrlc(signum, frame): - sys.exit(1) - - -def parser_shortname(parser_argument): - """short name of the parser with dashes and no -- prefix""" - return parser_argument[2:] - - -def parser_argument(parser): - """short name of the parser with dashes and with -- prefix""" - return f'--{parser}' - - -def parser_mod_shortname(parser): - """short name of the parser's module name (no -- prefix and dashes converted to underscores)""" - return parser.replace('--', '').replace('-', '_') - - -def parser_module(parser): - """import the module just in time and present the module object""" - importlib.import_module('jc.parsers.' + parser_mod_shortname(parser)) - return getattr(jc.parsers, parser_mod_shortname(parser)) - - -def parsers_text(indent=0, pad=0): - 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 - padding = pad - len(parser_arg) - padding_char = ' ' - indent_text = padding_char * indent - padding_text = padding_char * padding - ptext += indent_text + parser_arg + padding_text + parser_desc + '\n' - - return ptext - - -def about_jc(): - parser_list = [] - - for parser in parsers: - parser_mod = parser_module(parser) - - if hasattr(parser_mod, 'info'): - info_dict = {} - info_dict['name'] = parser_mod.__name__.split('.')[-1] - info_dict['argument'] = parser_argument(parser) - parser_entry = vars(parser_mod.info) - - for k, v in parser_entry.items(): - if not k.startswith('__'): - info_dict[k] = v - - parser_list.append(info_dict) - - return { - 'name': 'jc', - 'version': info.version, - 'description': info.description, - 'author': info.author, - 'author_email': info.author_email, - 'parser_count': len(parser_list), - 'parsers': parser_list - } - - -def helptext(message): - parsers_string = parsers_text(indent=12, pad=17) - - helptext_string = f''' - jc: {message} - - Usage: jc PARSER [OPTIONS] - - Parsers: -{parsers_string} - Options: - -a about jc - -d debug - show trace messages - -p pretty print output - -q quiet - suppress warnings - -r raw JSON output - - Example: - ls -al | jc --ls -p - ''' - print(textwrap.dedent(helptext_string), file=sys.stderr) - - -def json_out(data, pretty=False): - if pretty: - print(json.dumps(data, indent=2)) - else: - print(json.dumps(data)) - - -def main(): - signal.signal(signal.SIGINT, ctrlc) - - debug = False - pretty = False - quiet = False - raw = False - - # options - if '-d' in sys.argv: - debug = True - - if '-p' in sys.argv: - pretty = True - - if '-q' in sys.argv: - quiet = True - - if '-r' in sys.argv: - raw = True - - if '-a' in sys.argv: - json_out(about_jc(), pretty=pretty) - exit() - - if sys.stdin.isatty(): - helptext('missing piped data') - sys.exit(1) - - data = sys.stdin.read() - - found = False - - if debug: - 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) - found = True - break - else: - 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) - try: - result = parser.parse(data, raw=raw, quiet=quiet) - found = True - break - except: - 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.') - sys.exit(1) - - if not found: - helptext('missing or incorrect arguments') - sys.exit(1) - - json_out(result, pretty=pretty) - - -if __name__ == '__main__': - main() From 08c1e2aec9d6bb68653dc12ba2272535fb7cef09 Mon Sep 17 00:00:00 2001 From: Kelly Brazil Date: Tue, 11 Feb 2020 18:08:59 -0800 Subject: [PATCH 03/27] add magic syntax --- changelog.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/changelog.txt b/changelog.txt index 945e2c71..5c67dbb8 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,8 @@ jc changelog +20200211 v1.7.3 +- Add alternative 'magic' syntax: e.g. `jc ls -al` + 20200208 v1.7.2 - Include test fixtures in wheel From d49323e4ebf2a8aebd1d8ea65ef0854ee8bcb29f Mon Sep 17 00:00:00 2001 From: Kelly Brazil Date: Tue, 11 Feb 2020 18:09:21 -0800 Subject: [PATCH 04/27] add magic_commands list to info --- jc/parsers/arp.py | 1 + jc/parsers/crontab.py | 1 + jc/parsers/df.py | 1 + jc/parsers/dig.py | 1 + jc/parsers/du.py | 1 + jc/parsers/env.py | 1 + jc/parsers/foo.py | 1 + jc/parsers/free.py | 1 + jc/parsers/fstab.py | 1 + jc/parsers/history.py | 1 + jc/parsers/id.py | 1 + jc/parsers/ifconfig.py | 1 + jc/parsers/iptables.py | 1 + jc/parsers/jobs.py | 1 + jc/parsers/ls.py | 1 + jc/parsers/lsblk.py | 1 + jc/parsers/lsmod.py | 1 + jc/parsers/lsof.py | 1 + jc/parsers/mount.py | 1 + jc/parsers/netstat.py | 1 + jc/parsers/pip_list.py | 1 + jc/parsers/pip_show.py | 1 + jc/parsers/ps.py | 1 + jc/parsers/route.py | 1 + jc/parsers/ss.py | 1 + jc/parsers/stat.py | 1 + jc/parsers/systemctl.py | 1 + jc/parsers/systemctl_lj.py | 1 + jc/parsers/systemctl_ls.py | 1 + jc/parsers/systemctl_luf.py | 1 + jc/parsers/uname.py | 1 + jc/parsers/uptime.py | 1 + jc/parsers/w.py | 1 + 33 files changed, 33 insertions(+) diff --git a/jc/parsers/arp.py b/jc/parsers/arp.py index 87960000..ae4a016c 100644 --- a/jc/parsers/arp.py +++ b/jc/parsers/arp.py @@ -98,6 +98,7 @@ class info(): # compatible options: linux, darwin, cygwin, win32, aix, freebsd compatible = ['linux', 'aix', 'freebsd', 'darwin'] + magic_commands = ['arp'] __version__ = info.version diff --git a/jc/parsers/crontab.py b/jc/parsers/crontab.py index 4336b0ef..8acfacb6 100644 --- a/jc/parsers/crontab.py +++ b/jc/parsers/crontab.py @@ -140,6 +140,7 @@ class info(): # compatible options: linux, darwin, cygwin, win32, aix, freebsd compatible = ['linux', 'darwin', 'aix', 'freebsd'] + magic_commands = ['crontab'] __version__ = info.version diff --git a/jc/parsers/df.py b/jc/parsers/df.py index 379b3cc4..6ca5ca32 100644 --- a/jc/parsers/df.py +++ b/jc/parsers/df.py @@ -80,6 +80,7 @@ class info(): # compatible options: linux, darwin, cygwin, win32, aix, freebsd compatible = ['linux', 'darwin'] + magic_commands = ['df'] __version__ = info.version diff --git a/jc/parsers/dig.py b/jc/parsers/dig.py index b381483e..4b1a347a 100644 --- a/jc/parsers/dig.py +++ b/jc/parsers/dig.py @@ -331,6 +331,7 @@ class info(): # compatible options: linux, darwin, cygwin, win32, aix, freebsd compatible = ['linux', 'aix', 'freebsd', 'darwin'] + magic_commands = ['dig'] __version__ = info.version diff --git a/jc/parsers/du.py b/jc/parsers/du.py index c21eec8e..a9bb0285 100644 --- a/jc/parsers/du.py +++ b/jc/parsers/du.py @@ -81,6 +81,7 @@ class info(): # compatible options: linux, darwin, cygwin, win32, aix, freebsd compatible = ['linux', 'darwin', 'aix', 'freebsd'] + magic_commands = ['du'] __version__ = info.version diff --git a/jc/parsers/env.py b/jc/parsers/env.py index 86931c2e..2db52784 100644 --- a/jc/parsers/env.py +++ b/jc/parsers/env.py @@ -59,6 +59,7 @@ class info(): # compatible options: linux, darwin, cygwin, win32, aix, freebsd compatible = ['linux', 'darwin', 'cygwin', 'win32', 'aix', 'freebsd'] + magic_commands = ['env'] __version__ = info.version diff --git a/jc/parsers/foo.py b/jc/parsers/foo.py index 983fe6d8..51170fb2 100644 --- a/jc/parsers/foo.py +++ b/jc/parsers/foo.py @@ -28,6 +28,7 @@ class info(): # compatible options: linux, darwin, cygwin, win32, aix, freebsd compatible = ['linux', 'darwin', 'cygwin', 'win32', 'aix', 'freebsd'] + magic_commands = ['foo'] __version__ = info.version diff --git a/jc/parsers/free.py b/jc/parsers/free.py index 5deeb802..5eefe44a 100644 --- a/jc/parsers/free.py +++ b/jc/parsers/free.py @@ -60,6 +60,7 @@ class info(): # compatible options: linux, darwin, cygwin, win32, aix, freebsd compatible = ['linux'] + magic_commands = ['free'] __version__ = info.version diff --git a/jc/parsers/fstab.py b/jc/parsers/fstab.py index f8c79f2f..374cc883 100644 --- a/jc/parsers/fstab.py +++ b/jc/parsers/fstab.py @@ -77,6 +77,7 @@ class info(): # compatible options: linux, darwin, cygwin, win32, aix, freebsd compatible = ['linux'] + magic_commands = ['fstab'] __version__ = info.version diff --git a/jc/parsers/history.py b/jc/parsers/history.py index 09950578..5dba34a9 100644 --- a/jc/parsers/history.py +++ b/jc/parsers/history.py @@ -51,6 +51,7 @@ class info(): # compatible options: linux, darwin, cygwin, win32, aix, freebsd compatible = ['linux', 'darwin', 'cygwin', 'aix', 'freebsd'] + magic_commands = ['history'] __version__ = info.version diff --git a/jc/parsers/id.py b/jc/parsers/id.py index 42b7ff7c..a7aacf9f 100644 --- a/jc/parsers/id.py +++ b/jc/parsers/id.py @@ -78,6 +78,7 @@ class info(): # compatible options: linux, darwin, cygwin, win32, aix, freebsd compatible = ['linux', 'darwin', 'aix', 'freebsd'] + magic_commands = ['id'] __version__ = info.version diff --git a/jc/parsers/ifconfig.py b/jc/parsers/ifconfig.py index 633050ff..02173c8a 100644 --- a/jc/parsers/ifconfig.py +++ b/jc/parsers/ifconfig.py @@ -154,6 +154,7 @@ class info(): # compatible options: linux, darwin, cygwin, win32, aix, freebsd compatible = ['linux', 'aix', 'freebsd', 'darwin'] + magic_commands = ['ifconfig'] __version__ = info.version diff --git a/jc/parsers/iptables.py b/jc/parsers/iptables.py index 380ceb63..89ba2767 100644 --- a/jc/parsers/iptables.py +++ b/jc/parsers/iptables.py @@ -141,6 +141,7 @@ class info(): # compatible options: linux, darwin, cygwin, win32, aix, freebsd compatible = ['linux'] + magic_commands = ['iptables'] __version__ = info.version diff --git a/jc/parsers/jobs.py b/jc/parsers/jobs.py index 27760553..a717b000 100644 --- a/jc/parsers/jobs.py +++ b/jc/parsers/jobs.py @@ -84,6 +84,7 @@ class info(): # compatible options: linux, darwin, cygwin, win32, aix, freebsd compatible = ['linux', 'darwin', 'cygwin', 'aix', 'freebsd'] + magic_commands = ['jobs'] __version__ = info.version diff --git a/jc/parsers/ls.py b/jc/parsers/ls.py index 9b5485bf..e3430319 100644 --- a/jc/parsers/ls.py +++ b/jc/parsers/ls.py @@ -151,6 +151,7 @@ class info(): # compatible options: linux, darwin, cygwin, win32, aix, freebsd compatible = ['linux', 'darwin', 'cygwin', 'aix', 'freebsd'] + magic_commands = ['ls'] __version__ = info.version diff --git a/jc/parsers/lsblk.py b/jc/parsers/lsblk.py index 38bb6508..10ffb0b9 100644 --- a/jc/parsers/lsblk.py +++ b/jc/parsers/lsblk.py @@ -223,6 +223,7 @@ class info(): # compatible options: linux, darwin, cygwin, win32, aix, freebsd compatible = ['linux'] + magic_commands = ['lsblk'] __version__ = info.version diff --git a/jc/parsers/lsmod.py b/jc/parsers/lsmod.py index 9cbf0f62..512bbd52 100644 --- a/jc/parsers/lsmod.py +++ b/jc/parsers/lsmod.py @@ -114,6 +114,7 @@ class info(): # compatible options: linux, darwin, cygwin, win32, aix, freebsd compatible = ['linux'] + magic_commands = ['lsmod'] __version__ = info.version diff --git a/jc/parsers/lsof.py b/jc/parsers/lsof.py index 97a92fc3..c388f923 100644 --- a/jc/parsers/lsof.py +++ b/jc/parsers/lsof.py @@ -104,6 +104,7 @@ class info(): # compatible options: linux, darwin, cygwin, win32, aix, freebsd compatible = ['linux'] + magic_commands = ['lsof'] __version__ = info.version diff --git a/jc/parsers/mount.py b/jc/parsers/mount.py index e265b10e..32a16763 100644 --- a/jc/parsers/mount.py +++ b/jc/parsers/mount.py @@ -63,6 +63,7 @@ class info(): # compatible options: linux, darwin, cygwin, win32, aix, freebsd compatible = ['linux', 'darwin'] + magic_commands = ['mount'] __version__ = info.version diff --git a/jc/parsers/netstat.py b/jc/parsers/netstat.py index a7bc00f8..622d09e6 100644 --- a/jc/parsers/netstat.py +++ b/jc/parsers/netstat.py @@ -320,6 +320,7 @@ class info(): # compatible options: linux, darwin, cygwin, win32, aix, freebsd compatible = ['linux'] + magic_commands = ['netstat'] __version__ = info.version diff --git a/jc/parsers/pip_list.py b/jc/parsers/pip_list.py index e7d1e96a..806006fa 100644 --- a/jc/parsers/pip_list.py +++ b/jc/parsers/pip_list.py @@ -39,6 +39,7 @@ class info(): # compatible options: linux, darwin, cygwin, win32, aix, freebsd compatible = ['linux', 'darwin', 'cygwin', 'win32', 'aix', 'freebsd'] + magic_commands = ['pip list', 'pip3 list'] __version__ = info.version diff --git a/jc/parsers/pip_show.py b/jc/parsers/pip_show.py index 6d085832..ceeadc67 100644 --- a/jc/parsers/pip_show.py +++ b/jc/parsers/pip_show.py @@ -49,6 +49,7 @@ class info(): # compatible options: linux, darwin, cygwin, win32, aix, freebsd compatible = ['linux', 'darwin', 'cygwin', 'win32', 'aix', 'freebsd'] + magic_commands = ['pip show', 'pip3 show'] __version__ = info.version diff --git a/jc/parsers/ps.py b/jc/parsers/ps.py index 05c8bfe7..5489c3c8 100644 --- a/jc/parsers/ps.py +++ b/jc/parsers/ps.py @@ -184,6 +184,7 @@ class info(): # compatible options: linux, darwin, cygwin, win32, aix, freebsd compatible = ['linux', 'darwin', 'cygwin', 'aix', 'freebsd'] + magic_commands = ['ps'] __version__ = info.version diff --git a/jc/parsers/route.py b/jc/parsers/route.py index 7921edd7..43ad8720 100644 --- a/jc/parsers/route.py +++ b/jc/parsers/route.py @@ -108,6 +108,7 @@ class info(): # compatible options: linux, darwin, cygwin, win32, aix, freebsd compatible = ['linux'] + magic_commands = ['route'] __version__ = info.version diff --git a/jc/parsers/ss.py b/jc/parsers/ss.py index 05a6e5df..974d4b24 100644 --- a/jc/parsers/ss.py +++ b/jc/parsers/ss.py @@ -258,6 +258,7 @@ class info(): # compatible options: linux, darwin, cygwin, win32, aix, freebsd compatible = ['linux'] + magic_commands = ['ss'] __version__ = info.version diff --git a/jc/parsers/stat.py b/jc/parsers/stat.py index 72283c3a..1c37f84a 100644 --- a/jc/parsers/stat.py +++ b/jc/parsers/stat.py @@ -111,6 +111,7 @@ class info(): # compatible options: linux, darwin, cygwin, win32, aix, freebsd compatible = ['linux'] + magic_commands = ['stat'] __version__ = info.version diff --git a/jc/parsers/systemctl.py b/jc/parsers/systemctl.py index d9cec6dd..0e68ccdf 100644 --- a/jc/parsers/systemctl.py +++ b/jc/parsers/systemctl.py @@ -47,6 +47,7 @@ class info(): # compatible options: linux, darwin, cygwin, win32, aix, freebsd compatible = ['linux'] + magic_commands = ['systemctl'] __version__ = info.version diff --git a/jc/parsers/systemctl_lj.py b/jc/parsers/systemctl_lj.py index fa2c1a68..685a4ef2 100644 --- a/jc/parsers/systemctl_lj.py +++ b/jc/parsers/systemctl_lj.py @@ -66,6 +66,7 @@ class info(): # compatible options: linux, darwin, cygwin, win32, aix, freebsd compatible = ['linux'] + magic_commands = ['systemctl list-jobs'] __version__ = info.version diff --git a/jc/parsers/systemctl_ls.py b/jc/parsers/systemctl_ls.py index ce01b91a..074cd628 100644 --- a/jc/parsers/systemctl_ls.py +++ b/jc/parsers/systemctl_ls.py @@ -41,6 +41,7 @@ class info(): # compatible options: linux, darwin, cygwin, win32, aix, freebsd compatible = ['linux'] + magic_commands = ['systemctl list-sockets'] __version__ = info.version diff --git a/jc/parsers/systemctl_luf.py b/jc/parsers/systemctl_luf.py index e169da5f..5b0f09e7 100644 --- a/jc/parsers/systemctl_luf.py +++ b/jc/parsers/systemctl_luf.py @@ -38,6 +38,7 @@ class info(): # compatible options: linux, darwin, cygwin, win32, aix, freebsd compatible = ['linux'] + magic_commands = ['systemctl list-unit-files'] __version__ = info.version diff --git a/jc/parsers/uname.py b/jc/parsers/uname.py index 1c4a6876..a4983d09 100644 --- a/jc/parsers/uname.py +++ b/jc/parsers/uname.py @@ -37,6 +37,7 @@ class info(): # compatible options: linux, darwin, cygwin, win32, aix, freebsd compatible = ['linux', 'darwin'] + magic_commands = ['uname'] __version__ = info.version diff --git a/jc/parsers/uptime.py b/jc/parsers/uptime.py index 61a05508..2454a314 100644 --- a/jc/parsers/uptime.py +++ b/jc/parsers/uptime.py @@ -41,6 +41,7 @@ class info(): # compatible options: linux, darwin, cygwin, win32, aix, freebsd compatible = ['linux', 'darwin', 'cygwin', 'aix', 'freebsd'] + magic_commands = ['uptime'] __version__ = info.version diff --git a/jc/parsers/w.py b/jc/parsers/w.py index 8bae56e7..77ad68cb 100644 --- a/jc/parsers/w.py +++ b/jc/parsers/w.py @@ -90,6 +90,7 @@ class info(): # compatible options: linux, darwin, cygwin, win32, aix, freebsd compatible = ['linux', 'darwin', 'cygwin', 'aix', 'freebsd'] + magic_commands = ['w'] __version__ = info.version From 95dbf98e8e03486f74d986683f52699f4f9c9577 Mon Sep 17 00:00:00 2001 From: Kelly Brazil Date: Tue, 11 Feb 2020 19:14:51 -0800 Subject: [PATCH 05/27] allow options in magic syntax --- jc/cli.py | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/jc/cli.py b/jc/cli.py index 2f1f52df..1f02fc54 100644 --- a/jc/cli.py +++ b/jc/cli.py @@ -141,9 +141,13 @@ def helptext(message): Usage: jc PARSER [OPTIONS] + or + + jc [OPTIONS] PARSER + or magic syntax: - jc COMMAND + jc [OPTIONS] COMMAND Parsers: {parsers_string} @@ -159,7 +163,7 @@ def helptext(message): or using the magic syntax: - jc ls -al + jc -p ls -al ''' print(textwrap.dedent(helptext_string), file=sys.stderr) @@ -172,12 +176,26 @@ def json_out(data, pretty=False): def magic(): - """Parse with magic syntax: jc ls -al""" - if len(sys.argv) > 1 and not sys.argv[1].startswith('-'): + """Parse with magic syntax: jc -p ls -al""" + if len(sys.argv) > 1 and not sys.argv[1].startswith('--'): parser_info = about_jc()['parsers'] args_given = sys.argv[1:] + options = [] found_parser = None + # find the options + if args_given[0].startswith('-'): + p = 0 + for i, arg in list(enumerate(args_given)): + if arg.startswith('--'): + return + elif arg.startswith('-'): + options.append(args_given.pop(i - p)) + p = p + 1 + else: + break + + # find the command and parser for parser in parser_info: if 'magic_commands' in parser: for magic_command in parser['magic_commands']: @@ -189,8 +207,10 @@ def magic(): break if found_parser: - run_command = ' '.join(sys.argv[1:]) - whole_command = [run_command, '|', 'jc', found_parser, '-p'] + run_command = ' '.join(args_given) + cmd_options = ' '.join(options) + whole_command = [run_command, '|', 'jc', found_parser, cmd_options] + os.system(' '.join(whole_command)) exit() else: From 6d50ec71997cac9c898737de93b8a945f0351e53 Mon Sep 17 00:00:00 2001 From: Kelly Brazil Date: Wed, 12 Feb 2020 00:11:48 -0500 Subject: [PATCH 06/27] add try/except to fix bare jc command condition --- jc/cli.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/jc/cli.py b/jc/cli.py index 1f02fc54..2ef1e4f2 100644 --- a/jc/cli.py +++ b/jc/cli.py @@ -199,12 +199,15 @@ def magic(): for parser in parser_info: if 'magic_commands' in parser: for magic_command in parser['magic_commands']: - 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 + 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 if found_parser: run_command = ' '.join(args_given) From a7158373cd225685f5c398fa36b8d5208e20521c Mon Sep 17 00:00:00 2001 From: Kelly Brazil Date: Wed, 12 Feb 2020 00:16:17 -0500 Subject: [PATCH 07/27] comment update --- jc/cli.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jc/cli.py b/jc/cli.py index 2ef1e4f2..4b43aa4b 100644 --- a/jc/cli.py +++ b/jc/cli.py @@ -225,7 +225,7 @@ def magic(): def main(): signal.signal(signal.SIGINT, ctrlc) - # try magic syntax first + # try magic syntax first: jc -p ls -al magic() debug = False From dd6680efb2b15bb8ed676016aae1c65193dfe147 Mon Sep 17 00:00:00 2001 From: Kelly Brazil Date: Thu, 13 Feb 2020 09:47:16 -0500 Subject: [PATCH 08/27] allow condensed options (-prdq is equivalent to -p -r -d -q) --- changelog.txt | 3 +++ jc/cli.py | 40 ++++++++++++++++++++++++++-------------- 2 files changed, 29 insertions(+), 14 deletions(-) diff --git a/changelog.txt b/changelog.txt index 5c67dbb8..d2951150 100644 --- a/changelog.txt +++ b/changelog.txt @@ -2,6 +2,9 @@ jc changelog 20200211 v1.7.3 - Add alternative 'magic' syntax: e.g. `jc ls -al` +- Options can now be condensed (e.g. -prq is equivalant to -p -r -q ) +- TODO: check if fstab works for both command and file +- TODO: check if crontab works for both command and file 20200208 v1.7.2 - Include test fixtures in wheel diff --git a/jc/cli.py b/jc/cli.py index 4b43aa4b..2fa75791 100644 --- a/jc/cli.py +++ b/jc/cli.py @@ -139,11 +139,11 @@ def helptext(message): helptext_string = f''' jc: {message} - Usage: jc PARSER [OPTIONS] + Usage: COMMAND | jc PARSER [OPTIONS] or - jc [OPTIONS] PARSER + COMMAND | jc [OPTIONS] PARSER or magic syntax: @@ -179,6 +179,7 @@ def magic(): """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:] options = [] found_parser = None @@ -187,11 +188,14 @@ def magic(): if args_given[0].startswith('-'): p = 0 for i, arg in list(enumerate(args_given)): + # parser found if arg.startswith('--'): return + # option found elif arg.startswith('-'): - options.append(args_given.pop(i - p)) + options.append(args_given.pop(i - p)[1:]) p = p + 1 + # command found else: break @@ -209,16 +213,18 @@ def magic(): except Exception: return + run_command = ' '.join(args_given) if found_parser: - run_command = ' '.join(args_given) - cmd_options = ' '.join(options) - whole_command = [run_command, '|', 'jc', found_parser, cmd_options] + if options: + cmd_options = '-' + ''.join(options) + else: + cmd_options = '' + whole_command = ' '.join([run_command, '|', 'jc', found_parser, cmd_options]) - os.system(' '.join(whole_command)) + os.system(whole_command) exit() else: - args_given_pretty = ' '.join(args_given) - helptext(f'parser not found for "{args_given_pretty}"') + helptext(f'parser not found for "{run_command}"') sys.exit(1) @@ -228,25 +234,31 @@ def main(): # try magic syntax first: jc -p ls -al magic() + options = [] debug = False pretty = False quiet = False raw = False # options - if '-d' in sys.argv: + for opt in sys.argv: + if opt.startswith('-') and not opt.startswith('--'): + for flag in opt[1:]: + options.append(flag) + + if 'd' in options: debug = True - if '-p' in sys.argv: + if 'p' in options: pretty = True - if '-q' in sys.argv: + if 'q' in options: quiet = True - if '-r' in sys.argv: + if 'r' in options: raw = True - if '-a' in sys.argv: + if 'a' in options: json_out(about_jc(), pretty=pretty) exit() From 515a8a84b79c9bf6f3001d3c52faa82a6709b8d1 Mon Sep 17 00:00:00 2001 From: Kelly Brazil Date: Thu, 13 Feb 2020 09:47:40 -0500 Subject: [PATCH 09/27] add "command" to description --- jc/parsers/arp.py | 2 +- jc/parsers/crontab.py | 2 +- jc/parsers/df.py | 2 +- jc/parsers/dig.py | 2 +- jc/parsers/du.py | 2 +- jc/parsers/env.py | 2 +- jc/parsers/foo.py | 2 +- jc/parsers/free.py | 2 +- jc/parsers/fstab.py | 2 +- jc/parsers/history.py | 2 +- jc/parsers/id.py | 2 +- jc/parsers/ifconfig.py | 2 +- jc/parsers/iptables.py | 2 +- jc/parsers/jobs.py | 2 +- jc/parsers/ls.py | 2 +- jc/parsers/lsblk.py | 2 +- jc/parsers/lsmod.py | 2 +- jc/parsers/lsof.py | 2 +- jc/parsers/mount.py | 2 +- jc/parsers/netstat.py | 2 +- jc/parsers/pip_list.py | 2 +- jc/parsers/pip_show.py | 2 +- jc/parsers/ps.py | 2 +- jc/parsers/route.py | 2 +- jc/parsers/ss.py | 2 +- jc/parsers/stat.py | 2 +- jc/parsers/systemctl.py | 2 +- jc/parsers/systemctl_lj.py | 2 +- jc/parsers/systemctl_ls.py | 2 +- jc/parsers/systemctl_luf.py | 2 +- jc/parsers/uname.py | 2 +- jc/parsers/uptime.py | 2 +- jc/parsers/w.py | 2 +- 33 files changed, 33 insertions(+), 33 deletions(-) diff --git a/jc/parsers/arp.py b/jc/parsers/arp.py index ae4a016c..1d8e9d92 100644 --- a/jc/parsers/arp.py +++ b/jc/parsers/arp.py @@ -92,7 +92,7 @@ import jc.parsers.universal class info(): version = '1.1' - description = 'arp parser' + description = 'arp command parser' author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' diff --git a/jc/parsers/crontab.py b/jc/parsers/crontab.py index 8acfacb6..ea234a20 100644 --- a/jc/parsers/crontab.py +++ b/jc/parsers/crontab.py @@ -133,7 +133,7 @@ import jc.parsers.universal class info(): version = '1.1' - description = 'crontab file parser' + description = 'crontab command and file parser' author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' # details = 'enter any other details here' diff --git a/jc/parsers/df.py b/jc/parsers/df.py index 6ca5ca32..817f0a21 100644 --- a/jc/parsers/df.py +++ b/jc/parsers/df.py @@ -74,7 +74,7 @@ import jc.parsers.universal class info(): version = '1.1' - description = 'df parser' + description = 'df command parser' author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' diff --git a/jc/parsers/dig.py b/jc/parsers/dig.py index 4b1a347a..6966942d 100644 --- a/jc/parsers/dig.py +++ b/jc/parsers/dig.py @@ -325,7 +325,7 @@ import jc.utils class info(): version = '1.0' - description = 'dig parser' + description = 'dig command parser' author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' diff --git a/jc/parsers/du.py b/jc/parsers/du.py index a9bb0285..c0026558 100644 --- a/jc/parsers/du.py +++ b/jc/parsers/du.py @@ -74,7 +74,7 @@ import jc.parsers.universal class info(): version = '1.1' - description = 'du parser' + description = 'du command parser' author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' # details = 'enter any other details here' diff --git a/jc/parsers/env.py b/jc/parsers/env.py index 2db52784..d95ed003 100644 --- a/jc/parsers/env.py +++ b/jc/parsers/env.py @@ -53,7 +53,7 @@ import jc.utils class info(): version = '1.1' - description = 'env parser' + description = 'env command parser' author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' diff --git a/jc/parsers/foo.py b/jc/parsers/foo.py index 51170fb2..ffb80951 100644 --- a/jc/parsers/foo.py +++ b/jc/parsers/foo.py @@ -21,7 +21,7 @@ import jc.utils class info(): version = '1.0' - description = 'foo parser' + description = 'foo command parser' author = 'John Doe' author_email = 'johndoe@gmail.com' # details = 'enter any other details here' diff --git a/jc/parsers/free.py b/jc/parsers/free.py index 5eefe44a..aaf119ec 100644 --- a/jc/parsers/free.py +++ b/jc/parsers/free.py @@ -54,7 +54,7 @@ import jc.parsers.universal class info(): version = '1.0' - description = 'free parser' + description = 'free command parser' author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' diff --git a/jc/parsers/fstab.py b/jc/parsers/fstab.py index 374cc883..4d1387ef 100644 --- a/jc/parsers/fstab.py +++ b/jc/parsers/fstab.py @@ -71,7 +71,7 @@ import jc.utils class info(): version = '1.0' - description = '/etc/fstab file parser' + description = 'fstab command and file parser' author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' diff --git a/jc/parsers/history.py b/jc/parsers/history.py index 5dba34a9..4f1b62b4 100644 --- a/jc/parsers/history.py +++ b/jc/parsers/history.py @@ -45,7 +45,7 @@ import jc class info(): version = '1.1' - description = 'history parser' + description = 'history command parser' author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' diff --git a/jc/parsers/id.py b/jc/parsers/id.py index a7aacf9f..c19021c6 100644 --- a/jc/parsers/id.py +++ b/jc/parsers/id.py @@ -71,7 +71,7 @@ import jc.utils class info(): version = '1.0' - description = 'id parser' + description = 'id command parser' author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' # details = 'enter any other details here' diff --git a/jc/parsers/ifconfig.py b/jc/parsers/ifconfig.py index 02173c8a..7c4df2ed 100644 --- a/jc/parsers/ifconfig.py +++ b/jc/parsers/ifconfig.py @@ -147,7 +147,7 @@ from ifconfigparser import IfconfigParser class info(): version = '1.5' - description = 'ifconfig parser' + description = 'ifconfig command parser' author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' details = 'Using ifconfig-parser package from https://github.com/KnightWhoSayNi/ifconfig-parser' diff --git a/jc/parsers/iptables.py b/jc/parsers/iptables.py index 89ba2767..9a75fd78 100644 --- a/jc/parsers/iptables.py +++ b/jc/parsers/iptables.py @@ -135,7 +135,7 @@ import jc.utils class info(): version = '1.1' - description = 'iptables parser' + description = 'iptables command parser' author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' diff --git a/jc/parsers/jobs.py b/jc/parsers/jobs.py index a717b000..c5c5f0b8 100644 --- a/jc/parsers/jobs.py +++ b/jc/parsers/jobs.py @@ -78,7 +78,7 @@ import jc.utils class info(): version = '1.0' - description = 'jobs parser' + description = 'jobs command parser' author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' diff --git a/jc/parsers/ls.py b/jc/parsers/ls.py index e3430319..e3500428 100644 --- a/jc/parsers/ls.py +++ b/jc/parsers/ls.py @@ -145,7 +145,7 @@ import jc.utils class info(): version = '1.0' - description = 'ls parser' + description = 'ls command parser' author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' diff --git a/jc/parsers/lsblk.py b/jc/parsers/lsblk.py index 10ffb0b9..49544d51 100644 --- a/jc/parsers/lsblk.py +++ b/jc/parsers/lsblk.py @@ -217,7 +217,7 @@ import jc.parsers.universal class info(): version = '1.3' - description = 'lsblk parser' + description = 'lsblk command parser' author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' diff --git a/jc/parsers/lsmod.py b/jc/parsers/lsmod.py index 512bbd52..6b8cf40a 100644 --- a/jc/parsers/lsmod.py +++ b/jc/parsers/lsmod.py @@ -108,7 +108,7 @@ import jc.parsers.universal class info(): version = '1.1' - description = 'lsmod parser' + description = 'lsmod command parser' author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' diff --git a/jc/parsers/lsof.py b/jc/parsers/lsof.py index c388f923..6557ad9f 100644 --- a/jc/parsers/lsof.py +++ b/jc/parsers/lsof.py @@ -98,7 +98,7 @@ import jc.parsers.universal class info(): version = '1.0' - description = 'lsof parser' + description = 'lsof command parser' author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' diff --git a/jc/parsers/mount.py b/jc/parsers/mount.py index 32a16763..0fcdb690 100644 --- a/jc/parsers/mount.py +++ b/jc/parsers/mount.py @@ -57,7 +57,7 @@ import jc.utils class info(): version = '1.1' - description = 'mount parser' + description = 'mount command parser' author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' diff --git a/jc/parsers/netstat.py b/jc/parsers/netstat.py index 622d09e6..a31531c5 100644 --- a/jc/parsers/netstat.py +++ b/jc/parsers/netstat.py @@ -314,7 +314,7 @@ import jc.utils class info(): version = '1.2' - description = 'netstat parser' + description = 'netstat command parser' author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' diff --git a/jc/parsers/pip_list.py b/jc/parsers/pip_list.py index 806006fa..5bcd5160 100644 --- a/jc/parsers/pip_list.py +++ b/jc/parsers/pip_list.py @@ -33,7 +33,7 @@ import jc.parsers.universal class info(): version = '1.0' - description = 'pip list parser' + description = 'pip list command parser' author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' diff --git a/jc/parsers/pip_show.py b/jc/parsers/pip_show.py index ceeadc67..185e0528 100644 --- a/jc/parsers/pip_show.py +++ b/jc/parsers/pip_show.py @@ -43,7 +43,7 @@ import jc.utils class info(): version = '1.0' - description = 'pip show parser' + description = 'pip show command parser' author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' diff --git a/jc/parsers/ps.py b/jc/parsers/ps.py index 5489c3c8..1b91275a 100644 --- a/jc/parsers/ps.py +++ b/jc/parsers/ps.py @@ -178,7 +178,7 @@ import jc.parsers.universal class info(): version = '1.1' - description = 'ps parser' + description = 'ps command parser' author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' diff --git a/jc/parsers/route.py b/jc/parsers/route.py index 43ad8720..668ea354 100644 --- a/jc/parsers/route.py +++ b/jc/parsers/route.py @@ -102,7 +102,7 @@ import jc.parsers.universal class info(): version = '1.0' - description = 'route parser' + description = 'route command parser' author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' diff --git a/jc/parsers/ss.py b/jc/parsers/ss.py index 974d4b24..13ac4166 100644 --- a/jc/parsers/ss.py +++ b/jc/parsers/ss.py @@ -252,7 +252,7 @@ import jc.utils class info(): version = '1.0' - description = 'ss parser' + description = 'ss command parser' author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' diff --git a/jc/parsers/stat.py b/jc/parsers/stat.py index 1c37f84a..ecf89f48 100644 --- a/jc/parsers/stat.py +++ b/jc/parsers/stat.py @@ -105,7 +105,7 @@ import jc.utils class info(): version = '1.0' - description = 'stat parser' + description = 'stat command parser' author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' diff --git a/jc/parsers/systemctl.py b/jc/parsers/systemctl.py index 0e68ccdf..d70fe63f 100644 --- a/jc/parsers/systemctl.py +++ b/jc/parsers/systemctl.py @@ -41,7 +41,7 @@ import jc.utils class info(): version = '1.0' - description = 'systemctl parser' + description = 'systemctl command parser' author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' diff --git a/jc/parsers/systemctl_lj.py b/jc/parsers/systemctl_lj.py index 685a4ef2..0ca69dbf 100644 --- a/jc/parsers/systemctl_lj.py +++ b/jc/parsers/systemctl_lj.py @@ -60,7 +60,7 @@ import jc.utils class info(): version = '1.0' - description = 'systemctl list-jobs parser' + description = 'systemctl list-jobs command parser' author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' diff --git a/jc/parsers/systemctl_ls.py b/jc/parsers/systemctl_ls.py index 074cd628..bbc2c27d 100644 --- a/jc/parsers/systemctl_ls.py +++ b/jc/parsers/systemctl_ls.py @@ -35,7 +35,7 @@ import jc.utils class info(): version = '1.0' - description = 'systemctl list-sockets parser' + description = 'systemctl list-sockets command parser' author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' diff --git a/jc/parsers/systemctl_luf.py b/jc/parsers/systemctl_luf.py index 5b0f09e7..a049d351 100644 --- a/jc/parsers/systemctl_luf.py +++ b/jc/parsers/systemctl_luf.py @@ -32,7 +32,7 @@ import jc.utils class info(): version = '1.0' - description = 'systemctl list-unit-files parser' + description = 'systemctl list-unit-files command parser' author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' diff --git a/jc/parsers/uname.py b/jc/parsers/uname.py index a4983d09..41876d56 100644 --- a/jc/parsers/uname.py +++ b/jc/parsers/uname.py @@ -31,7 +31,7 @@ import jc.utils class info(): version = '1.1' - description = 'uname -a parser' + description = 'uname -a command parser' author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' diff --git a/jc/parsers/uptime.py b/jc/parsers/uptime.py index 2454a314..bf0bc2c5 100644 --- a/jc/parsers/uptime.py +++ b/jc/parsers/uptime.py @@ -35,7 +35,7 @@ import jc.utils class info(): version = '1.0' - description = 'uptime parser' + description = 'uptime command parser' author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' diff --git a/jc/parsers/w.py b/jc/parsers/w.py index 77ad68cb..119ad5c5 100644 --- a/jc/parsers/w.py +++ b/jc/parsers/w.py @@ -84,7 +84,7 @@ import jc.utils class info(): version = '1.0' - description = 'w parser' + description = 'w command parser' author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' From 7492c3f1e312cdb0c58c2077db0d60d2cab1e58f Mon Sep 17 00:00:00 2001 From: Kelly Brazil Date: Thu, 13 Feb 2020 09:48:42 -0500 Subject: [PATCH 10/27] changelog update --- changelog.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog.txt b/changelog.txt index d2951150..a606af98 100644 --- a/changelog.txt +++ b/changelog.txt @@ -7,7 +7,7 @@ jc changelog - TODO: check if crontab works for both command and file 20200208 v1.7.2 -- Include test fixtures in wheel +- Include test fixtures in wheel and sdist 20200205 v1.7.1 - Add YAML file parser From 0d370eb403ab4d9ed11e36776efc08bdd3505c1a Mon Sep 17 00:00:00 2001 From: Kelly Brazil Date: Thu, 13 Feb 2020 10:03:11 -0500 Subject: [PATCH 11/27] doc update --- jc/cli.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jc/cli.py b/jc/cli.py index 2fa75791..dbfab530 100644 --- a/jc/cli.py +++ b/jc/cli.py @@ -82,7 +82,7 @@ def parser_mod_shortname(parser): def parser_module(parser): - """import the module just in time and present the module object""" + """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)) From 81c11a975c6ee66a1f83350df8b065a6b07dcc9c Mon Sep 17 00:00:00 2001 From: Kelly Brazil Date: Thu, 13 Feb 2020 10:08:43 -0500 Subject: [PATCH 12/27] added docstrings --- jc/cli.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/jc/cli.py b/jc/cli.py index dbfab530..6899b09b 100644 --- a/jc/cli.py +++ b/jc/cli.py @@ -88,6 +88,7 @@ def parser_module(parser): def parsers_text(indent=0, pad=0): + """return the argument and description information from each parser""" ptext = '' for parser in parsers: parser_arg = parser_argument(parser) @@ -105,6 +106,7 @@ def parsers_text(indent=0, pad=0): def about_jc(): + """return jc info and the contents of each parser.info as a dictionary""" parser_list = [] for parser in parsers: @@ -134,6 +136,7 @@ def about_jc(): def helptext(message): + """return the help text with the list of parsers""" parsers_string = parsers_text(indent=12, pad=17) helptext_string = f''' From c8b502c571d3081ca3086e349a426ca252a3bb84 Mon Sep 17 00:00:00 2001 From: Kelly Brazil Date: Thu, 13 Feb 2020 10:14:32 -0500 Subject: [PATCH 13/27] remove unnecessary join and add comments --- jc/cli.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/jc/cli.py b/jc/cli.py index 6899b09b..dc7a8a00 100644 --- a/jc/cli.py +++ b/jc/cli.py @@ -210,12 +210,13 @@ def magic(): if ' '.join(args_given[0:2]) == magic_command: found_parser = parser['argument'] break - elif ''.join(args_given[0]) == magic_command: + elif args_given[0] == magic_command: found_parser = parser['argument'] break except Exception: return + # construct a new command line using the standard syntax: COMMAND | jc --PARSER -OPTIONS run_command = ' '.join(args_given) if found_parser: if options: @@ -232,9 +233,10 @@ def magic(): def main(): + # break on ctrl-c keyboard interrupt signal.signal(signal.SIGINT, ctrlc) - # try magic syntax first: jc -p ls -al + # try magic syntax first: e.g. jc -p ls -al magic() options = [] From bf8811e03e2a5b736f4c084f42400fcde400ff3f Mon Sep 17 00:00:00 2001 From: Kelly Brazil Date: Thu, 13 Feb 2020 10:25:41 -0500 Subject: [PATCH 14/27] add comments --- jc/cli.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/jc/cli.py b/jc/cli.py index dc7a8a00..4dcf9962 100644 --- a/jc/cli.py +++ b/jc/cli.py @@ -191,14 +191,14 @@ def magic(): if args_given[0].startswith('-'): p = 0 for i, arg in list(enumerate(args_given)): - # parser found + # parser found - use standard syntax if arg.startswith('--'): return - # option found + # option found - populate option list elif arg.startswith('-'): options.append(args_given.pop(i - p)[1:]) p = p + 1 - # command found + # command found if iterator didn't already stop - stop iterating else: break @@ -207,12 +207,15 @@ def magic(): if 'magic_commands' in parser: for magic_command in parser['magic_commands']: try: + # two arguments: e.g. 'pip list' if ' '.join(args_given[0:2]) == magic_command: found_parser = parser['argument'] break + # one argument: e.g. 'ls' elif args_given[0] == magic_command: found_parser = parser['argument'] break + # No command found - use standard syntax (for cases like 'jc -a') except Exception: return From 2fb6ae08d76e7a5727b2fcdd5def3e3822e0cf72 Mon Sep 17 00:00:00 2001 From: Kelly Brazil Date: Thu, 13 Feb 2020 12:17:41 -0800 Subject: [PATCH 15/27] fix shlex usage --- jc/cli.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/jc/cli.py b/jc/cli.py index 4dcf9962..caa3f217 100644 --- a/jc/cli.py +++ b/jc/cli.py @@ -4,6 +4,7 @@ JC cli module """ import sys import os +import shlex import importlib import textwrap import signal @@ -182,8 +183,8 @@ def magic(): """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:] + # correctly parse escape characters and spaces with shlex + args_given = " ".join(map(shlex.quote, sys.argv[1:])).split() options = [] found_parser = None From 66942d64babf932faeb887eb4ec0cab32829321a Mon Sep 17 00:00:00 2001 From: Kelly Brazil Date: Thu, 13 Feb 2020 16:56:48 -0800 Subject: [PATCH 16/27] changelog update --- changelog.txt | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/changelog.txt b/changelog.txt index a606af98..da8ab063 100644 --- a/changelog.txt +++ b/changelog.txt @@ -2,9 +2,7 @@ jc changelog 20200211 v1.7.3 - Add alternative 'magic' syntax: e.g. `jc ls -al` -- Options can now be condensed (e.g. -prq is equivalant to -p -r -q ) -- TODO: check if fstab works for both command and file -- TODO: check if crontab works for both command and file +- Options can now be condensed (e.g. -prq is equivalant to -p -r -q) 20200208 v1.7.2 - Include test fixtures in wheel and sdist From 1c1b19a478fe5c356a93cd165518429e77fcbdf0 Mon Sep 17 00:00:00 2001 From: Kelly Brazil Date: Thu, 13 Feb 2020 16:57:30 -0800 Subject: [PATCH 17/27] doc update --- jc/parsers/crontab.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/jc/parsers/crontab.py b/jc/parsers/crontab.py index ea234a20..f656cf40 100644 --- a/jc/parsers/crontab.py +++ b/jc/parsers/crontab.py @@ -1,8 +1,8 @@ -"""jc - JSON CLI output utility crontab file Parser +"""jc - JSON CLI output utility crontab command and file Parser Usage: - specify --crontab as the first argument if the piped input is coming from a crontab file + specify --crontab as the first argument if the piped input is coming from crontab -l or a crontab file Compatibility: @@ -10,7 +10,7 @@ Compatibility: Examples: - $ cat /etc/crontab | jc --crontab -p + $ crontab -l | jc --crontab -p { "variables": [ { From 1af85811e06c489051c456918284a746ce12c692 Mon Sep 17 00:00:00 2001 From: Kelly Brazil Date: Thu, 13 Feb 2020 16:57:57 -0800 Subject: [PATCH 18/27] remove magic_command info --- jc/parsers/fstab.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/jc/parsers/fstab.py b/jc/parsers/fstab.py index 4d1387ef..430378d0 100644 --- a/jc/parsers/fstab.py +++ b/jc/parsers/fstab.py @@ -71,13 +71,12 @@ import jc.utils class info(): version = '1.0' - description = 'fstab command and file parser' + description = 'fstab file parser' author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' # compatible options: linux, darwin, cygwin, win32, aix, freebsd compatible = ['linux'] - magic_commands = ['fstab'] __version__ = info.version From dc2907d3ce101043b30bcce71abb4bb02897f43a Mon Sep 17 00:00:00 2001 From: Kelly Brazil Date: Thu, 13 Feb 2020 16:58:25 -0800 Subject: [PATCH 19/27] doc update --- docs/parsers/crontab.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/parsers/crontab.md b/docs/parsers/crontab.md index 98eb08e4..c6d63870 100644 --- a/docs/parsers/crontab.md +++ b/docs/parsers/crontab.md @@ -1,9 +1,9 @@ # jc.parsers.crontab -jc - JSON CLI output utility crontab file Parser +jc - JSON CLI output utility crontab command and file Parser Usage: - specify --crontab as the first argument if the piped input is coming from a crontab file + specify --crontab as the first argument if the piped input is coming from crontab -l or a crontab file Compatibility: @@ -11,7 +11,7 @@ Compatibility: Examples: - $ cat /etc/crontab | jc --crontab -p + $ crontab -l | jc --crontab -p { "variables": [ { From 7f47b533701e60449351d5da03cd5960e0dec9ad Mon Sep 17 00:00:00 2001 From: Kelly Brazil Date: Thu, 13 Feb 2020 17:20:00 -0800 Subject: [PATCH 20/27] add alternate magic syntax --- README.md | 158 +++++++++++++++++++++++++++++++----------------------- 1 file changed, 90 insertions(+), 68 deletions(-) diff --git a/README.md b/README.md index 5bc90011..481d2751 100755 --- a/README.md +++ b/README.md @@ -17,6 +17,20 @@ $ ls -l /usr/bin | jc --ls | jq '.[] | select(.size > 50000000)' "date": "Aug 14 19:41" } ``` +or using the alternative syntax: +``` +$ jc ls -l /usr/bin | jq '.[] | select(.size > 50000000)' +{ + "filename": "docker", + "flags": "-rwxr-xr-x", + "links": 1, + "owner": "root", + "group": "root", + "size": 68677120, + "date": "Aug 14 19:41" +} +``` + For more information on the motivations for this project, please see my blog post at https://blog.kellybrazil.com/2019/11/26/bringing-the-unix-philosophy-to-the-21st-century/. The `jc` parsers can also be used as python modules. In this case the output will be a python dictionary instead of JSON: @@ -58,47 +72,55 @@ $ pip3 install --upgrade jc ## Usage ``` -jc PARSER [OPTIONS] +COMMAND | jc PARSER [OPTIONS] +``` +or +``` +COMMAND | jc [OPTIONS] PARSER +``` +or +``` +jc [OPTIONS] COMMAND ``` `jc` accepts piped input from `STDIN` and outputs a JSON representation of the previous command's output to `STDOUT`. The JSON output can be compact or pretty formatted. ### Parsers -- `--arp` enables the `arp` parser -- `--crontab` enables the `crontab` file parser +- `--arp` enables the `arp` command parser +- `--crontab` enables the `crontab` command and file parser - `--crontab-u` enables the `crontab` file parser with user support -- `--df` enables the `df` parser -- `--dig` enables the `dig` parser -- `--du` enables the `du` parser -- `--env` enables the `env` parser -- `--free` enables the `free` parser +- `--df` enables the `df` command parser +- `--dig` enables the `dig` command parser +- `--du` enables the `du` command parser +- `--env` enables the `env` command parser +- `--free` enables the `free` command parser - `--fstab` enables the `/etc/fstab` file parser -- `--history` enables the `history` parser +- `--history` enables the `history` command parser - `--hosts` enables the `/etc/hosts` file parser -- `--id` enables the `id` parser -- `--ifconfig` enables the `ifconfig` parser +- `--id` enables the `id` command parser +- `--ifconfig` enables the `ifconfig` command parser - `--ini` enables the `INI` file parser -- `--iptables` enables the `iptables` parser -- `--jobs` enables the `jobs` parser -- `--ls` enables the `ls` parser -- `--lsblk` enables the `lsblk` parser -- `--lsmod` enables the `lsmod` parser -- `--lsof` enables the `lsof` parser -- `--mount` enables the `mount` parser -- `--netstat` enables the `netstat` parser -- `--pip-list` enables the `pip list` parser -- `--pip-show` enables the `pip show` parser -- `--ps` enables the `ps` parser -- `--route` enables the `route` parser -- `--ss` enables the `ss` parser -- `--stat` enables the `stat` parser -- `--systemctl` enables the `systemctl` parser -- `--systemctl-lj` enables the `systemctl list-jobs` parser -- `--systemctl-ls` enables the `systemctl list-sockets` parser -- `--systemctl-luf` enables the `systemctl list-unit-files` parser -- `--uname` enables the `uname -a` parser -- `--uptime` enables the `uptime` parser -- `--w` enables the `w` parser +- `--iptables` enables the `iptables` command parser +- `--jobs` enables the `jobs` command parser +- `--ls` enables the `ls` command parser +- `--lsblk` enables the `lsblk` command parser +- `--lsmod` enables the `lsmod` command parser +- `--lsof` enables the `lsof` command parser +- `--mount` enables the `mount` command parser +- `--netstat` enables the `netstat` command parser +- `--pip-list` enables the `pip list` command parser +- `--pip-show` enables the `pip show` command parser +- `--ps` enables the `ps` command parser +- `--route` enables the `route` command parser +- `--ss` enables the `ss` command parser +- `--stat` enables the `stat` command parser +- `--systemctl` enables the `systemctl` command parser +- `--systemctl-lj` enables the `systemctl list-jobs` command parser +- `--systemctl-ls` enables the `systemctl list-sockets` command parser +- `--systemctl-luf` enables the `systemctl list-unit-files` command parser +- `--uname` enables the `uname -a` command parser +- `--uptime` enables the `uptime` command parser +- `--w` enables the `w` command parser - `--xml` enables the `XML` file parser - `--yaml` enables the `YAML` file parser @@ -112,7 +134,7 @@ jc PARSER [OPTIONS] ## Examples ### arp ``` -$ arp | jc --arp -p +$ arp | jc --arp -p # or: jc -p arp [ { "address": "gateway", @@ -138,7 +160,7 @@ $ arp | jc --arp -p ] ``` ``` -$ arp -a | jc --arp -p +$ arp -a | jc --arp -p # or: jc -p arp -a [ { "name": null, @@ -165,7 +187,7 @@ $ arp -a | jc --arp -p ``` ### crontab ``` -$ cat /etc/crontab | jc --crontab -p +$ cat /etc/crontab | jc --crontab -p # or: jc -p crontab -l { "variables": [ { @@ -312,7 +334,7 @@ $ cat /etc/crontab | jc --crontab-u -p ``` ### df ``` -$ df | jc --df -p +$ df | jc --df -p # or: jc -p df [ { "filesystem": "devtmpfs", @@ -335,7 +357,7 @@ $ df | jc --df -p ``` ### dig ``` -$ dig cnn.com www.cnn.com @205.251.194.64 | jc --dig -p +$ dig cnn.com www.cnn.com @205.251.194.64 | jc --dig -p # or: jc -p dig cnn.com www.cnn.com @205.251.194.64 [ { "id": 5509, @@ -455,7 +477,7 @@ $ dig cnn.com www.cnn.com @205.251.194.64 | jc --dig -p ] ``` ``` -$ dig -x 1.1.1.1 | jc --dig -p +$ dig -x 1.1.1.1 | jc --dig -p # or: jc -p dig -x 1.1.1.1 [ { "id": 50324, @@ -493,7 +515,7 @@ $ dig -x 1.1.1.1 | jc --dig -p ``` ### du ``` -$ du /usr | jc --du -p +$ du /usr | jc --du -p # or: jc -p du /usr [ { "size": 104608, @@ -524,7 +546,7 @@ $ du /usr | jc --du -p ``` ### env ``` -$ env | jc --env -p +$ env | jc --env -p # or: jc -p env [ { "name": "XDG_SESSION_ID", @@ -551,7 +573,7 @@ $ env | jc --env -p ``` ### free ``` -$ free | jc --free -p +$ free | jc --free -p # or: jc -p free [ { "type": "Mem", @@ -602,7 +624,7 @@ $ cat /etc/fstab | jc --fstab -p ``` ### history ``` -$ history | jc --history -p +$ history | jc --history -p # or: jc -p history [ { "line": 118, @@ -674,7 +696,7 @@ $ cat /etc/hosts | jc --hosts -p ``` ### id ``` -$ id | jc --id -p +$ id | jc --id -p # or: jc -p id { "uid": { "id": 1000, @@ -704,7 +726,7 @@ $ id | jc --id -p ``` ### ifconfig ``` -$ ifconfig | jc --ifconfig -p +$ ifconfig | jc --ifconfig -p # or: jc -p ifconfig [ { "name": "ens33", @@ -809,7 +831,7 @@ $ cat example.ini | jc --ini -p ``` ### iptables ``` -$ sudo iptables --line-numbers -v -L -t nat | jc --iptables -p +$ sudo iptables --line-numbers -v -L -t nat | jc --iptables -p # or: sudo jc -p iptables --line-numbers -v -L -t nat [ { "chain": "PREROUTING", @@ -870,7 +892,7 @@ $ sudo iptables --line-numbers -v -L -t nat | jc --iptables -p ``` ### jobs ``` -$ jobs -l | jc --jobs -p +$ jobs -l | jc --jobs -p # or: jc -p jobs [ { "job_number": 1, @@ -902,7 +924,7 @@ $ jobs -l | jc --jobs -p ``` ### ls ``` -$ ls -l /usr/bin | jc --ls -p +$ ls -l /usr/bin | jc --ls -p # or: jc -p ls -l /usr/bin [ { "filename": "apropos", @@ -937,7 +959,7 @@ $ ls -l /usr/bin | jc --ls -p ``` ### lsblk ``` -$ lsblk | jc --lsblk -p +$ lsblk | jc --lsblk -p # or: jc -p lsblk [ { "name": "sda", @@ -962,7 +984,7 @@ $ lsblk | jc --lsblk -p ``` ### lsmod ``` -$ lsmod | jc --lsmod -p +$ lsmod | jc --lsmod -p # or: jc -p lsmod [ ... { @@ -1009,7 +1031,7 @@ $ lsmod | jc --lsmod -p ``` ### lsof ``` -$ sudo lsof | jc --lsof -p +$ sudo lsof | jc --lsof -p # or: jc -p lsof [ { "command": "systemd", @@ -1052,7 +1074,7 @@ $ sudo lsof | jc --lsof -p ``` ### mount ``` -$ mount | jc --mount -p +$ mount | jc --mount -p # or: jc -p mount [ { "filesystem": "sysfs", @@ -1096,7 +1118,7 @@ $ mount | jc --mount -p ``` ### netstat ``` -$ sudo netstat -apee | jc --netstat -p +$ sudo netstat -apee | jc --netstat -p # or: sudo jc -p netstat -apee [ { "proto": "tcp", @@ -1248,7 +1270,7 @@ $ sudo netstat -apee | jc --netstat -p ``` ### pip list ``` -$ pip list | jc --pip-list -p +$ pip list | jc --pip-list -p # or: jc -p pip list # or: jc -p pip3 list [ { "package": "ansible", @@ -1268,7 +1290,7 @@ $ pip list | jc --pip-list -p ``` ### pip show ``` -$ pip show wrapt wheel | jc --pip-show -p +$ pip show wrapt wheel | jc --pip-show -p # or: jc -p pip show wrapt wheel # or: jc -p pip3 show wrapt wheel [ { "name": "wrapt", @@ -1298,7 +1320,7 @@ $ pip show wrapt wheel | jc --pip-show -p ``` ### ps ``` -$ ps -ef | jc --ps -p +$ ps -ef | jc --ps -p # or: jc -p ps -ef [ { "uid": "root", @@ -1334,7 +1356,7 @@ $ ps -ef | jc --ps -p ] ``` ``` -$ ps axu | jc --ps -p +$ ps axu | jc --ps -p # or: jc -p ps axu [ { "user": "root", @@ -1380,7 +1402,7 @@ $ ps axu | jc --ps -p ``` ### route ``` -$ route -ee | jc --route -p +$ route -ee | jc --route -p # or: jc -p route -ee [ { "destination": "default", @@ -1425,7 +1447,7 @@ $ route -ee | jc --route -p ``` ### ss ``` -$ sudo ss -a | jc --ss -p +$ sudo ss -a | jc --ss -p # or: sudo jc -p ss -a [ { "netid": "nl", @@ -1544,7 +1566,7 @@ $ sudo ss -a | jc --ss -p ``` ### stat ``` -$ stat /bin/* | jc --stat -p +$ stat /bin/* | jc --stat -p # or: jc -p stat /bin/* [ { "file": "/bin/bash", @@ -1591,7 +1613,7 @@ $ stat /bin/* | jc --stat -p ``` ### systemctl ``` -$ systemctl -a | jc --systemctl -p +$ systemctl -a | jc --systemctl -p # or: jc -p systemctl -a [ { "unit": "proc-sys-fs-binfmt_misc.automount", @@ -1619,7 +1641,7 @@ $ systemctl -a | jc --systemctl -p ``` ### systemctl list-jobs ``` -$ systemctl list-jobs| jc --systemctl-lj -p +$ systemctl list-jobs | jc --systemctl-lj -p # or: jc -p systemctl list-jobs [ { "job": 3543, @@ -1643,7 +1665,7 @@ $ systemctl list-jobs| jc --systemctl-lj -p ``` ### systemctl list-sockets ``` -$ systemctl list-sockets | jc --systemctl-ls -p +$ systemctl list-sockets | jc --systemctl-ls -p # or: jc -p systemctl list-sockets [ { "listen": "/dev/log", @@ -1665,7 +1687,7 @@ $ systemctl list-sockets | jc --systemctl-ls -p ``` ### systemctl list-unit-files ``` -$ systemctl list-unit-files | jc --systemctl-luf -p +$ systemctl list-unit-files | jc --systemctl-luf -p # or: jc -p systemctl list-unit-files [ { "unit_file": "proc-sys-fs-binfmt_misc.automount", @@ -1684,7 +1706,7 @@ $ systemctl list-unit-files | jc --systemctl-luf -p ``` ### uname -a ``` -$ uname -a | jc --uname -p +$ uname -a | jc --uname -p # or: jc -p uname -a { "kernel_name": "Linux", "node_name": "user-ubuntu", @@ -1698,7 +1720,7 @@ $ uname -a | jc --uname -p ``` ### uptime ``` -$ uptime | jc --uptime -p +$ uptime | jc --uptime -p # or: jc -p uptime { "time": "11:30:44", "uptime": "1 day, 21:17", @@ -1710,7 +1732,7 @@ $ uptime | jc --uptime -p ``` ### w ``` -$ w | jc --w -p +$ w | jc --w -p # or: jc -p w [ { "user": "root", @@ -1792,7 +1814,7 @@ $ cat cd_catalog.xml | jc --xml -p ``` ### YAML files ``` -$ cat istio-mtls-permissive.yaml +$ cat istio.yaml apiVersion: "authentication.istio.io/v1alpha1" kind: "Policy" metadata: @@ -1813,7 +1835,7 @@ spec: tls: mode: ISTIO_MUTUAL -$ cat istio-mtls-permissive.yaml | jc --yaml -p +$ cat istio.yaml | jc --yaml -p [ { "apiVersion": "authentication.istio.io/v1alpha1", From 474eb0f3fdeaa612e189d745b12a5ed9c6e0a31b Mon Sep 17 00:00:00 2001 From: Kelly Brazil Date: Thu, 13 Feb 2020 17:24:10 -0800 Subject: [PATCH 21/27] doc updates --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 481d2751..dad0f1e7 100755 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ $ ls -l /usr/bin | jc --ls | jq '.[] | select(.size > 50000000)' "date": "Aug 14 19:41" } ``` -or using the alternative syntax: +or using the alternative "magic" syntax: ``` $ jc ls -l /usr/bin | jq '.[] | select(.size > 50000000)' { @@ -1031,7 +1031,7 @@ $ lsmod | jc --lsmod -p # or: jc -p lsmod ``` ### lsof ``` -$ sudo lsof | jc --lsof -p # or: jc -p lsof +$ sudo lsof | jc --lsof -p # or: sudo jc -p lsof [ { "command": "systemd", From 18fb69e36e303a3f9530dcb80354b25bfdbb931d Mon Sep 17 00:00:00 2001 From: Kelly Brazil Date: Thu, 13 Feb 2020 17:29:45 -0800 Subject: [PATCH 22/27] docs/parsers link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index dad0f1e7..e6e9b356 100755 --- a/README.md +++ b/README.md @@ -63,7 +63,7 @@ Two representations of the data are possible. The default representation uses a To access the raw, pre-processed JSON, use the `-r` cli option or the `raw=True` function parameter in `parse()`. -Schemas for each parser can be found in the `docs/parsers` folder. +Schemas for each parser can be found in the [`docs/parsers`](https://github.com/kellyjonbrazil/jc/tree/dev/docs/parsers) folder. ## Installation ``` From cfba62db20674d788601354211a10e2b1675a0c9 Mon Sep 17 00:00:00 2001 From: Kelly Brazil Date: Thu, 13 Feb 2020 17:48:21 -0800 Subject: [PATCH 23/27] correct parser search in magic() --- jc/cli.py | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/jc/cli.py b/jc/cli.py index caa3f217..b3e9c35a 100644 --- a/jc/cli.py +++ b/jc/cli.py @@ -206,19 +206,26 @@ def magic(): # find the command and parser for parser in parser_info: if 'magic_commands' in parser: + # first pass for two word commands: e.g. 'pip list' for magic_command in parser['magic_commands']: try: - # two arguments: e.g. 'pip list' if ' '.join(args_given[0:2]) == magic_command: found_parser = parser['argument'] break - # one argument: e.g. 'ls' - elif args_given[0] == magic_command: - found_parser = parser['argument'] - break # No command found - use standard syntax (for cases like 'jc -a') except Exception: - return + break + + # second pass for one word commands: e.g. 'ls' + if not found_parser: + for magic_command in parser['magic_commands']: + try: + if args_given[0] == magic_command: + found_parser = parser['argument'] + break + # No command found - use standard syntax (for cases like 'jc -a') + except Exception: + return # construct a new command line using the standard syntax: COMMAND | jc --PARSER -OPTIONS run_command = ' '.join(args_given) From 13bcdbc6c9a81bc5d52fe7a84e6a0a34908db8bc Mon Sep 17 00:00:00 2001 From: Kelly Brazil Date: Thu, 13 Feb 2020 17:50:51 -0800 Subject: [PATCH 24/27] doc update --- jc/cli.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jc/cli.py b/jc/cli.py index b3e9c35a..73024747 100644 --- a/jc/cli.py +++ b/jc/cli.py @@ -212,7 +212,7 @@ def magic(): if ' '.join(args_given[0:2]) == magic_command: found_parser = parser['argument'] break - # No command found - use standard syntax (for cases like 'jc -a') + # No command found - go to next loop (for cases like 'jc -a') except Exception: break From 6ba64f1128373843b068226424a3af545ac9d22c Mon Sep 17 00:00:00 2001 From: Kelly Brazil Date: Thu, 13 Feb 2020 18:19:19 -0800 Subject: [PATCH 25/27] usage update --- README.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index e6e9b356..8acc0606 100755 --- a/README.md +++ b/README.md @@ -71,6 +71,9 @@ $ pip3 install --upgrade jc ``` ## Usage +`jc` accepts piped input from `STDIN` and outputs a JSON representation of the previous command's output to `STDOUT`. + +Alternatively, the "magic" syntax can be used by prepending `jc` to the command to be converted. Options can be passed to `jc` immediately before the command is given. ``` COMMAND | jc PARSER [OPTIONS] ``` @@ -78,12 +81,12 @@ or ``` COMMAND | jc [OPTIONS] PARSER ``` -or +or using the "magic" syntax ``` jc [OPTIONS] COMMAND ``` -`jc` accepts piped input from `STDIN` and outputs a JSON representation of the previous command's output to `STDOUT`. The JSON output can be compact or pretty formatted. +The JSON output can be compact (default) or pretty formatted with the `-p` option. ### Parsers - `--arp` enables the `arp` command parser From f0e3846c038ec5507cd4a19980d83ee0fb969ef5 Mon Sep 17 00:00:00 2001 From: Kelly Brazil Date: Thu, 13 Feb 2020 18:20:22 -0800 Subject: [PATCH 26/27] formatting --- README.md | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/README.md b/README.md index 8acc0606..e783610f 100755 --- a/README.md +++ b/README.md @@ -72,8 +72,6 @@ $ pip3 install --upgrade jc ## Usage `jc` accepts piped input from `STDIN` and outputs a JSON representation of the previous command's output to `STDOUT`. - -Alternatively, the "magic" syntax can be used by prepending `jc` to the command to be converted. Options can be passed to `jc` immediately before the command is given. ``` COMMAND | jc PARSER [OPTIONS] ``` @@ -81,11 +79,10 @@ or ``` COMMAND | jc [OPTIONS] PARSER ``` -or using the "magic" syntax +Alternatively, the "magic" syntax can be used by prepending `jc` to the command to be converted. Options can be passed to `jc` immediately before the command is given. ``` jc [OPTIONS] COMMAND ``` - The JSON output can be compact (default) or pretty formatted with the `-p` option. ### Parsers From 14d6d8b84f0ddeff984aff486e2e697a91ac6fb6 Mon Sep 17 00:00:00 2001 From: Kelly Brazil Date: Thu, 13 Feb 2020 18:24:53 -0800 Subject: [PATCH 27/27] version bump to 1.7.3 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index f1382252..e0db21c3 100755 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ with open('README.md', 'r') as f: setuptools.setup( name='jc', - version='1.7.2', + version='1.7.3', author='Kelly Brazil', author_email='kellyjonbrazil@gmail.com', description='This tool serializes the output of popular command line tools and filetypes to structured JSON output.',