mirror of
https://github.com/kellyjonbrazil/jc.git
synced 2025-06-19 00:17:51 +02:00
First commit
This commit is contained in:
6
.gitignore
vendored
Executable file
6
.gitignore
vendored
Executable file
@ -0,0 +1,6 @@
|
||||
__pycache__
|
||||
*.pyc
|
||||
dist/
|
||||
build/
|
||||
*.egg-info/
|
||||
jc/parsers.old/
|
21
LICENSE.md
Executable file
21
LICENSE.md
Executable 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
53
README.md
Executable 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
5
build-package.sh
Executable 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
61
jc/__init__.py
Normal 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
38
jc/jc.py
Executable 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
3
jc/parsers/__init__.py
Normal file
@ -0,0 +1,3 @@
|
||||
__all__ = ['ifconfig',
|
||||
'ls',
|
||||
'netstat']
|
30
jc/parsers/ifconfig.py
Normal file
30
jc/parsers/ifconfig.py
Normal 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
130
jc/parsers/ls.py
Normal 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
55
jc/parsers/netstat.py
Normal 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
27
setup.py
Executable 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",
|
||||
],
|
||||
)
|
Reference in New Issue
Block a user