mirror of
https://github.com/kellyjonbrazil/jc.git
synced 2026-04-03 17:44:07 +02:00
Compare commits
37 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4c8610c54f | ||
|
|
c8f886dc8f | ||
|
|
4cfc2d22b3 | ||
|
|
59238c8540 | ||
|
|
30080c0165 | ||
|
|
fab80bb3b4 | ||
|
|
a9f2df8054 | ||
|
|
1d110be6cb | ||
|
|
be81b5e1ed | ||
|
|
5f88f7d8a0 | ||
|
|
e57c7cc8ef | ||
|
|
b216627c10 | ||
|
|
6e925eab13 | ||
|
|
d54d906c57 | ||
|
|
0040febbf0 | ||
|
|
e416c77bed | ||
|
|
9181d242aa | ||
|
|
d6f94c53a4 | ||
|
|
a9294f32a0 | ||
|
|
4d93b38fe4 | ||
|
|
77b74c5507 | ||
|
|
51a1d3e6f4 | ||
|
|
2eba30422b | ||
|
|
43ed09ce5b | ||
|
|
367ab54f94 | ||
|
|
4f552e370e | ||
|
|
7571139f79 | ||
|
|
8ec1bec317 | ||
|
|
c04895407f | ||
|
|
cec73d6131 | ||
|
|
756c2bc9ac | ||
|
|
084048987c | ||
|
|
02d97394dd | ||
|
|
177f948f97 | ||
|
|
c2b013150e | ||
|
|
0bec67c29c | ||
|
|
8073d15fe1 |
443
README.md
443
README.md
@@ -1,52 +1,57 @@
|
||||
# JC
|
||||
JSON CLI output utility
|
||||
|
||||
`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.
|
||||
`jc` is used to JSONify the output of many standard linux cli tools for easier parsing in scripts. Parsers for `ls`, `ifconfig`, `ps`, `route`, and `netstat` are currently included and more can be added via modules.
|
||||
|
||||
This allows further command line processing of output with tools like `jq` simply by piping commands:
|
||||
|
||||
```
|
||||
$ 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"
|
||||
}
|
||||
```
|
||||
|
||||
The `jc` parsers can also be used as python modules by referencing them via:
|
||||
```
|
||||
import jc.parsers.[parser]
|
||||
|
||||
data = 'data to parse'
|
||||
jc.parsers.[parser].parse(data)
|
||||
```
|
||||
In this case the output will be a python dictionary instead of JSON.
|
||||
|
||||
## Installation
|
||||
```
|
||||
$ pip3 install jc
|
||||
```
|
||||
|
||||
## 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.
|
||||
```
|
||||
jc [parser] [options]
|
||||
```
|
||||
|
||||
The first argument is required and identifies the command that is piping output to `jc` input. For example:
|
||||
- `--ls` enables the `ls` parser
|
||||
`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:
|
||||
- `--ifconfig` enables the `ifconfig` parser
|
||||
- `--ls` enables the `ls` parser
|
||||
- `--netstat` enables the `netstat` parser
|
||||
- `--ps` enables the `ps` parser
|
||||
- `--route` enables the `route` parser
|
||||
|
||||
The second `-p` argument is optional and specifies whether to pretty format the JSON output.
|
||||
Options:
|
||||
- `-p` specifies whether to pretty format the JSON output
|
||||
|
||||
## Examples
|
||||
### ifconfig
|
||||
```
|
||||
$ 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"
|
||||
},
|
||||
...
|
||||
]
|
||||
|
||||
$ ifconfig | jc --ifconfig -p
|
||||
[
|
||||
{
|
||||
@@ -128,131 +133,251 @@ $ ifconfig | jc --ifconfig -p
|
||||
"metric": null
|
||||
}
|
||||
]
|
||||
|
||||
```
|
||||
### ls
|
||||
```
|
||||
$ 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"
|
||||
},
|
||||
...
|
||||
]
|
||||
```
|
||||
### netstat
|
||||
```
|
||||
$ netstat -p | jc --netstat -p
|
||||
{
|
||||
"client": {
|
||||
"tcp": {
|
||||
"ipv4": [
|
||||
{
|
||||
"local_address": "localhost.localdo",
|
||||
"local_port": "34480",
|
||||
"foreign_address": "lb-192-30-255-113",
|
||||
"foreign_port": "https",
|
||||
"state": "ESTABLISHED",
|
||||
"pid": 53550,
|
||||
"program_name": "git-remote-ht",
|
||||
"receive_q": 0,
|
||||
"send_q": 0
|
||||
},
|
||||
{
|
||||
"local_address": "localhost.localdo",
|
||||
"local_port": "34478",
|
||||
"foreign_address": "lb-192-30-255-113",
|
||||
"foreign_port": "https",
|
||||
"state": "ESTABLISHED",
|
||||
"pid": 53550,
|
||||
"program_name": "git-remote-ht",
|
||||
"receive_q": 0,
|
||||
"send_q": 0
|
||||
}
|
||||
]
|
||||
}
|
||||
[
|
||||
{
|
||||
"session_protocol": "tcp",
|
||||
"network_protocol": "ipv4",
|
||||
"local_address": "localhost.localdo",
|
||||
"local_port": "34480",
|
||||
"foreign_address": "lb-192-30-255-113",
|
||||
"foreign_port": "https",
|
||||
"state": "ESTABLISHED",
|
||||
"pid": 53550,
|
||||
"program_name": "git-remote-ht",
|
||||
"receive_q": 0,
|
||||
"send_q": 0
|
||||
},
|
||||
{
|
||||
"session_protocol": "tcp",
|
||||
"network_protocol": "ipv4",
|
||||
"local_address": "localhost.localdo",
|
||||
"local_port": "34478",
|
||||
"foreign_address": "lb-192-30-255-113",
|
||||
"foreign_port": "https",
|
||||
"state": "ESTABLISHED",
|
||||
"pid": 53550,
|
||||
"program_name": "git-remote-ht",
|
||||
"receive_q": 0,
|
||||
"send_q": 0
|
||||
}
|
||||
}
|
||||
|
||||
$ netstat -lp | jc --netstat -p
|
||||
{
|
||||
"server": {
|
||||
"tcp": {
|
||||
"ipv4": [
|
||||
{
|
||||
"local_address": "localhost",
|
||||
"local_port": "smtp",
|
||||
"foreign_address": "0.0.0.0",
|
||||
"foreign_port": "*",
|
||||
"state": "LISTEN",
|
||||
"pid": 1594,
|
||||
"program_name": "master",
|
||||
"receive_q": 0,
|
||||
"send_q": 0
|
||||
},
|
||||
{
|
||||
"local_address": "0.0.0.0",
|
||||
"local_port": "ssh",
|
||||
"foreign_address": "0.0.0.0",
|
||||
"foreign_port": "*",
|
||||
"state": "LISTEN",
|
||||
"pid": 21918,
|
||||
"program_name": "sshd",
|
||||
"receive_q": 0,
|
||||
"send_q": 0
|
||||
}
|
||||
],
|
||||
"ipv6": [
|
||||
{
|
||||
"local_address": "localhost",
|
||||
"local_port": "smtp",
|
||||
"foreign_address": "[::]",
|
||||
"foreign_port": "*",
|
||||
"state": "LISTEN",
|
||||
"pid": 1594,
|
||||
"program_name": "master",
|
||||
"receive_q": 0,
|
||||
"send_q": 0
|
||||
},
|
||||
{
|
||||
"local_address": "[::]",
|
||||
"local_port": "ssh",
|
||||
"foreign_address": "[::]",
|
||||
"foreign_port": "*",
|
||||
"state": "LISTEN",
|
||||
"pid": 21918,
|
||||
"program_name": "sshd",
|
||||
"receive_q": 0,
|
||||
"send_q": 0
|
||||
}
|
||||
]
|
||||
},
|
||||
"udp": {
|
||||
"ipv4": [
|
||||
{
|
||||
"local_address": "0.0.0.0",
|
||||
"local_port": "bootpc",
|
||||
"foreign_address": "0.0.0.0",
|
||||
"foreign_port": "*",
|
||||
"pid": 13903,
|
||||
"program_name": "dhclient",
|
||||
"receive_q": 0,
|
||||
"send_q": 0
|
||||
},
|
||||
{
|
||||
"local_address": "localhost",
|
||||
"local_port": "323",
|
||||
"foreign_address": "0.0.0.0",
|
||||
"foreign_port": "*",
|
||||
"pid": 30926,
|
||||
"program_name": "chronyd",
|
||||
"receive_q": 0,
|
||||
"send_q": 0
|
||||
}
|
||||
],
|
||||
"ipv6": [
|
||||
{
|
||||
"local_address": "localhost",
|
||||
"local_port": "323",
|
||||
"foreign_address": "[::]",
|
||||
"foreign_port": "*",
|
||||
"pid": 30926,
|
||||
"program_name": "chronyd",
|
||||
"receive_q": 0,
|
||||
"send_q": 0
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
```
|
||||
```
|
||||
$ netstat -lpn | jc --netstat -p
|
||||
[
|
||||
{
|
||||
"session_protocol": "tcp",
|
||||
"network_protocol": "ipv4",
|
||||
"local_address": "127.0.0.1",
|
||||
"local_port": "42351",
|
||||
"foreign_address": "0.0.0.0",
|
||||
"foreign_port": "*",
|
||||
"state": "LISTEN",
|
||||
"pid": 1112,
|
||||
"program_name": "containerd",
|
||||
"receive_q": 0,
|
||||
"send_q": 0
|
||||
},
|
||||
{
|
||||
"session_protocol": "tcp",
|
||||
"network_protocol": "ipv4",
|
||||
"local_address": "127.0.0.53",
|
||||
"local_port": "53",
|
||||
"foreign_address": "0.0.0.0",
|
||||
"foreign_port": "*",
|
||||
"state": "LISTEN",
|
||||
"pid": 885,
|
||||
"program_name": "systemd-resolve",
|
||||
"receive_q": 0,
|
||||
"send_q": 0
|
||||
},
|
||||
{
|
||||
"session_protocol": "tcp",
|
||||
"network_protocol": "ipv4",
|
||||
"local_address": "0.0.0.0",
|
||||
"local_port": "22",
|
||||
"foreign_address": "0.0.0.0",
|
||||
"foreign_port": "*",
|
||||
"state": "LISTEN",
|
||||
"pid": 1127,
|
||||
"program_name": "sshd",
|
||||
"receive_q": 0,
|
||||
"send_q": 0
|
||||
},
|
||||
{
|
||||
"session_protocol": "tcp",
|
||||
"network_protocol": "ipv6",
|
||||
"local_address": "::",
|
||||
"local_port": "22",
|
||||
"foreign_address": "::",
|
||||
"foreign_port": "*",
|
||||
"state": "LISTEN",
|
||||
"pid": 1127,
|
||||
"program_name": "sshd",
|
||||
"receive_q": 0,
|
||||
"send_q": 0
|
||||
},
|
||||
{
|
||||
"session_protocol": "udp",
|
||||
"network_protocol": "ipv4",
|
||||
"local_address": "127.0.0.53",
|
||||
"local_port": "53",
|
||||
"foreign_address": "0.0.0.0",
|
||||
"foreign_port": "*",
|
||||
"pid": 885,
|
||||
"program_name": "systemd-resolve",
|
||||
"receive_q": 0,
|
||||
"send_q": 0
|
||||
},
|
||||
{
|
||||
"session_protocol": "udp",
|
||||
"network_protocol": "ipv4",
|
||||
"local_address": "192.168.71.131",
|
||||
"local_port": "68",
|
||||
"foreign_address": "0.0.0.0",
|
||||
"foreign_port": "*",
|
||||
"pid": 867,
|
||||
"program_name": "systemd-network",
|
||||
"receive_q": 0,
|
||||
"send_q": 0
|
||||
}
|
||||
}
|
||||
]
|
||||
```
|
||||
### ps
|
||||
```
|
||||
$ ps -ef | jc --ps -p
|
||||
[
|
||||
{
|
||||
"UID": "root",
|
||||
"PID": "1",
|
||||
"PPID": "0",
|
||||
"C": "0",
|
||||
"STIME": "13:58",
|
||||
"TTY": "?",
|
||||
"TIME": "00:00:05",
|
||||
"CMD": "/lib/systemd/systemd --system --deserialize 35"
|
||||
},
|
||||
{
|
||||
"UID": "root",
|
||||
"PID": "2",
|
||||
"PPID": "0",
|
||||
"C": "0",
|
||||
"STIME": "13:58",
|
||||
"TTY": "?",
|
||||
"TIME": "00:00:00",
|
||||
"CMD": "[kthreadd]"
|
||||
},
|
||||
{
|
||||
"UID": "root",
|
||||
"PID": "4",
|
||||
"PPID": "2",
|
||||
"C": "0",
|
||||
"STIME": "13:58",
|
||||
"TTY": "?",
|
||||
"TIME": "00:00:00",
|
||||
"CMD": "[kworker/0:0H]"
|
||||
},
|
||||
{
|
||||
"UID": "root",
|
||||
"PID": "6",
|
||||
"PPID": "2",
|
||||
"C": "0",
|
||||
"STIME": "13:58",
|
||||
"TTY": "?",
|
||||
"TIME": "00:00:00",
|
||||
"CMD": "[mm_percpu_wq]"
|
||||
},
|
||||
...
|
||||
]
|
||||
```
|
||||
### route
|
||||
```
|
||||
$ route -n | jc --route -p
|
||||
[
|
||||
{
|
||||
"Destination": "0.0.0.0",
|
||||
"Gateway": "192.168.71.2",
|
||||
"Genmask": "0.0.0.0",
|
||||
"Flags": "UG",
|
||||
"Metric": "100",
|
||||
"Ref": "0",
|
||||
"Use": "0",
|
||||
"Iface": "ens33"
|
||||
},
|
||||
{
|
||||
"Destination": "172.17.0.0",
|
||||
"Gateway": "0.0.0.0",
|
||||
"Genmask": "255.255.0.0",
|
||||
"Flags": "U",
|
||||
"Metric": "0",
|
||||
"Ref": "0",
|
||||
"Use": "0",
|
||||
"Iface": "docker0"
|
||||
},
|
||||
{
|
||||
"Destination": "192.168.71.0",
|
||||
"Gateway": "0.0.0.0",
|
||||
"Genmask": "255.255.255.0",
|
||||
"Flags": "U",
|
||||
"Metric": "0",
|
||||
"Ref": "0",
|
||||
"Use": "0",
|
||||
"Iface": "ens33"
|
||||
},
|
||||
{
|
||||
"Destination": "192.168.71.2",
|
||||
"Gateway": "0.0.0.0",
|
||||
"Genmask": "255.255.255.255",
|
||||
"Flags": "UH",
|
||||
"Metric": "100",
|
||||
"Ref": "0",
|
||||
"Use": "0",
|
||||
"Iface": "ens33"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
## Contributions
|
||||
Feel free to add/improve code or parsers!
|
||||
|
||||
|
||||
## Acknowledgments
|
||||
- `ifconfig-parser` module from https://github.com/KnightWhoSayNi/ifconfig-parser
|
||||
- Parsing code from Conor Heine at https://gist.github.com/cahna/43a1a3ff4d075bcd71f9d7120037a501
|
||||
|
||||
15
changelog.txt
Normal file
15
changelog.txt
Normal file
@@ -0,0 +1,15 @@
|
||||
jc changelog
|
||||
|
||||
20191021 v0.6.3
|
||||
- Flatten netstat parser output
|
||||
- Clean up argument parsing
|
||||
- Fix command help
|
||||
|
||||
20191018 v0.5.5
|
||||
- Fix netstat -p parsing for Ubuntu
|
||||
- Add ps parser
|
||||
- Add route parser
|
||||
- ls parser fixes
|
||||
|
||||
20191017 v0.2.0
|
||||
- ifconfig, ls, and netstat support
|
||||
@@ -1,7 +1,5 @@
|
||||
"""JC - JSON CLI output utility
|
||||
|
||||
v0.1
|
||||
|
||||
* kellyjonbrazil@gmail.com
|
||||
|
||||
This module serializes standard unix command line output to structured JSON
|
||||
|
||||
48
jc/jc.py
48
jc/jc.py
@@ -9,35 +9,53 @@ import json
|
||||
import jc.parsers.ifconfig
|
||||
import jc.parsers.ls
|
||||
import jc.parsers.netstat
|
||||
import jc.parsers.ps
|
||||
import jc.parsers.route
|
||||
|
||||
|
||||
def main():
|
||||
pretty = False
|
||||
data = sys.stdin.read()
|
||||
pretty = False
|
||||
|
||||
if len(sys.argv) < 2:
|
||||
print(f'\nError: jc\n Must specify parser. (e.g. --ls, --netstat, --ifconfig, etc.)')
|
||||
print(' Use -p to pretty print')
|
||||
print(f'\nExample: ls -al | jc --ls -p\n')
|
||||
exit()
|
||||
if '-p' in sys.argv:
|
||||
pretty = True
|
||||
|
||||
arg = sys.argv[1]
|
||||
|
||||
if len(sys.argv) > 2:
|
||||
if sys.argv[2] == '-p':
|
||||
pretty = True
|
||||
|
||||
if arg == '--ifconfig':
|
||||
if '--ifconfig' in sys.argv:
|
||||
result = jc.parsers.ifconfig.parse(data)
|
||||
elif arg == '--ls':
|
||||
|
||||
elif '--ls' in sys.argv:
|
||||
result = jc.parsers.ls.parse(data)
|
||||
elif arg == '--netstat':
|
||||
|
||||
elif '--netstat' in sys.argv:
|
||||
result = jc.parsers.netstat.parse(data)
|
||||
|
||||
elif '--ps' in sys.argv:
|
||||
result = jc.parsers.ps.parse(data)
|
||||
|
||||
elif '--route' in sys.argv:
|
||||
result = jc.parsers.route.parse(data)
|
||||
|
||||
else:
|
||||
print('jc: missing arguments', file=sys.stderr)
|
||||
print('Usage: jc [parser] [options]\n', file=sys.stderr)
|
||||
print('Parsers:', file=sys.stderr)
|
||||
print(' --ifconfig iconfig parser', file=sys.stderr)
|
||||
print(' --ls ls parser', file=sys.stderr)
|
||||
print(' --netstat netstat parser', file=sys.stderr)
|
||||
print(' --ps ps parser', file=sys.stderr)
|
||||
print(' --route route parser\n', file=sys.stderr)
|
||||
print('Options:', file=sys.stderr)
|
||||
print(' -p pretty print output\n', file=sys.stderr)
|
||||
print('Example:', file=sys.stderr)
|
||||
print(' ls -al | jc --ls -p\n', file=sys.stderr)
|
||||
exit()
|
||||
|
||||
# output resulting dictionary as json
|
||||
if pretty:
|
||||
print(json.dumps(result, indent=2))
|
||||
else:
|
||||
print(json.dumps(result))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
||||
@@ -13,9 +13,10 @@ $ ifconfig | jc --ifconfig -p
|
||||
from collections import namedtuple
|
||||
from ifconfigparser import IfconfigParser
|
||||
|
||||
|
||||
def parse(data):
|
||||
output = []
|
||||
|
||||
|
||||
parsed = IfconfigParser(console_output=data)
|
||||
interfaces = parsed.get_interfaces()
|
||||
|
||||
@@ -24,7 +25,5 @@ def parse(data):
|
||||
d = interfaces[iface]._asdict()
|
||||
dct = dict(d)
|
||||
output.append(dct)
|
||||
|
||||
|
||||
return output
|
||||
|
||||
|
||||
|
||||
@@ -85,46 +85,48 @@ $ $ ls -l /usr/bin | jc --ls | jq .[] | jq 'select(.bytes > 50000000)'
|
||||
"""
|
||||
import re
|
||||
|
||||
|
||||
def parse(data):
|
||||
output = []
|
||||
|
||||
cleandata = data.splitlines()
|
||||
|
||||
linedata = data.splitlines()
|
||||
|
||||
# Delete first line if it starts with 'total'
|
||||
if cleandata[0].find('total') == 0:
|
||||
cleandata.pop(0)
|
||||
if linedata:
|
||||
if linedata[0].find('total') == 0:
|
||||
linedata.pop(0)
|
||||
|
||||
# Delete last line if it is blank
|
||||
if cleandata[-1] == '':
|
||||
cleandata.pop(-1)
|
||||
# Clear any blank lines
|
||||
cleandata = list(filter(None, linedata))
|
||||
|
||||
# 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 = {}
|
||||
if cleandata:
|
||||
# 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()
|
||||
parsed_line = entry.split(maxsplit=8)
|
||||
|
||||
# split filenames and links
|
||||
filename_field = ' '.join(parsed_line[8:]).split(' -> ')
|
||||
# split filenames and links
|
||||
filename_field = parsed_line[8].split(' -> ')
|
||||
|
||||
# create list of dictionaries
|
||||
output_line['filename'] = filename_field[0]
|
||||
# create list of dictionaries
|
||||
output_line['filename'] = filename_field[0]
|
||||
|
||||
if len(filename_field) > 1:
|
||||
output_line['link_to'] = filename_field[1]
|
||||
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)
|
||||
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
|
||||
|
||||
@@ -11,152 +11,145 @@ Limitations:
|
||||
Examples:
|
||||
|
||||
$ netstat -p | jc --netstat -p
|
||||
{
|
||||
"client": {
|
||||
"tcp": {
|
||||
"ipv4": [
|
||||
{
|
||||
"local_address": "localhost.localdo",
|
||||
"local_port": "34480",
|
||||
"foreign_address": "lb-192-30-255-113",
|
||||
"foreign_port": "https",
|
||||
"state": "ESTABLISHED",
|
||||
"pid": 53550,
|
||||
"program_name": "git-remote-ht",
|
||||
"receive_q": 0,
|
||||
"send_q": 0
|
||||
},
|
||||
{
|
||||
"local_address": "localhost.localdo",
|
||||
"local_port": "34478",
|
||||
"foreign_address": "lb-192-30-255-113",
|
||||
"foreign_port": "https",
|
||||
"state": "ESTABLISHED",
|
||||
"pid": 53550,
|
||||
"program_name": "git-remote-ht",
|
||||
"receive_q": 0,
|
||||
"send_q": 0
|
||||
}
|
||||
]
|
||||
}
|
||||
[
|
||||
{
|
||||
"session_protocol": "tcp",
|
||||
"network_protocol": "ipv4",
|
||||
"local_address": "localhost.localdo",
|
||||
"local_port": "34480",
|
||||
"foreign_address": "lb-192-30-255-113",
|
||||
"foreign_port": "https",
|
||||
"state": "ESTABLISHED",
|
||||
"pid": 53550,
|
||||
"program_name": "git-remote-ht",
|
||||
"receive_q": 0,
|
||||
"send_q": 0
|
||||
},
|
||||
{
|
||||
"session_protocol": "tcp",
|
||||
"network_protocol": "ipv4",
|
||||
"local_address": "localhost.localdo",
|
||||
"local_port": "34478",
|
||||
"foreign_address": "lb-192-30-255-113",
|
||||
"foreign_port": "https",
|
||||
"state": "ESTABLISHED",
|
||||
"pid": 53550,
|
||||
"program_name": "git-remote-ht",
|
||||
"receive_q": 0,
|
||||
"send_q": 0
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
$ netstat -lp | jc --netstat -p
|
||||
{
|
||||
"server": {
|
||||
"tcp": {
|
||||
"ipv4": [
|
||||
{
|
||||
"local_address": "localhost",
|
||||
"local_port": "smtp",
|
||||
"foreign_address": "0.0.0.0",
|
||||
"foreign_port": "*",
|
||||
"state": "LISTEN",
|
||||
"pid": 1594,
|
||||
"program_name": "master",
|
||||
"receive_q": 0,
|
||||
"send_q": 0
|
||||
},
|
||||
{
|
||||
"local_address": "0.0.0.0",
|
||||
"local_port": "ssh",
|
||||
"foreign_address": "0.0.0.0",
|
||||
"foreign_port": "*",
|
||||
"state": "LISTEN",
|
||||
"pid": 21918,
|
||||
"program_name": "sshd",
|
||||
"receive_q": 0,
|
||||
"send_q": 0
|
||||
}
|
||||
],
|
||||
"ipv6": [
|
||||
{
|
||||
"local_address": "localhost",
|
||||
"local_port": "smtp",
|
||||
"foreign_address": "[::]",
|
||||
"foreign_port": "*",
|
||||
"state": "LISTEN",
|
||||
"pid": 1594,
|
||||
"program_name": "master",
|
||||
"receive_q": 0,
|
||||
"send_q": 0
|
||||
},
|
||||
{
|
||||
"local_address": "[::]",
|
||||
"local_port": "ssh",
|
||||
"foreign_address": "[::]",
|
||||
"foreign_port": "*",
|
||||
"state": "LISTEN",
|
||||
"pid": 21918,
|
||||
"program_name": "sshd",
|
||||
"receive_q": 0,
|
||||
"send_q": 0
|
||||
}
|
||||
]
|
||||
},
|
||||
"udp": {
|
||||
"ipv4": [
|
||||
{
|
||||
"local_address": "0.0.0.0",
|
||||
"local_port": "bootpc",
|
||||
"foreign_address": "0.0.0.0",
|
||||
"foreign_port": "*",
|
||||
"pid": 13903,
|
||||
"program_name": "dhclient",
|
||||
"receive_q": 0,
|
||||
"send_q": 0
|
||||
},
|
||||
{
|
||||
"local_address": "localhost",
|
||||
"local_port": "323",
|
||||
"foreign_address": "0.0.0.0",
|
||||
"foreign_port": "*",
|
||||
"pid": 30926,
|
||||
"program_name": "chronyd",
|
||||
"receive_q": 0,
|
||||
"send_q": 0
|
||||
}
|
||||
],
|
||||
"ipv6": [
|
||||
{
|
||||
"local_address": "localhost",
|
||||
"local_port": "323",
|
||||
"foreign_address": "[::]",
|
||||
"foreign_port": "*",
|
||||
"pid": 30926,
|
||||
"program_name": "chronyd",
|
||||
"receive_q": 0,
|
||||
"send_q": 0
|
||||
}
|
||||
]
|
||||
}
|
||||
$ netstat -lpn | jc --netstat -p
|
||||
[
|
||||
{
|
||||
"session_protocol": "tcp",
|
||||
"network_protocol": "ipv4",
|
||||
"local_address": "127.0.0.1",
|
||||
"local_port": "42351",
|
||||
"foreign_address": "0.0.0.0",
|
||||
"foreign_port": "*",
|
||||
"state": "LISTEN",
|
||||
"pid": 1112,
|
||||
"program_name": "containerd",
|
||||
"receive_q": 0,
|
||||
"send_q": 0
|
||||
},
|
||||
{
|
||||
"session_protocol": "tcp",
|
||||
"network_protocol": "ipv4",
|
||||
"local_address": "127.0.0.53",
|
||||
"local_port": "53",
|
||||
"foreign_address": "0.0.0.0",
|
||||
"foreign_port": "*",
|
||||
"state": "LISTEN",
|
||||
"pid": 885,
|
||||
"program_name": "systemd-resolve",
|
||||
"receive_q": 0,
|
||||
"send_q": 0
|
||||
},
|
||||
{
|
||||
"session_protocol": "tcp",
|
||||
"network_protocol": "ipv4",
|
||||
"local_address": "0.0.0.0",
|
||||
"local_port": "22",
|
||||
"foreign_address": "0.0.0.0",
|
||||
"foreign_port": "*",
|
||||
"state": "LISTEN",
|
||||
"pid": 1127,
|
||||
"program_name": "sshd",
|
||||
"receive_q": 0,
|
||||
"send_q": 0
|
||||
},
|
||||
{
|
||||
"session_protocol": "tcp",
|
||||
"network_protocol": "ipv6",
|
||||
"local_address": "::",
|
||||
"local_port": "22",
|
||||
"foreign_address": "::",
|
||||
"foreign_port": "*",
|
||||
"state": "LISTEN",
|
||||
"pid": 1127,
|
||||
"program_name": "sshd",
|
||||
"receive_q": 0,
|
||||
"send_q": 0
|
||||
},
|
||||
{
|
||||
"session_protocol": "udp",
|
||||
"network_protocol": "ipv4",
|
||||
"local_address": "127.0.0.53",
|
||||
"local_port": "53",
|
||||
"foreign_address": "0.0.0.0",
|
||||
"foreign_port": "*",
|
||||
"pid": 885,
|
||||
"program_name": "systemd-resolve",
|
||||
"receive_q": 0,
|
||||
"send_q": 0
|
||||
},
|
||||
{
|
||||
"session_protocol": "udp",
|
||||
"network_protocol": "ipv4",
|
||||
"local_address": "192.168.71.131",
|
||||
"local_port": "68",
|
||||
"foreign_address": "0.0.0.0",
|
||||
"foreign_port": "*",
|
||||
"pid": 867,
|
||||
"program_name": "systemd-network",
|
||||
"receive_q": 0,
|
||||
"send_q": 0
|
||||
}
|
||||
}
|
||||
]
|
||||
"""
|
||||
import string
|
||||
|
||||
output = {}
|
||||
output = []
|
||||
|
||||
class state():
|
||||
section = ''
|
||||
session = ''
|
||||
network = ''
|
||||
|
||||
client_tcp_ip4 = []
|
||||
client_tcp_ip6 = []
|
||||
client_udp_ip4 = []
|
||||
client_udp_ip6 = []
|
||||
|
||||
server_tcp_ip4 = []
|
||||
server_tcp_ip6 = []
|
||||
server_udp_ip4 = []
|
||||
server_udp_ip6 = []
|
||||
|
||||
def parse_line(entry):
|
||||
parsed_line = entry.split()
|
||||
output_line = {}
|
||||
|
||||
if entry.find('tcp') == 0:
|
||||
output_line['session_protocol'] = 'tcp'
|
||||
|
||||
if entry.find('p6') == 2:
|
||||
output_line['network_protocol'] = 'ipv6'
|
||||
|
||||
else:
|
||||
output_line['network_protocol'] = 'ipv4'
|
||||
|
||||
elif entry.find('udp') == 0:
|
||||
output_line['session_protocol'] = 'udp'
|
||||
|
||||
if entry.find('p6') == 2:
|
||||
output_line['network_protocol'] = 'ipv6'
|
||||
|
||||
else:
|
||||
output_line['network_protocol'] = 'ipv4'
|
||||
else:
|
||||
return
|
||||
|
||||
parsed_line = entry.split()
|
||||
|
||||
output_line['local_address'] = parsed_line[3].rsplit(':', 1)[0]
|
||||
output_line['local_port'] = parsed_line[3].rsplit(':', 1)[-1]
|
||||
output_line['foreign_address'] = parsed_line[4].rsplit(':', 1)[0]
|
||||
@@ -164,151 +157,41 @@ def parse_line(entry):
|
||||
|
||||
if len(parsed_line) > 5:
|
||||
|
||||
if parsed_line[5][0] not in string.digits:
|
||||
if parsed_line[5][0] not in string.digits and parsed_line[5][0] != '-':
|
||||
output_line['state'] = parsed_line[5]
|
||||
|
||||
if len(parsed_line) > 6:
|
||||
|
||||
if len(parsed_line) > 6 and parsed_line[6][0] in string.digits:
|
||||
output_line['pid'] = int(parsed_line[6].split('/')[0])
|
||||
output_line['program_name'] = parsed_line[6].split('/')[1]
|
||||
else:
|
||||
output_line['pid'] = int(parsed_line[5].split('/')[0])
|
||||
output_line['program_name'] = parsed_line[5].split('/')[1]
|
||||
if parsed_line[5][0] in string.digits:
|
||||
output_line['pid'] = int(parsed_line[5].split('/')[0])
|
||||
output_line['program_name'] = parsed_line[5].split('/')[1]
|
||||
|
||||
output_line['receive_q'] = int(parsed_line[1])
|
||||
output_line['send_q'] = int(parsed_line[2])
|
||||
|
||||
return output_line
|
||||
|
||||
|
||||
def parse(data):
|
||||
cleandata = data.splitlines()
|
||||
|
||||
for line in cleandata:
|
||||
|
||||
if line.find('Active Internet connections (w/o servers)') == 0:
|
||||
state.section = "client"
|
||||
continue
|
||||
|
||||
if line.find('Active Internet connections (only servers)') == 0:
|
||||
state.section = "server"
|
||||
continue
|
||||
|
||||
|
||||
if line.find('Proto') == 0:
|
||||
continue
|
||||
|
||||
if line.find('Active UNIX') == 0:
|
||||
break
|
||||
|
||||
if state.section == 'client':
|
||||
if line.find('tcp') == 0:
|
||||
state.session = 'tcp'
|
||||
if line.find('p6') == 2:
|
||||
state.network = 'ipv6'
|
||||
else:
|
||||
state.network = 'ipv4'
|
||||
elif line.find('udp') == 0:
|
||||
state.session = 'udp'
|
||||
if line.find('p6') == 2:
|
||||
state.network = 'ipv6'
|
||||
else:
|
||||
state.network = 'ipv4'
|
||||
elif state.section == 'server':
|
||||
if line.find('tcp') == 0:
|
||||
state.session = 'tcp'
|
||||
if line.find('p6') == 2:
|
||||
state.network = 'ipv6'
|
||||
else:
|
||||
state.network = 'ipv4'
|
||||
elif line.find('udp') == 0:
|
||||
state.session = 'udp'
|
||||
if line.find('p6') == 2:
|
||||
state.network = 'ipv6'
|
||||
else:
|
||||
state.network = 'ipv4'
|
||||
|
||||
if state.section == 'client' and state.session == 'tcp' and state.network == 'ipv4':
|
||||
state.client_tcp_ip4.append(parse_line(line))
|
||||
output.append(parse_line(line))
|
||||
|
||||
if state.section == 'client' and state.session == 'tcp' and state.network == 'ipv6':
|
||||
state.client_tcp_ip6.append(parse_line(line))
|
||||
|
||||
if state.section == 'client' and state.session == 'udp' and state.network == 'ipv4':
|
||||
state.client_udp_ip4.append(parse_line(line))
|
||||
|
||||
if state.section == 'client' and state.session == 'udp' and state.network == 'ipv6':
|
||||
state.client_udp_ip6.append(parse_line(line))
|
||||
|
||||
|
||||
if state.section == 'server' and state.session == 'tcp' and state.network == 'ipv4':
|
||||
state.server_tcp_ip4.append(parse_line(line))
|
||||
|
||||
if state.section == 'server' and state.session == 'tcp' and state.network == 'ipv6':
|
||||
state.server_tcp_ip6.append(parse_line(line))
|
||||
|
||||
if state.section == 'server' and state.session == 'udp' and state.network == 'ipv4':
|
||||
state.server_udp_ip4.append(parse_line(line))
|
||||
|
||||
if state.section == 'server' and state.session == 'udp' and state.network == 'ipv6':
|
||||
state.server_udp_ip6.append(parse_line(line))
|
||||
|
||||
state.session = ''
|
||||
state.network = ''
|
||||
|
||||
# build dictionary
|
||||
if state.client_tcp_ip4:
|
||||
if 'client' not in output:
|
||||
output['client'] = {}
|
||||
if 'tcp' not in output['client']:
|
||||
output['client']['tcp'] = {}
|
||||
output['client']['tcp']['ipv4'] = state.client_tcp_ip4
|
||||
|
||||
if state.client_tcp_ip6:
|
||||
if 'client' not in output:
|
||||
output['client'] = {}
|
||||
if 'tcp' not in output['client']:
|
||||
output['client']['tcp'] = {}
|
||||
output['client']['tcp']['ipv6'] = state.client_tcp_ip6
|
||||
|
||||
if state.client_udp_ip4:
|
||||
if 'client' not in output:
|
||||
output['client'] = {}
|
||||
if 'udp' not in output['client']:
|
||||
output['client']['udp'] = {}
|
||||
output['client']['udp']['ipv4'] = state.client_udp_ip4
|
||||
|
||||
if state.client_udp_ip6:
|
||||
if 'client' not in output:
|
||||
output['client'] = {}
|
||||
if 'udp' not in output['client']:
|
||||
output['client']['udp'] = {}
|
||||
output['client']['udp']['ipv6'] = state.client_udp_ip6
|
||||
|
||||
|
||||
if state.server_tcp_ip4:
|
||||
if 'server' not in output:
|
||||
output['server'] = {}
|
||||
if 'tcp' not in output['server']:
|
||||
output['server']['tcp'] = {}
|
||||
output['server']['tcp']['ipv4'] = state.server_tcp_ip4
|
||||
|
||||
if state.server_tcp_ip6:
|
||||
if 'server' not in output:
|
||||
output['server'] = {}
|
||||
if 'tcp' not in output['server']:
|
||||
output['server']['tcp'] = {}
|
||||
output['server']['tcp']['ipv6'] = state.server_tcp_ip6
|
||||
|
||||
if state.server_udp_ip4:
|
||||
if 'server' not in output:
|
||||
output['server'] = {}
|
||||
if 'udp' not in output['server']:
|
||||
output['server']['udp'] = {}
|
||||
output['server']['udp']['ipv4'] = state.server_udp_ip4
|
||||
|
||||
if state.server_udp_ip6:
|
||||
if 'server' not in output:
|
||||
output['server'] = {}
|
||||
if 'udp' not in output['server']:
|
||||
output['server']['udp'] = {}
|
||||
output['server']['udp']['ipv6'] = state.server_udp_ip6
|
||||
|
||||
return output
|
||||
clean_output = list(filter(None, output))
|
||||
return clean_output
|
||||
|
||||
67
jc/parsers/ps.py
Normal file
67
jc/parsers/ps.py
Normal file
@@ -0,0 +1,67 @@
|
||||
"""jc - JSON CLI output utility ps Parser
|
||||
|
||||
Usage:
|
||||
specify --ps as the first argument if the piped input is coming from ps
|
||||
|
||||
ps options supported:
|
||||
- ef
|
||||
- axu
|
||||
|
||||
Example:
|
||||
|
||||
$ ps -ef | jc --ps -p
|
||||
[
|
||||
{
|
||||
"UID": "root",
|
||||
"PID": "1",
|
||||
"PPID": "0",
|
||||
"C": "0",
|
||||
"STIME": "13:58",
|
||||
"TTY": "?",
|
||||
"TIME": "00:00:05",
|
||||
"CMD": "/lib/systemd/systemd --system --deserialize 35"
|
||||
},
|
||||
{
|
||||
"UID": "root",
|
||||
"PID": "2",
|
||||
"PPID": "0",
|
||||
"C": "0",
|
||||
"STIME": "13:58",
|
||||
"TTY": "?",
|
||||
"TIME": "00:00:00",
|
||||
"CMD": "[kthreadd]"
|
||||
},
|
||||
{
|
||||
"UID": "root",
|
||||
"PID": "4",
|
||||
"PPID": "2",
|
||||
"C": "0",
|
||||
"STIME": "13:58",
|
||||
"TTY": "?",
|
||||
"TIME": "00:00:00",
|
||||
"CMD": "[kworker/0:0H]"
|
||||
},
|
||||
{
|
||||
"UID": "root",
|
||||
"PID": "6",
|
||||
"PPID": "2",
|
||||
"C": "0",
|
||||
"STIME": "13:58",
|
||||
"TTY": "?",
|
||||
"TIME": "00:00:00",
|
||||
"CMD": "[mm_percpu_wq]"
|
||||
},
|
||||
...
|
||||
]
|
||||
"""
|
||||
|
||||
|
||||
def parse(data):
|
||||
|
||||
# code adapted from Conor Heine at:
|
||||
# https://gist.github.com/cahna/43a1a3ff4d075bcd71f9d7120037a501
|
||||
|
||||
cleandata = data.splitlines()
|
||||
headers = [h for h in ' '.join(cleandata[0].strip().split()).split() if h]
|
||||
raw_data = map(lambda s: s.strip().split(None, len(headers) - 1), cleandata[1:])
|
||||
return [dict(zip(headers, r)) for r in raw_data]
|
||||
63
jc/parsers/route.py
Normal file
63
jc/parsers/route.py
Normal file
@@ -0,0 +1,63 @@
|
||||
"""jc - JSON CLI output utility route Parser
|
||||
|
||||
Usage:
|
||||
specify --route as the first argument if the piped input is coming from route
|
||||
|
||||
|
||||
Example:
|
||||
|
||||
$ route -n | jc --route -p
|
||||
[
|
||||
{
|
||||
"Destination": "0.0.0.0",
|
||||
"Gateway": "192.168.71.2",
|
||||
"Genmask": "0.0.0.0",
|
||||
"Flags": "UG",
|
||||
"Metric": "100",
|
||||
"Ref": "0",
|
||||
"Use": "0",
|
||||
"Iface": "ens33"
|
||||
},
|
||||
{
|
||||
"Destination": "172.17.0.0",
|
||||
"Gateway": "0.0.0.0",
|
||||
"Genmask": "255.255.0.0",
|
||||
"Flags": "U",
|
||||
"Metric": "0",
|
||||
"Ref": "0",
|
||||
"Use": "0",
|
||||
"Iface": "docker0"
|
||||
},
|
||||
{
|
||||
"Destination": "192.168.71.0",
|
||||
"Gateway": "0.0.0.0",
|
||||
"Genmask": "255.255.255.0",
|
||||
"Flags": "U",
|
||||
"Metric": "0",
|
||||
"Ref": "0",
|
||||
"Use": "0",
|
||||
"Iface": "ens33"
|
||||
},
|
||||
{
|
||||
"Destination": "192.168.71.2",
|
||||
"Gateway": "0.0.0.0",
|
||||
"Genmask": "255.255.255.255",
|
||||
"Flags": "UH",
|
||||
"Metric": "100",
|
||||
"Ref": "0",
|
||||
"Use": "0",
|
||||
"Iface": "ens33"
|
||||
}
|
||||
]
|
||||
"""
|
||||
|
||||
|
||||
def parse(data):
|
||||
|
||||
# code adapted from Conor Heine at:
|
||||
# https://gist.github.com/cahna/43a1a3ff4d075bcd71f9d7120037a501
|
||||
|
||||
cleandata = data.splitlines()[1:]
|
||||
headers = [h for h in ' '.join(cleandata[0].strip().split()).split() if h]
|
||||
raw_data = map(lambda s: s.strip().split(None, len(headers) - 1), cleandata[1:])
|
||||
return [dict(zip(headers, r)) for r in raw_data]
|
||||
2
setup.py
2
setup.py
@@ -5,7 +5,7 @@ with open('README.md', 'r') as f:
|
||||
|
||||
setuptools.setup(
|
||||
name='jc',
|
||||
version='0.2.0',
|
||||
version='0.6.3',
|
||||
author='Kelly Brazil',
|
||||
author_email='kellyjonbrazil@gmail.com',
|
||||
description='This tool serializes the output of popular command line tools to structured JSON output.',
|
||||
|
||||
Reference in New Issue
Block a user