1
0
mirror of https://github.com/kellyjonbrazil/jc.git synced 2025-06-19 00:17:51 +02:00

First commit

This commit is contained in:
Kelly Brazil
2019-10-15 15:06:09 -07:00
commit 3e576250b1
11 changed files with 429 additions and 0 deletions

6
.gitignore vendored Executable file
View File

@ -0,0 +1,6 @@
__pycache__
*.pyc
dist/
build/
*.egg-info/
jc/parsers.old/

21
LICENSE.md Executable file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2019 Kelly Brazil
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

53
README.md Executable file
View File

@ -0,0 +1,53 @@
# JC
JSON CLI output utility
v0.1
`jc` is used to JSONify the output of many standard linux cli tools for easier parsing in scripts. Parsers for `ls`, `ifconfig`, and `netstat` are currently included and more can be added via modules.
## Usage
`jc` accepts piped input from `STDIN` and outputs a JSON representation of the previous command to `STDOUT`. The JSON output can be compact or pretty formatted.
The first argument is required and identifies the command that is piping output to `jc` input. For example:
- `--ls` enables the `ls` parser
- `--ifconfig` enables the `ifconfig` parser
- `--netstat` enables the `netstat` parser
The second `-p` argument is optional and specifies whether to pretty format the JSON output.
## Examples
```
$ ls -l /bin | jc --ls -p
[
{
"filename": "bash",
"flags": "-r-xr-xr-x",
"links": 1,
"owner": "root",
"group": "wheel",
"bytes": 618416,
"date": "May 3 22:26"
},
{
"filename": "cat",
"flags": "-rwxr-xr-x",
"links": 1,
"owner": "root",
"group": "wheel",
"bytes": 23648,
"date": "May 3 22:26"
},
{
"filename": "chmod",
"flags": "-rwxr-xr-x",
"links": 1,
"owner": "root",
"group": "wheel",
"bytes": 30016,
"date": "May 3 22:26"
},
...
]
```

5
build-package.sh Executable file
View File

@ -0,0 +1,5 @@
#!/bin/bash
# build jc PIP package
# to install locally, run: pip3 install jc-x.x.tar.gz
python3 setup.py sdist bdist_wheel

61
jc/__init__.py Normal file
View File

@ -0,0 +1,61 @@
"""JC - JSON CLI output utility
v0.1
* kellyjonbrazil@gmail.com
This module serializes standard unix command line output to structured JSON
output.
Example:
$ ls -al | jc | jq .
[
{
"filename": ".",
"suffix": Null,
"bytes": 224,
"date_updated": "Oct 1 12:09",
"owner_user": "joeuser",
"owner_group": "staff",
"flags": "drwxr-xr-x+",
"link_to": Null,
"links": 47
},
{
"filename": "..",
"suffix": Null,
"bytes": 224,
"date_updated": "Oct 1 12:09",
"owner_user": "admin",
"owner_group": "root",
"flags": "drwxr-xr-x",
"link_to": Null,
"links": 7
},
{
"filename": "testfile.txt",
"suffix": "txt",
"bytes": 14686,
"date_updated": "Oct 1 12:09",
"owner_user": "joeuser",
"owner_group": "staff",
"flags": "-rwxr-xr-x@",
"link_to": Null,
"links": 1
},
{
"filename": "ncat",
"suffix": Null,
"bytes": 14686,
"date_updated": "Oct 1 12:09",
"owner_user": "joeuser",
"owner_group": "staff",
"flags": "lrwxr-xr-x",
"link_to": "../Cellar/nmap/7.70/bin/ncat",
"links": 1
}
]
"""
name = 'jc'

38
jc/jc.py Executable file
View File

@ -0,0 +1,38 @@
#!/usr/bin/env python3
"""jc - JSON CLI output utility
Main input module
"""
import sys
import json
import parsers
from parsers import *
pretty = False
data = sys.stdin.read()
if len(sys.argv) < 2:
print(f'\nError: {sys.argv[0]}\n Must specify parser. (e.g. --ls, --netstat, --ifconfig, etc.)')
print(' Use -p to pretty print')
print(f'\nExample: ls -al | {sys.argv[0]} --ls -p\n')
exit()
arg = sys.argv[1]
if len(sys.argv) > 2:
if sys.argv[2] == '-p':
pretty = True
if arg == '--ifconfig':
result = parsers.ifconfig.parse(data)
elif arg == '--ls':
result = parsers.ls.parse(data)
elif arg == '--netstat':
result = parsers.netstat.parse(data)
# output resulting dictionary as json
if pretty:
print(json.dumps(result, indent=2))
else:
print(json.dumps(result))

3
jc/parsers/__init__.py Normal file
View File

@ -0,0 +1,3 @@
__all__ = ['ifconfig',
'ls',
'netstat']

30
jc/parsers/ifconfig.py Normal file
View File

@ -0,0 +1,30 @@
"""jc - JSON CLI output utility ifconfig Parser
Usage:
specify --ifconfig as the first argument if the piped input is coming from ifconfig
no ifconfig options are supported.
Example:
$ ifconfig | ./jc.py --ifconfig -p
"""
from collections import namedtuple
from ifconfigparser import IfconfigParser
def parse(data):
output = []
parsed = IfconfigParser(console_output=data)
interfaces = parsed.get_interfaces()
# convert ifconfigparser output to a dictionary
for iface in interfaces:
d = interfaces[iface]._asdict()
dct = dict(d)
output.append(dct)
return output

130
jc/parsers/ls.py Normal file
View File

@ -0,0 +1,130 @@
"""jc - JSON CLI output utility ls Parser
Usage:
specify --ls as the first argument if the piped input is coming from ls
ls options supported:
- None
- l
- a
Examples:
$ ls -a /usr/bin | jc --ls -p
[
{
"filename": "."
},
{
"filename": ".."
},
{
"filename": "2to3-"
},
{
"filename": "2to3-2.7"
},
{
"filename": "AssetCacheLocatorUtil"
},
...
]
$ ls -al /usr/bin | jc --ls -p
[
{
"filename": ".",
"flags": "drwxr-xr-x",
"links": 970,
"owner": "root",
"group": "wheel",
"bytes": 31040,
"date": "Aug 27 21:20"
},
{
"filename": "..",
"flags": "drwxr-xr-x@",
"links": 9,
"owner": "root",
"group": "wheel",
"bytes": 288,
"date": "May 3 22:14"
},
{
"filename": "2to3-",
"flags": "-rwxr-xr-x",
"links": 4,
"owner": "root",
"group": "wheel",
"bytes": 925,
"date": "Feb 22 2019"
},
{
"filename": "2to3-2.7",
"link_to": "../../System/Library/Frameworks/Python.framework/Versions/2.7/bin/2to3-2.7",
"flags": "lrwxr-xr-x",
"links": 1,
"owner": "root",
"group": "wheel",
"bytes": 74,
"date": "May 4 02:12"
},
...
]
$ $ ls -l /usr/bin | jc --ls | jq .[] | jq 'select(.bytes > 50000000)'
{
"filename": "emacs",
"flags": "-r-xr-xr-x",
"links": 1,
"owner": "root",
"group": "wheel",
"bytes": 117164432,
"date": "May 3 22:26"
}
"""
import re
def parse(data):
output = []
cleandata = data.splitlines()
# Delete first line if it starts with 'total'
if cleandata[0].find('total') == 0:
cleandata.pop(0)
# Delete last line if it is blank
if cleandata[-1] == '':
cleandata.pop(-1)
# Check if -l was used to parse extra data
if re.match('^[-dclpsbDCMnP?]([-r][-w][-xsS]){2}([-r][-w][-xtT])[+]?', cleandata[0]):
for entry in cleandata:
output_line = {}
parsed_line = entry.split()
# split filenames and links
filename_field = ' '.join(parsed_line[8:]).split(' -> ')
# create list of dictionaries
output_line['filename'] = filename_field[0]
if len(filename_field) > 1:
output_line['link_to'] = filename_field[1]
output_line['flags'] = parsed_line[0]
output_line['links'] = int(parsed_line[1])
output_line['owner'] = parsed_line[2]
output_line['group'] = parsed_line[3]
output_line['bytes'] = int(parsed_line[4])
output_line['date'] = ' '.join(parsed_line[5:8])
output.append(output_line)
else:
for entry in cleandata:
output_line = {}
output_line['filename'] = entry
output.append(output_line)
return output

55
jc/parsers/netstat.py Normal file
View File

@ -0,0 +1,55 @@
"""jc - JSON CLI output utility netstat Parser
Usage:
specify --netstat as the first argument if the piped input is coming from netstat
Example:
$ netstat | jc --netstat -p
"""
import re
def parse(data):
output = []
cleandata = data.splitlines()
# Delete last line if it is blank
if cleandata[-1] == '':
cleandata.pop(-1)
# Delete first line if it starts with 'total'
if cleandata[0].find('total') == 0:
cleandata.pop(0)
# Check if -l was used to parse extra data
if re.match('^[-dclpsbDCMnP?]([-r][-w][-xsS]){2}([-r][-w][-xtT])[+]?', cleandata[0]):
for entry in cleandata:
output_line = {}
parsed_line = entry.split()
# split filenames and links
filename_field = ' '.join(parsed_line[8:]).split(' -> ')
# create list of dictionaries
output_line['filename'] = filename_field[0]
if len(filename_field) > 1:
output_line['link_to'] = filename_field[1]
output_line['flags'] = parsed_line[0]
output_line['links'] = int(parsed_line[1])
output_line['owner'] = parsed_line[2]
output_line['group'] = parsed_line[3]
output_line['bytes'] = int(parsed_line[4])
output_line['date'] = ' '.join(parsed_line[5:8])
output.append(output_line)
else:
for entry in cleandata:
output_line = {}
output_line['filename'] = entry
output.append(output_line)
return output

27
setup.py Executable file
View File

@ -0,0 +1,27 @@
import setuptools
with open("README.md", "r") as f:
long_description = f.read()
setuptools.setup(
name="jc",
version="0.1",
author="Kelly Brazil",
description="This tool serializes the output of popular command line tools to structured JSON output.",
install_requires=[
collections,
ifconfigparser,
json
],
license='MIT',
long_description=long_description,
long_description_content_type="text/markdown",
python_requires='~=3.4',
url="https://github.com/kellyjonbrazil/jc",
packages=setuptools.find_packages(),
classifiers=[
"Programming Language :: Python :: 3",
"License :: OSI Approved :: MIT License",
"Operating System :: OS Independent",
],
)