mirror of
https://github.com/kellyjonbrazil/jc.git
synced 2026-04-03 17:44:07 +02:00
Compare commits
177 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d298e101e9 | ||
|
|
cea975d7f1 | ||
|
|
1ed69f9e6a | ||
|
|
ab0e05ec82 | ||
|
|
c16cce4bf0 | ||
|
|
d3489536a1 | ||
|
|
041050ce28 | ||
|
|
7de1a8a5d6 | ||
|
|
d4604743d1 | ||
|
|
0b8fb31298 | ||
|
|
dcdd79e28c | ||
|
|
5291baeb8e | ||
|
|
6867102c66 | ||
|
|
36ed2c7e2e | ||
|
|
4ab0aba9d3 | ||
|
|
e643badaf7 | ||
|
|
d96e96219e | ||
|
|
e42af3353e | ||
|
|
4ec2b16f42 | ||
|
|
0a028456bf | ||
|
|
a1f10928e1 | ||
|
|
eae1d4b89a | ||
|
|
d3c7cec333 | ||
|
|
36fa08d711 | ||
|
|
a9958841e4 | ||
|
|
504ad81a01 | ||
|
|
8bf2f4f4d0 | ||
|
|
805397ea18 | ||
|
|
1b3985c2d7 | ||
|
|
f602043642 | ||
|
|
1a1aa8fda3 | ||
|
|
3249a017ae | ||
|
|
84f0246b2d | ||
|
|
1c795982b0 | ||
|
|
c5164b4108 | ||
|
|
dc3716ecb3 | ||
|
|
c5165ccc21 | ||
|
|
5b2035e0e6 | ||
|
|
5205154aaf | ||
|
|
f500de3af6 | ||
|
|
4b028b5080 | ||
|
|
4cd721be85 | ||
|
|
d58ca402a7 | ||
|
|
5386879040 | ||
|
|
f19a1f23a9 | ||
|
|
5023e5be4c | ||
|
|
5527d22459 | ||
|
|
9d567c2e70 | ||
|
|
39c03a15d5 | ||
|
|
5e6d2562f9 | ||
|
|
7009d5a014 | ||
|
|
42f9ddabb9 | ||
|
|
30efb5afc0 | ||
|
|
786dc76c09 | ||
|
|
fc48874a5d | ||
|
|
67164e7b23 | ||
|
|
3db9774ac6 | ||
|
|
03aef93d9a | ||
|
|
9b7e3de3ed | ||
|
|
291b6b061a | ||
|
|
08496533e2 | ||
|
|
f8dceb5046 | ||
|
|
240ed4047f | ||
|
|
0bf6f7cd7c | ||
|
|
313bd86e3e | ||
|
|
56259d5605 | ||
|
|
8a4885c1fe | ||
|
|
5b06f84917 | ||
|
|
37a1428914 | ||
|
|
bff065daf3 | ||
|
|
c60d899f31 | ||
|
|
fb7c390506 | ||
|
|
22afb69573 | ||
|
|
be51304c9c | ||
|
|
44f83d800f | ||
|
|
bf07973d90 | ||
|
|
11e94b686c | ||
|
|
c68bf674a1 | ||
|
|
dbbc310082 | ||
|
|
e861f4a597 | ||
|
|
049e93707c | ||
|
|
164294ecb7 | ||
|
|
22ef489795 | ||
|
|
8f9d650f6c | ||
|
|
7e134a63bd | ||
|
|
9b5c25cb5b | ||
|
|
894946b207 | ||
|
|
92ad2068db | ||
|
|
59662a1500 | ||
|
|
125b88a2ca | ||
|
|
79fce8c769 | ||
|
|
a4a53f8b3b | ||
|
|
743e1ae90f | ||
|
|
dbcff80907 | ||
|
|
6e10965aed | ||
|
|
0c8c4a9c53 | ||
|
|
7632541a1e | ||
|
|
a213ad9a85 | ||
|
|
15ac5a9004 | ||
|
|
0658668eb0 | ||
|
|
ccef69ac37 | ||
|
|
64676fda2e | ||
|
|
6ea2d776ae | ||
|
|
98ced9616c | ||
|
|
ae19183803 | ||
|
|
8536514baf | ||
|
|
81982a9f79 | ||
|
|
1acbb2f096 | ||
|
|
80fb4d40a5 | ||
|
|
c972dd1aac | ||
|
|
9dde65c25c | ||
|
|
7486b0c7cd | ||
|
|
5c7a520a0b | ||
|
|
193ddf71f8 | ||
|
|
c46fe9816c | ||
|
|
aada5f0794 | ||
|
|
5c7bf363a6 | ||
|
|
00b74be540 | ||
|
|
96cb01f57a | ||
|
|
adf5f403ae | ||
|
|
1c09c95c71 | ||
|
|
64f442d743 | ||
|
|
cd8d43446b | ||
|
|
7361eac1a4 | ||
|
|
9d41f0a938 | ||
|
|
00274c15df | ||
|
|
d0b8a91f94 | ||
|
|
098e8dbef6 | ||
|
|
ec29b8bbc6 | ||
|
|
1d8f83b8c6 | ||
|
|
12c4419c6a | ||
|
|
b134c53f33 | ||
|
|
23ff19fdb5 | ||
|
|
7a43ba631b | ||
|
|
2beb26f3e3 | ||
|
|
4b55f49e99 | ||
|
|
c0239a771c | ||
|
|
bc4738e900 | ||
|
|
1c76caf59b | ||
|
|
0679951d4a | ||
|
|
df7aee9e4b | ||
|
|
a8d97a2521 | ||
|
|
9370b336d8 | ||
|
|
118f98222a | ||
|
|
dc997821f4 | ||
|
|
aef0fdb435 | ||
|
|
21ee3c0e78 | ||
|
|
d6c665f74b | ||
|
|
79305a50d0 | ||
|
|
e758aa41ef | ||
|
|
910cb34b09 | ||
|
|
976fea4eb2 | ||
|
|
81447ec9e6 | ||
|
|
61b8e9f7b5 | ||
|
|
8e86a04448 | ||
|
|
f23715a783 | ||
|
|
fa693a8bb1 | ||
|
|
103168c25b | ||
|
|
ae7ed4eec5 | ||
|
|
caa516db72 | ||
|
|
f05c3d6113 | ||
|
|
5d872b1535 | ||
|
|
1559cd9f5c | ||
|
|
a5a87c7da1 | ||
|
|
0e6cec62c1 | ||
|
|
3bb1d89165 | ||
|
|
f5f5f102fb | ||
|
|
0c82fe7e4d | ||
|
|
13ffe8a84d | ||
|
|
4c6eebaa33 | ||
|
|
f8fbb2dce2 | ||
|
|
dbfe682674 | ||
|
|
2a148d44a1 | ||
|
|
0648d2e9e3 | ||
|
|
ac9128fa0c | ||
|
|
4d7a872670 | ||
|
|
07f716b254 |
72
CHANGELOG
72
CHANGELOG
@@ -1,5 +1,77 @@
|
||||
jc changelog
|
||||
|
||||
20231021 v1.23.5
|
||||
- Add `host` command parser
|
||||
- Add `nsd-control` command parser
|
||||
- Add `lsb_release` command parser
|
||||
- Add `/etc/os-release` file parser
|
||||
- Enhance `env` command parser to support multi-line values
|
||||
- Enhance `ping` and `ping-s` parsers to add error and corrupted support
|
||||
- Enhance `xml` parser to include comments in the JSON output
|
||||
- Fix `pidstat` command parser when using `-T ALL`
|
||||
- Fix `x509-cert` parser to allow negative serial numbers
|
||||
- Fix `x509-cert` parser for cases when bitstrings are larger than standard
|
||||
- Fix `xrandr` command parser for associated device issues
|
||||
- Fix error when pygments library is not installed
|
||||
|
||||
20230730 v1.23.4
|
||||
- Add `/etc/resolve.conf` file parser
|
||||
- Add `/proc/net/tcp` and `/proc/net/tcp6` file parser
|
||||
- Add `find` command parser
|
||||
- Add `ip route` command parser
|
||||
- Fix `certbot` command parser to be more robust with different line endings
|
||||
|
||||
20230621 v1.23.3
|
||||
- Add `lsattr` command parser
|
||||
- Add `srt` file parser
|
||||
- Add `veracrypt` command parser
|
||||
- Add X509 Certificate Request file parser
|
||||
- Enhance X509 Certificate parser to allow non-compliant email addresses with a warning
|
||||
- Enhance `dig` command parser to support the `+nsid` option
|
||||
- Enhance `last` and `lastb` command parser to support the `-x` option
|
||||
- Enhance `route` command parser to add Windows support
|
||||
- Enhnace `netstat` command parser to add Windows support
|
||||
- Enhance `ss` command parser to support extended options
|
||||
- Enhance the compatibility warning message
|
||||
- Fix `bluetoothctl` command parser for some mouse devices
|
||||
- Fix `ping` command parsers for output with missing hostname
|
||||
- Fix `stat` command parser for older versions that may not contain all fields
|
||||
- Fix deprecated option in `setup.cfg`
|
||||
|
||||
20230429 v1.23.2
|
||||
- Add `bluetoothctl` command parser
|
||||
- Add `certbot` command parser for `certificates` and `show_account` options
|
||||
- Fix `acpi` command parser for "Not charging" battery status lines
|
||||
- Fix `iwconfig` command parser for SSIDs with dashes in the name
|
||||
- Fix `crontab` command parsers for incorrect variable parsing in some cases
|
||||
- Fix `git-log` and `git-log-s` command parsers for incorrect insertion/deletion parsing
|
||||
- Fix `ufw-appinfo` command parser for parsing errors on multiline description fields
|
||||
- Fix pytest warnings
|
||||
|
||||
20230323 v1.23.1
|
||||
- Fix `zpool-status` command parser for lines that start with tab
|
||||
- Fix `timedatectl` command parser when RTC set to local
|
||||
- Fix to ensure `py.typed` file is included in the package wheel
|
||||
- Fix `lsusb` command parser to support CDC MBIM and CDC MBIM Extended fields
|
||||
- Add support for the `timesync-status` for the `timedatectl` command parser
|
||||
- Fix to ignore non-parser-plugins in the parser plugin directory
|
||||
|
||||
20230227 v1.23.0
|
||||
- Add input slicing as a `jc` command-line option
|
||||
- Add `ssh` configuration file parser
|
||||
- Add `ver` Version string parser
|
||||
- Add `zpool iostat` command parser
|
||||
- Add `zpool status` command parser
|
||||
- Fix `acpi` command parser for "will never fully discharge" battery state
|
||||
- Fix `crontab` and `crontab-u` command and file parsers for cases where only
|
||||
shortcut schedule items exist
|
||||
- Fix `ifconfig` command parser for older-style linux output
|
||||
- Fix `xrandr` command parser for proper `is_current` output
|
||||
- Fix `xrandr` command parser for infinite loop with some device configurations
|
||||
- Add `reflection` key to `xrandr` parser schema
|
||||
- Add display model info from EDID to `xrandr` parser
|
||||
- Add `MPX-specific VMA` support for VM Flags in `/proc/<pid>/smaps` parser
|
||||
|
||||
20230111 v1.22.5
|
||||
- Add TOML file parser
|
||||
- Add INI with duplicate key support file parser
|
||||
|
||||
51
EXAMPLES.md
51
EXAMPLES.md
@@ -4551,6 +4551,57 @@ cat entrust.pem | jc --x509-cert -p
|
||||
}
|
||||
]
|
||||
```
|
||||
### X.509 PEM and DER certificate request files
|
||||
```bash
|
||||
cat myserver.csr | jc --x509-csr -p
|
||||
```
|
||||
```json
|
||||
[
|
||||
{
|
||||
"certification_request_info": {
|
||||
"version": "v1",
|
||||
"subject": {
|
||||
"common_name": "myserver.for.example"
|
||||
},
|
||||
"subject_pk_info": {
|
||||
"algorithm": {
|
||||
"algorithm": "ec",
|
||||
"parameters": "secp256r1"
|
||||
},
|
||||
"public_key": "04:40:33:c0:91:8f:e9:46:ea:d0:dc:d0:f9:63:2c:a4:35:1f:0f:54:c8:a9:9b:e3:9e:d4:f3:64:b8:60:cc:7f:39:75:dd:a7:61:31:02:7c:9e:89:c6:db:45:15:f2:5f:b0:65:29:0b:42:d2:6e:c2:ea:a6:23:bd:fc:65:e5:7d:4e"
|
||||
},
|
||||
"attributes": [
|
||||
{
|
||||
"type": "extension_request",
|
||||
"values": [
|
||||
[
|
||||
{
|
||||
"extn_id": "extended_key_usage",
|
||||
"critical": false,
|
||||
"extn_value": [
|
||||
"server_auth"
|
||||
]
|
||||
},
|
||||
{
|
||||
"extn_id": "subject_alt_name",
|
||||
"critical": false,
|
||||
"extn_value": [
|
||||
"myserver.for.example"
|
||||
]
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"signature_algorithm": {
|
||||
"algorithm": "sha384_ecdsa",
|
||||
"parameters": null
|
||||
},
|
||||
"signature": "30:45:02:20:77:ac:5b:51:bf:c5:f5:43:02:52:ae:66:8a:fe:95:98:98:98:a9:45:34:31:08:ff:2c:cc:92:d9:1c:70:28:74:02:21:00:97:79:7b:e7:45:18:76:cf:d7:3b:79:34:56:d2:69:b5:73:41:9b:8a:b7:ad:ec:80:23:c1:2f:64:da:e5:28:19"
|
||||
}
|
||||
]
|
||||
```
|
||||
### XML files
|
||||
```bash
|
||||
cat cd_catalog.xml
|
||||
|
||||
365
README.md
365
README.md
@@ -3,13 +3,15 @@
|
||||
|
||||
> Check out the `jc` Python [package documentation](https://github.com/kellyjonbrazil/jc/tree/master/docs) for developers
|
||||
|
||||
> Try the `jc` [web demo](https://jc-web.onrender.com/)
|
||||
> Try the `jc` [web demo](https://jc-web.onrender.com/) and [REST API](https://github.com/kellyjonbrazil/jc-restapi)
|
||||
|
||||
> JC is [now available](https://galaxy.ansible.com/community/general) as an
|
||||
> `jc` is [now available](https://galaxy.ansible.com/community/general) as an
|
||||
Ansible filter plugin in the `community.general` collection. See this
|
||||
[blog post](https://blog.kellybrazil.com/2020/08/30/parsing-command-output-in-ansible-with-jc/)
|
||||
for an example.
|
||||
|
||||
> Looking for something like `jc` but lower-level? Check out [regex2json](https://gitlab.com/tozd/regex2json).
|
||||
|
||||
# JC
|
||||
JSON Convert
|
||||
|
||||
@@ -44,8 +46,8 @@ $ jc dig example.com | jq -r '.[].answer[].data'
|
||||
93.184.216.34
|
||||
```
|
||||
|
||||
`jc` can also be used as a python library. In this case the output will be
|
||||
a python dictionary, a list of dictionaries, or even a
|
||||
`jc` can also be used as a python library. In this case the returned value
|
||||
will be a python dictionary, a list of dictionaries, or even a
|
||||
[lazy iterable of dictionaries](#using-streaming-parsers-as-python-modules)
|
||||
instead of JSON:
|
||||
```python
|
||||
@@ -133,9 +135,9 @@ on Github.
|
||||
`jc` accepts piped input from `STDIN` and outputs a JSON representation of the
|
||||
previous command's output to `STDOUT`.
|
||||
```bash
|
||||
COMMAND | jc [OPTIONS] PARSER
|
||||
cat FILE | jc [OPTIONS] PARSER
|
||||
echo STRING | jc [OPTIONS] PARSER
|
||||
COMMAND | jc [SLICE] [OPTIONS] PARSER
|
||||
cat FILE | jc [SLICE] [OPTIONS] PARSER
|
||||
echo STRING | jc [SLICE] [OPTIONS] PARSER
|
||||
```
|
||||
|
||||
Alternatively, the "magic" syntax can be used by prepending `jc` to the command
|
||||
@@ -143,8 +145,8 @@ to be converted or in front of the absolute path for Proc files. Options can be
|
||||
passed to `jc` immediately before the command or Proc file path is given.
|
||||
(Note: command aliases and shell builtins are not supported)
|
||||
```bash
|
||||
jc [OPTIONS] COMMAND
|
||||
jc [OPTIONS] /proc/<path-to-procfile>
|
||||
jc [SLICE] [OPTIONS] COMMAND
|
||||
jc [SLICE] [OPTIONS] /proc/<path-to-procfile>
|
||||
```
|
||||
|
||||
The JSON output can be compact (default) or pretty formatted with the `-p`
|
||||
@@ -154,143 +156,160 @@ option.
|
||||
|
||||
| Argument | Command or Filetype | Documentation |
|
||||
|-------------------|---------------------------------------------------------|----------------------------------------------------------------------------|
|
||||
| ` --acpi` | `acpi` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/acpi) |
|
||||
| ` --airport` | `airport -I` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/airport) |
|
||||
| ` --airport-s` | `airport -s` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/airport_s) |
|
||||
| ` --arp` | `arp` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/arp) |
|
||||
| ` --asciitable` | ASCII and Unicode table parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/asciitable) |
|
||||
| ` --asciitable-m` | multi-line ASCII and Unicode table parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/asciitable_m) |
|
||||
| ` --blkid` | `blkid` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/blkid) |
|
||||
| ` --cbt` | `cbt` (Google Bigtable) command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/cbt) |
|
||||
| ` --cef` | CEF string parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/cef) |
|
||||
| ` --cef-s` | CEF string streaming parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/cef_s) |
|
||||
| ` --chage` | `chage --list` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/chage) |
|
||||
| ` --cksum` | `cksum` and `sum` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/cksum) |
|
||||
| ` --clf` | Common and Combined Log Format file parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/clf) |
|
||||
| ` --clf-s` | Common and Combined Log Format file streaming parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/clf_s) |
|
||||
| ` --crontab` | `crontab` command and file parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/crontab) |
|
||||
| ` --crontab-u` | `crontab` file parser with user support | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/crontab_u) |
|
||||
| ` --csv` | CSV file parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/csv) |
|
||||
| ` --csv-s` | CSV file streaming parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/csv_s) |
|
||||
| ` --date` | `date` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/date) |
|
||||
| ` --datetime-iso` | ISO 8601 Datetime string parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/datetime_iso) |
|
||||
| ` --df` | `df` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/df) |
|
||||
| ` --dig` | `dig` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/dig) |
|
||||
| ` --dir` | `dir` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/dir) |
|
||||
| ` --dmidecode` | `dmidecode` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/dmidecode) |
|
||||
| ` --dpkg-l` | `dpkg -l` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/dpkg_l) |
|
||||
| ` --du` | `du` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/du) |
|
||||
| `--acpi` | `acpi` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/acpi) |
|
||||
| `--airport` | `airport -I` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/airport) |
|
||||
| `--airport-s` | `airport -s` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/airport_s) |
|
||||
| `--arp` | `arp` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/arp) |
|
||||
| `--asciitable` | ASCII and Unicode table parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/asciitable) |
|
||||
| `--asciitable-m` | multi-line ASCII and Unicode table parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/asciitable_m) |
|
||||
| `--blkid` | `blkid` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/blkid) |
|
||||
| `--bluetoothctl` | `bluetoothctl` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/bluetoothctl) |
|
||||
| `--cbt` | `cbt` (Google Bigtable) command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/cbt) |
|
||||
| `--cef` | CEF string parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/cef) |
|
||||
| `--cef-s` | CEF string streaming parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/cef_s) |
|
||||
| `--certbot` | `certbot` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/certbot) |
|
||||
| `--chage` | `chage --list` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/chage) |
|
||||
| `--cksum` | `cksum` and `sum` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/cksum) |
|
||||
| `--clf` | Common and Combined Log Format file parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/clf) |
|
||||
| `--clf-s` | Common and Combined Log Format file streaming parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/clf_s) |
|
||||
| `--crontab` | `crontab` command and file parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/crontab) |
|
||||
| `--crontab-u` | `crontab` file parser with user support | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/crontab_u) |
|
||||
| `--csv` | CSV file parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/csv) |
|
||||
| `--csv-s` | CSV file streaming parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/csv_s) |
|
||||
| `--date` | `date` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/date) |
|
||||
| `--datetime-iso` | ISO 8601 Datetime string parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/datetime_iso) |
|
||||
| `--df` | `df` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/df) |
|
||||
| `--dig` | `dig` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/dig) |
|
||||
| `--dir` | `dir` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/dir) |
|
||||
| `--dmidecode` | `dmidecode` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/dmidecode) |
|
||||
| `--dpkg-l` | `dpkg -l` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/dpkg_l) |
|
||||
| `--du` | `du` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/du) |
|
||||
| `--email-address` | Email Address string parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/email_address) |
|
||||
| ` --env` | `env` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/env) |
|
||||
| ` --file` | `file` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/file) |
|
||||
| ` --findmnt` | `findmnt` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/findmnt) |
|
||||
| ` --finger` | `finger` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/finger) |
|
||||
| ` --free` | `free` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/free) |
|
||||
| ` --fstab` | `/etc/fstab` file parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/fstab) |
|
||||
| ` --git-log` | `git log` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/git_log) |
|
||||
| ` --git-log-s` | `git log` command streaming parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/git_log_s) |
|
||||
| `--env` | `env` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/env) |
|
||||
| `--file` | `file` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/file) |
|
||||
| `--find` | `find` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/find) |
|
||||
| `--findmnt` | `findmnt` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/findmnt) |
|
||||
| `--finger` | `finger` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/finger) |
|
||||
| `--free` | `free` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/free) |
|
||||
| `--fstab` | `/etc/fstab` file parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/fstab) |
|
||||
| `--git-log` | `git log` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/git_log) |
|
||||
| `--git-log-s` | `git log` command streaming parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/git_log_s) |
|
||||
| `--git-ls-remote` | `git ls-remote` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/git_ls_remote) |
|
||||
| ` --gpg` | `gpg --with-colons` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/gpg) |
|
||||
| ` --group` | `/etc/group` file parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/group) |
|
||||
| ` --gshadow` | `/etc/gshadow` file parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/gshadow) |
|
||||
| ` --hash` | `hash` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/hash) |
|
||||
| ` --hashsum` | hashsum command parser (`md5sum`, `shasum`, etc.) | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/hashsum) |
|
||||
| ` --hciconfig` | `hciconfig` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/hciconfig) |
|
||||
| ` --history` | `history` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/history) |
|
||||
| ` --hosts` | `/etc/hosts` file parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/hosts) |
|
||||
| ` --id` | `id` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/id) |
|
||||
| ` --ifconfig` | `ifconfig` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/ifconfig) |
|
||||
| ` --ini` | INI file parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/ini) |
|
||||
| ` --ini-dup` | INI with duplicate key file parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/ini_dup) |
|
||||
| ` --iostat` | `iostat` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/iostat) |
|
||||
| ` --iostat-s` | `iostat` command streaming parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/iostat_s) |
|
||||
| ` --ip-address` | IPv4 and IPv6 Address string parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/ip_address) |
|
||||
| ` --iptables` | `iptables` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/iptables) |
|
||||
| ` --iw-scan` | `iw dev [device] scan` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/iw_scan) |
|
||||
| ` --iwconfig` | `iwconfig` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/iwconfig) |
|
||||
| ` --jar-manifest` | Java MANIFEST.MF file parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/jar_manifest) |
|
||||
| ` --jobs` | `jobs` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/jobs) |
|
||||
| ` --jwt` | JWT string parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/jwt) |
|
||||
| ` --kv` | Key/Value file and string parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/kv) |
|
||||
| ` --last` | `last` and `lastb` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/last) |
|
||||
| ` --ls` | `ls` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/ls) |
|
||||
| ` --ls-s` | `ls` command streaming parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/ls_s) |
|
||||
| ` --lsblk` | `lsblk` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/lsblk) |
|
||||
| ` --lsmod` | `lsmod` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/lsmod) |
|
||||
| ` --lsof` | `lsof` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/lsof) |
|
||||
| ` --lspci` | `lspci -mmv` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/lspci) |
|
||||
| ` --lsusb` | `lsusb` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/lsusb) |
|
||||
| ` --m3u` | M3U and M3U8 file parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/m3u) |
|
||||
| ` --mdadm` | `mdadm` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/mdadm) |
|
||||
| ` --mount` | `mount` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/mount) |
|
||||
| ` --mpstat` | `mpstat` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/mpstat) |
|
||||
| ` --mpstat-s` | `mpstat` command streaming parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/mpstat_s) |
|
||||
| ` --netstat` | `netstat` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/netstat) |
|
||||
| ` --nmcli` | `nmcli` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/nmcli) |
|
||||
| ` --ntpq` | `ntpq -p` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/ntpq) |
|
||||
| ` --openvpn` | openvpn-status.log file parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/openvpn) |
|
||||
| ` --os-prober` | `os-prober` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/os_prober) |
|
||||
| ` --passwd` | `/etc/passwd` file parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/passwd) |
|
||||
| ` --pci-ids` | `pci.ids` file parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/pci_ids) |
|
||||
| ` --pgpass` | PostgreSQL password file parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/pgpass) |
|
||||
| ` --pidstat` | `pidstat -H` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/pidstat) |
|
||||
| ` --pidstat-s` | `pidstat -H` command streaming parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/pidstat_s) |
|
||||
| ` --ping` | `ping` and `ping6` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/ping) |
|
||||
| ` --ping-s` | `ping` and `ping6` command streaming parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/ping_s) |
|
||||
| ` --pip-list` | `pip list` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/pip_list) |
|
||||
| ` --pip-show` | `pip show` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/pip_show) |
|
||||
| ` --plist` | PLIST file parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/plist) |
|
||||
| ` --postconf` | `postconf -M` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/postconf) |
|
||||
| ` --proc` | `/proc/` file parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/proc) |
|
||||
| ` --ps` | `ps` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/ps) |
|
||||
| ` --route` | `route` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/route) |
|
||||
| ` --rpm-qi` | `rpm -qi` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/rpm_qi) |
|
||||
| ` --rsync` | `rsync` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/rsync) |
|
||||
| ` --rsync-s` | `rsync` command streaming parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/rsync_s) |
|
||||
| ` --semver` | Semantic Version string parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/semver) |
|
||||
| ` --sfdisk` | `sfdisk` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/sfdisk) |
|
||||
| ` --shadow` | `/etc/shadow` file parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/shadow) |
|
||||
| ` --ss` | `ss` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/ss) |
|
||||
| ` --sshd-conf` | sshd config file and `sshd -T` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/sshd_conf) |
|
||||
| ` --stat` | `stat` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/stat) |
|
||||
| ` --stat-s` | `stat` command streaming parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/stat_s) |
|
||||
| ` --sysctl` | `sysctl` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/sysctl) |
|
||||
| ` --syslog` | Syslog RFC 5424 string parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/syslog) |
|
||||
| ` --syslog-s` | Syslog RFC 5424 string streaming parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/syslog_s) |
|
||||
| ` --syslog-bsd` | Syslog RFC 3164 string parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/syslog_bsd) |
|
||||
| ` --syslog-bsd-s` | Syslog RFC 3164 string streaming parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/syslog_bsd_s) |
|
||||
| ` --systemctl` | `systemctl` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/systemctl) |
|
||||
| ` --systemctl-lj` | `systemctl list-jobs` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/systemctl_lj) |
|
||||
| ` --systemctl-ls` | `systemctl list-sockets` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/systemctl_ls) |
|
||||
| `--gpg` | `gpg --with-colons` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/gpg) |
|
||||
| `--group` | `/etc/group` file parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/group) |
|
||||
| `--gshadow` | `/etc/gshadow` file parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/gshadow) |
|
||||
| `--hash` | `hash` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/hash) |
|
||||
| `--hashsum` | hashsum command parser (`md5sum`, `shasum`, etc.) | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/hashsum) |
|
||||
| `--hciconfig` | `hciconfig` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/hciconfig) |
|
||||
| `--history` | `history` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/history) |
|
||||
| `--host` | `host` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/host) |
|
||||
| `--hosts` | `/etc/hosts` file parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/hosts) |
|
||||
| `--id` | `id` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/id) |
|
||||
| `--ifconfig` | `ifconfig` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/ifconfig) |
|
||||
| `--ini` | INI file parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/ini) |
|
||||
| `--ini-dup` | INI with duplicate key file parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/ini_dup) |
|
||||
| `--iostat` | `iostat` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/iostat) |
|
||||
| `--iostat-s` | `iostat` command streaming parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/iostat_s) |
|
||||
| `--ip-address` | IPv4 and IPv6 Address string parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/ip_address) |
|
||||
| `--iptables` | `iptables` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/iptables) |
|
||||
| `--ip-route` | `ip route` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/ip_route) |
|
||||
| `--iw-scan` | `iw dev [device] scan` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/iw_scan) |
|
||||
| `--iwconfig` | `iwconfig` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/iwconfig) |
|
||||
| `--jar-manifest` | Java MANIFEST.MF file parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/jar_manifest) |
|
||||
| `--jobs` | `jobs` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/jobs) |
|
||||
| `--jwt` | JWT string parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/jwt) |
|
||||
| `--kv` | Key/Value file and string parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/kv) |
|
||||
| `--last` | `last` and `lastb` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/last) |
|
||||
| `--ls` | `ls` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/ls) |
|
||||
| `--ls-s` | `ls` command streaming parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/ls_s) |
|
||||
| `--lsattr` | `lsattr` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/lsattr) |
|
||||
| `--lsb-release` | `lsb_release` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/lsb_release) |
|
||||
| `--lsblk` | `lsblk` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/lsblk) |
|
||||
| `--lsmod` | `lsmod` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/lsmod) |
|
||||
| `--lsof` | `lsof` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/lsof) |
|
||||
| `--lspci` | `lspci -mmv` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/lspci) |
|
||||
| `--lsusb` | `lsusb` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/lsusb) |
|
||||
| `--m3u` | M3U and M3U8 file parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/m3u) |
|
||||
| `--mdadm` | `mdadm` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/mdadm) |
|
||||
| `--mount` | `mount` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/mount) |
|
||||
| `--mpstat` | `mpstat` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/mpstat) |
|
||||
| `--mpstat-s` | `mpstat` command streaming parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/mpstat_s) |
|
||||
| `--netstat` | `netstat` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/netstat) |
|
||||
| `--nmcli` | `nmcli` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/nmcli) |
|
||||
| `--nsd-control` | `nsd-control` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/nsd_control) |
|
||||
| `--ntpq` | `ntpq -p` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/ntpq) |
|
||||
| `--openvpn` | openvpn-status.log file parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/openvpn) |
|
||||
| `--os-prober` | `os-prober` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/os_prober) |
|
||||
| `--os-release` | `/etc/os-release` file parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/os_release) |
|
||||
| `--passwd` | `/etc/passwd` file parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/passwd) |
|
||||
| `--pci-ids` | `pci.ids` file parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/pci_ids) |
|
||||
| `--pgpass` | PostgreSQL password file parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/pgpass) |
|
||||
| `--pidstat` | `pidstat -H` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/pidstat) |
|
||||
| `--pidstat-s` | `pidstat -H` command streaming parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/pidstat_s) |
|
||||
| `--ping` | `ping` and `ping6` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/ping) |
|
||||
| `--ping-s` | `ping` and `ping6` command streaming parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/ping_s) |
|
||||
| `--pip-list` | `pip list` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/pip_list) |
|
||||
| `--pip-show` | `pip show` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/pip_show) |
|
||||
| `--plist` | PLIST file parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/plist) |
|
||||
| `--postconf` | `postconf -M` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/postconf) |
|
||||
| `--proc` | `/proc/` file parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/proc) |
|
||||
| `--ps` | `ps` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/ps) |
|
||||
| `--resolve-conf` | `/etc/resolve.conf` file parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/resolve_conf) |
|
||||
| `--route` | `route` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/route) |
|
||||
| `--rpm-qi` | `rpm -qi` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/rpm_qi) |
|
||||
| `--rsync` | `rsync` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/rsync) |
|
||||
| `--rsync-s` | `rsync` command streaming parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/rsync_s) |
|
||||
| `--semver` | Semantic Version string parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/semver) |
|
||||
| `--sfdisk` | `sfdisk` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/sfdisk) |
|
||||
| `--shadow` | `/etc/shadow` file parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/shadow) |
|
||||
| `--srt` | SRT file parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/srt) |
|
||||
| `--ss` | `ss` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/ss) |
|
||||
| `--ssh-conf` | `ssh` config file and `ssh -G` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/ssh_conf) |
|
||||
| `--sshd-conf` | `sshd` config file and `sshd -T` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/sshd_conf) |
|
||||
| `--stat` | `stat` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/stat) |
|
||||
| `--stat-s` | `stat` command streaming parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/stat_s) |
|
||||
| `--sysctl` | `sysctl` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/sysctl) |
|
||||
| `--syslog` | Syslog RFC 5424 string parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/syslog) |
|
||||
| `--syslog-s` | Syslog RFC 5424 string streaming parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/syslog_s) |
|
||||
| `--syslog-bsd` | Syslog RFC 3164 string parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/syslog_bsd) |
|
||||
| `--syslog-bsd-s` | Syslog RFC 3164 string streaming parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/syslog_bsd_s) |
|
||||
| `--systemctl` | `systemctl` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/systemctl) |
|
||||
| `--systemctl-lj` | `systemctl list-jobs` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/systemctl_lj) |
|
||||
| `--systemctl-ls` | `systemctl list-sockets` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/systemctl_ls) |
|
||||
| `--systemctl-luf` | `systemctl list-unit-files` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/systemctl_luf) |
|
||||
| ` --systeminfo` | `systeminfo` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/systeminfo) |
|
||||
| ` --time` | `/usr/bin/time` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/time) |
|
||||
| ` --timedatectl` | `timedatectl status` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/timedatectl) |
|
||||
| ` --timestamp` | Unix Epoch Timestamp string parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/timestamp) |
|
||||
| ` --toml` | TOML file parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/toml) |
|
||||
| ` --top` | `top -b` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/top) |
|
||||
| ` --top-s` | `top -b` command streaming parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/top_s) |
|
||||
| ` --tracepath` | `tracepath` and `tracepath6` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/tracepath) |
|
||||
| ` --traceroute` | `traceroute` and `traceroute6` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/traceroute) |
|
||||
| ` --udevadm` | `udevadm info` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/udevadm) |
|
||||
| ` --ufw` | `ufw status` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/ufw) |
|
||||
| ` --ufw-appinfo` | `ufw app info [application]` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/ufw_appinfo) |
|
||||
| ` --uname` | `uname -a` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/uname) |
|
||||
| `--systeminfo` | `systeminfo` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/systeminfo) |
|
||||
| `--time` | `/usr/bin/time` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/time) |
|
||||
| `--timedatectl` | `timedatectl status` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/timedatectl) |
|
||||
| `--timestamp` | Unix Epoch Timestamp string parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/timestamp) |
|
||||
| `--toml` | TOML file parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/toml) |
|
||||
| `--top` | `top -b` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/top) |
|
||||
| `--top-s` | `top -b` command streaming parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/top_s) |
|
||||
| `--tracepath` | `tracepath` and `tracepath6` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/tracepath) |
|
||||
| `--traceroute` | `traceroute` and `traceroute6` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/traceroute) |
|
||||
| `--udevadm` | `udevadm info` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/udevadm) |
|
||||
| `--ufw` | `ufw status` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/ufw) |
|
||||
| `--ufw-appinfo` | `ufw app info [application]` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/ufw_appinfo) |
|
||||
| `--uname` | `uname -a` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/uname) |
|
||||
| `--update-alt-gs` | `update-alternatives --get-selections` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/update_alt_gs) |
|
||||
| ` --update-alt-q` | `update-alternatives --query` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/update_alt_q) |
|
||||
| ` --upower` | `upower` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/upower) |
|
||||
| ` --uptime` | `uptime` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/uptime) |
|
||||
| ` --url` | URL string parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/url) |
|
||||
| ` --vmstat` | `vmstat` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/vmstat) |
|
||||
| ` --vmstat-s` | `vmstat` command streaming parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/vmstat_s) |
|
||||
| ` --w` | `w` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/w) |
|
||||
| ` --wc` | `wc` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/wc) |
|
||||
| ` --who` | `who` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/who) |
|
||||
| ` --x509-cert` | X.509 PEM and DER certificate file parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/x509_cert) |
|
||||
| ` --xml` | XML file parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/xml) |
|
||||
| ` --xrandr` | `xrandr` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/xrandr) |
|
||||
| ` --yaml` | YAML file parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/yaml) |
|
||||
| ` --zipinfo` | `zipinfo` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/zipinfo) |
|
||||
| `--update-alt-q` | `update-alternatives --query` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/update_alt_q) |
|
||||
| `--upower` | `upower` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/upower) |
|
||||
| `--uptime` | `uptime` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/uptime) |
|
||||
| `--url` | URL string parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/url) |
|
||||
| `--ver` | Version string parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/ver) |
|
||||
| `--veracrypt` | `veracrypt` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/veracrypt) |
|
||||
| `--vmstat` | `vmstat` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/vmstat) |
|
||||
| `--vmstat-s` | `vmstat` command streaming parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/vmstat_s) |
|
||||
| `--w` | `w` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/w) |
|
||||
| `--wc` | `wc` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/wc) |
|
||||
| `--who` | `who` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/who) |
|
||||
| `--x509-cert` | X.509 PEM and DER certificate file parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/x509_cert) |
|
||||
| `--x509-csr` | X.509 PEM and DER certificate request file parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/x509_csr) |
|
||||
| `--xml` | XML file parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/xml) |
|
||||
| `--xrandr` | `xrandr` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/xrandr) |
|
||||
| `--yaml` | YAML file parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/yaml) |
|
||||
| `--zipinfo` | `zipinfo` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/zipinfo) |
|
||||
| `--zpool-iostat` | `zpool iostat` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/zpool_iostat) |
|
||||
| `--zpool-status` | `zpool status` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/zpool_status) |
|
||||
|
||||
### Options
|
||||
|
||||
@@ -311,6 +330,54 @@ option.
|
||||
| `-B` | `--bash-comp` | Generate Bash shell completion script ([more info](https://github.com/kellyjonbrazil/jc/wiki/Shell-Completions)) |
|
||||
| `-Z` | `--zsh-comp` | Generate Zsh shell completion script ([more info](https://github.com/kellyjonbrazil/jc/wiki/Shell-Completions)) |
|
||||
|
||||
### Slice
|
||||
Line slicing is supported using the `START:STOP` syntax similar to Python
|
||||
slicing. This allows you to skip lines at the beginning and/or end of the
|
||||
`STDIN` input you would like `jc` to convert.
|
||||
|
||||
`START` and `STOP` can be positive or negative integers or blank and allow
|
||||
you to specify how many lines to skip and how many lines to process.
|
||||
Positive and blank slices are the most memory efficient. Any negative
|
||||
integers in the slice will use more memory.
|
||||
|
||||
For example, to skip the first and last line of the following text, you
|
||||
could express the slice in a couple ways:
|
||||
|
||||
```bash
|
||||
$ cat table.txt
|
||||
### We want to skip this header ###
|
||||
col1 col2
|
||||
foo 1
|
||||
bar 2
|
||||
### We want to skip this footer ###
|
||||
$ cat table.txt | jc 1:-1 --asciitable
|
||||
[{"col1":"foo","col2":"1"},{"col1":"bar","col2":"2"}]
|
||||
$ cat table.txt | jc 1:4 --asciitable
|
||||
[{"col1":"foo","col2":"1"},{"col1":"bar","col2":"2"}]
|
||||
```
|
||||
In this example `1:-1` and `1:4` line slices provide the same output.
|
||||
|
||||
When using positive integers the index location of `STOP` is non-inclusive.
|
||||
Positive slices count from the first line of the input toward the end
|
||||
starting at `0` as the first line. Negative slices count from the last line
|
||||
toward the beginning starting at `-1` as the last line. This is also the way
|
||||
[Python's slicing](https://stackoverflow.com/questions/509211/understanding-slicing)
|
||||
feature works.
|
||||
|
||||
Here is a breakdown of line slice options:
|
||||
|
||||
| Slice Notation | Input Lines Processed |
|
||||
|----------------|--------------------------------------------------------------|
|
||||
| `START:STOP` | lines `START` through `STOP - 1` |
|
||||
| `START:` | lines `START` through the rest of the output |
|
||||
| `:STOP` | lines from the beginning through `STOP - 1` |
|
||||
| `-START:STOP` | `START` lines from the end through `STOP - 1` |
|
||||
| `START:-STOP` | lines `START` through `STOP` lines from the end |
|
||||
| `-START:-STOP` | `START` lines from the end through `STOP` lines from the end |
|
||||
| `-START:` | `START` lines from the end through the rest of the output |
|
||||
| `:-STOP` | lines from the beginning through `STOP` lines from the end |
|
||||
| `:` | all lines |
|
||||
|
||||
### Exit Codes
|
||||
Any fatal errors within `jc` will generate an exit code of `100`, otherwise the
|
||||
exit code will be `0`.
|
||||
@@ -481,20 +548,22 @@ for item in result:
|
||||
print(item["filename"])
|
||||
```
|
||||
|
||||
### Custom Parsers
|
||||
Custom local parser plugins may be placed in a `jc/jcparsers` folder in your
|
||||
local **"App data directory"**:
|
||||
### Parser Plugins
|
||||
Parser plugins may be placed in a `jc/jcparsers` folder in your local
|
||||
**"App data directory"**:
|
||||
|
||||
- Linux/unix: `$HOME/.local/share/jc/jcparsers`
|
||||
- macOS: `$HOME/Library/Application Support/jc/jcparsers`
|
||||
- Windows: `$LOCALAPPDATA\jc\jc\jcparsers`
|
||||
|
||||
Local parser plugins are standard python module files. Use the
|
||||
Parser plugins are standard python module files. Use the
|
||||
[`jc/parsers/foo.py`](https://github.com/kellyjonbrazil/jc/blob/master/jc/parsers/foo.py)
|
||||
or [`jc/parsers/foo_s.py (streaming)`](https://github.com/kellyjonbrazil/jc/blob/master/jc/parsers/foo_s.py)
|
||||
parser as a template and simply place a `.py` file in the `jcparsers` subfolder.
|
||||
Any dependencies can be placed in the `jc` folder above `jcparsers` and can
|
||||
be imported in the parser code.
|
||||
|
||||
Local plugin filenames must be valid python module names and therefore must
|
||||
Parser plugin filenames must be valid python module names and therefore must
|
||||
start with a letter and consist entirely of alphanumerics and underscores.
|
||||
Local plugins may override default parsers.
|
||||
|
||||
@@ -552,7 +621,7 @@ they are run on an unsupported platform. To see all parser information,
|
||||
including compatibility, run `jc -ap`.
|
||||
|
||||
You may still use a parser on an unsupported platform - for example, you may
|
||||
want to parse a file with linux `lsof` output on an macOS or Windows laptop. In
|
||||
want to parse a file with linux `lsof` output on a macOS or Windows laptop. In
|
||||
that case you can suppress the warning message with the `-q` cli option or the
|
||||
`quiet=True` function parameter in `parse()`:
|
||||
|
||||
|
||||
@@ -3,8 +3,8 @@ _jc()
|
||||
local cur prev words cword jc_commands jc_parsers jc_options \
|
||||
jc_about_options jc_about_mod_options jc_help_options jc_special_options
|
||||
|
||||
jc_commands=(acpi airport arp blkid cbt chage cksum crontab date df dig dmidecode dpkg du env file findmnt finger free git gpg hciconfig id ifconfig iostat iptables iw iwconfig jobs last lastb ls lsblk lsmod lsof lspci lsusb md5 md5sum mdadm mount mpstat netstat nmcli ntpq os-prober pidstat ping ping6 pip pip3 postconf printenv ps route rpm rsync sfdisk sha1sum sha224sum sha256sum sha384sum sha512sum shasum ss sshd stat sum sysctl systemctl systeminfo timedatectl top tracepath tracepath6 traceroute traceroute6 udevadm ufw uname update-alternatives upower uptime vdir vmstat w wc who xrandr zipinfo)
|
||||
jc_parsers=(--acpi --airport --airport-s --arp --asciitable --asciitable-m --blkid --cbt --cef --cef-s --chage --cksum --clf --clf-s --crontab --crontab-u --csv --csv-s --date --datetime-iso --df --dig --dir --dmidecode --dpkg-l --du --email-address --env --file --findmnt --finger --free --fstab --git-log --git-log-s --git-ls-remote --gpg --group --gshadow --hash --hashsum --hciconfig --history --hosts --id --ifconfig --ini --ini-dup --iostat --iostat-s --ip-address --iptables --iw-scan --iwconfig --jar-manifest --jobs --jwt --kv --last --ls --ls-s --lsblk --lsmod --lsof --lspci --lsusb --m3u --mdadm --mount --mpstat --mpstat-s --netstat --nmcli --ntpq --openvpn --os-prober --passwd --pci-ids --pgpass --pidstat --pidstat-s --ping --ping-s --pip-list --pip-show --plist --postconf --proc --proc-buddyinfo --proc-consoles --proc-cpuinfo --proc-crypto --proc-devices --proc-diskstats --proc-filesystems --proc-interrupts --proc-iomem --proc-ioports --proc-loadavg --proc-locks --proc-meminfo --proc-modules --proc-mtrr --proc-pagetypeinfo --proc-partitions --proc-slabinfo --proc-softirqs --proc-stat --proc-swaps --proc-uptime --proc-version --proc-vmallocinfo --proc-vmstat --proc-zoneinfo --proc-driver-rtc --proc-net-arp --proc-net-dev --proc-net-dev-mcast --proc-net-if-inet6 --proc-net-igmp --proc-net-igmp6 --proc-net-ipv6-route --proc-net-netlink --proc-net-netstat --proc-net-packet --proc-net-protocols --proc-net-route --proc-net-unix --proc-pid-fdinfo --proc-pid-io --proc-pid-maps --proc-pid-mountinfo --proc-pid-numa-maps --proc-pid-smaps --proc-pid-stat --proc-pid-statm --proc-pid-status --ps --route --rpm-qi --rsync --rsync-s --semver --sfdisk --shadow --ss --sshd-conf --stat --stat-s --sysctl --syslog --syslog-s --syslog-bsd --syslog-bsd-s --systemctl --systemctl-lj --systemctl-ls --systemctl-luf --systeminfo --time --timedatectl --timestamp --toml --top --top-s --tracepath --traceroute --udevadm --ufw --ufw-appinfo --uname --update-alt-gs --update-alt-q --upower --uptime --url --vmstat --vmstat-s --w --wc --who --x509-cert --xml --xrandr --yaml --zipinfo)
|
||||
jc_commands=(acpi airport arp blkid bluetoothctl cbt certbot chage cksum crontab date df dig dmidecode dpkg du env file findmnt finger free git gpg hciconfig host id ifconfig iostat ip iptables iw iwconfig jobs last lastb ls lsattr lsb_release lsblk lsmod lsof lspci lsusb md5 md5sum mdadm mount mpstat netstat nmcli nsd-control ntpq os-prober pidstat ping ping6 pip pip3 postconf printenv ps route rpm rsync sfdisk sha1sum sha224sum sha256sum sha384sum sha512sum shasum ss ssh sshd stat sum sysctl systemctl systeminfo timedatectl top tracepath tracepath6 traceroute traceroute6 udevadm ufw uname update-alternatives upower uptime vdir veracrypt vmstat w wc who xrandr zipinfo zpool)
|
||||
jc_parsers=(--acpi --airport --airport-s --arp --asciitable --asciitable-m --blkid --bluetoothctl --cbt --cef --cef-s --certbot --chage --cksum --clf --clf-s --crontab --crontab-u --csv --csv-s --date --datetime-iso --df --dig --dir --dmidecode --dpkg-l --du --email-address --env --file --find --findmnt --finger --free --fstab --git-log --git-log-s --git-ls-remote --gpg --group --gshadow --hash --hashsum --hciconfig --history --host --hosts --id --ifconfig --ini --ini-dup --iostat --iostat-s --ip-address --iptables --ip-route --iw-scan --iwconfig --jar-manifest --jobs --jwt --kv --last --ls --ls-s --lsattr --lsb-release --lsblk --lsmod --lsof --lspci --lsusb --m3u --mdadm --mount --mpstat --mpstat-s --netstat --nmcli --nsd-control --ntpq --openvpn --os-prober --os-release --passwd --pci-ids --pgpass --pidstat --pidstat-s --ping --ping-s --pip-list --pip-show --plist --postconf --proc --proc-buddyinfo --proc-consoles --proc-cpuinfo --proc-crypto --proc-devices --proc-diskstats --proc-filesystems --proc-interrupts --proc-iomem --proc-ioports --proc-loadavg --proc-locks --proc-meminfo --proc-modules --proc-mtrr --proc-pagetypeinfo --proc-partitions --proc-slabinfo --proc-softirqs --proc-stat --proc-swaps --proc-uptime --proc-version --proc-vmallocinfo --proc-vmstat --proc-zoneinfo --proc-driver-rtc --proc-net-arp --proc-net-dev --proc-net-dev-mcast --proc-net-if-inet6 --proc-net-igmp --proc-net-igmp6 --proc-net-ipv6-route --proc-net-netlink --proc-net-netstat --proc-net-packet --proc-net-protocols --proc-net-route --proc-net-tcp --proc-net-unix --proc-pid-fdinfo --proc-pid-io --proc-pid-maps --proc-pid-mountinfo --proc-pid-numa-maps --proc-pid-smaps --proc-pid-stat --proc-pid-statm --proc-pid-status --ps --resolve-conf --route --rpm-qi --rsync --rsync-s --semver --sfdisk --shadow --srt --ss --ssh-conf --sshd-conf --stat --stat-s --sysctl --syslog --syslog-s --syslog-bsd --syslog-bsd-s --systemctl --systemctl-lj --systemctl-ls --systemctl-luf --systeminfo --time --timedatectl --timestamp --toml --top --top-s --tracepath --traceroute --udevadm --ufw --ufw-appinfo --uname --update-alt-gs --update-alt-q --upower --uptime --url --ver --veracrypt --vmstat --vmstat-s --w --wc --who --x509-cert --x509-csr --xml --xrandr --yaml --zipinfo --zpool-iostat --zpool-status)
|
||||
jc_options=(--force-color -C --debug -d --monochrome -m --meta-out -M --pretty -p --quiet -q --raw -r --unbuffer -u --yaml-out -y)
|
||||
jc_about_options=(--about -a)
|
||||
jc_about_mod_options=(--pretty -p --yaml-out -y --monochrome -m --force-color -C)
|
||||
|
||||
@@ -9,13 +9,15 @@ _jc() {
|
||||
jc_help_options jc_help_options_describe \
|
||||
jc_special_options jc_special_options_describe
|
||||
|
||||
jc_commands=(acpi airport arp blkid cbt chage cksum crontab date df dig dmidecode dpkg du env file findmnt finger free git gpg hciconfig id ifconfig iostat iptables iw iwconfig jobs last lastb ls lsblk lsmod lsof lspci lsusb md5 md5sum mdadm mount mpstat netstat nmcli ntpq os-prober pidstat ping ping6 pip pip3 postconf printenv ps route rpm rsync sfdisk sha1sum sha224sum sha256sum sha384sum sha512sum shasum ss sshd stat sum sysctl systemctl systeminfo timedatectl top tracepath tracepath6 traceroute traceroute6 udevadm ufw uname update-alternatives upower uptime vdir vmstat w wc who xrandr zipinfo)
|
||||
jc_commands=(acpi airport arp blkid bluetoothctl cbt certbot chage cksum crontab date df dig dmidecode dpkg du env file findmnt finger free git gpg hciconfig host id ifconfig iostat ip iptables iw iwconfig jobs last lastb ls lsattr lsb_release lsblk lsmod lsof lspci lsusb md5 md5sum mdadm mount mpstat netstat nmcli nsd-control ntpq os-prober pidstat ping ping6 pip pip3 postconf printenv ps route rpm rsync sfdisk sha1sum sha224sum sha256sum sha384sum sha512sum shasum ss ssh sshd stat sum sysctl systemctl systeminfo timedatectl top tracepath tracepath6 traceroute traceroute6 udevadm ufw uname update-alternatives upower uptime vdir veracrypt vmstat w wc who xrandr zipinfo zpool)
|
||||
jc_commands_describe=(
|
||||
'acpi:run "acpi" command with magic syntax.'
|
||||
'airport:run "airport" command with magic syntax.'
|
||||
'arp:run "arp" command with magic syntax.'
|
||||
'blkid:run "blkid" command with magic syntax.'
|
||||
'bluetoothctl:run "bluetoothctl" command with magic syntax.'
|
||||
'cbt:run "cbt" command with magic syntax.'
|
||||
'certbot:run "certbot" command with magic syntax.'
|
||||
'chage:run "chage" command with magic syntax.'
|
||||
'cksum:run "cksum" command with magic syntax.'
|
||||
'crontab:run "crontab" command with magic syntax.'
|
||||
@@ -33,9 +35,11 @@ _jc() {
|
||||
'git:run "git" command with magic syntax.'
|
||||
'gpg:run "gpg" command with magic syntax.'
|
||||
'hciconfig:run "hciconfig" command with magic syntax.'
|
||||
'host:run "host" command with magic syntax.'
|
||||
'id:run "id" command with magic syntax.'
|
||||
'ifconfig:run "ifconfig" command with magic syntax.'
|
||||
'iostat:run "iostat" command with magic syntax.'
|
||||
'ip:run "ip" command with magic syntax.'
|
||||
'iptables:run "iptables" command with magic syntax.'
|
||||
'iw:run "iw" command with magic syntax.'
|
||||
'iwconfig:run "iwconfig" command with magic syntax.'
|
||||
@@ -43,6 +47,8 @@ _jc() {
|
||||
'last:run "last" command with magic syntax.'
|
||||
'lastb:run "lastb" command with magic syntax.'
|
||||
'ls:run "ls" command with magic syntax.'
|
||||
'lsattr:run "lsattr" command with magic syntax.'
|
||||
'lsb_release:run "lsb_release" command with magic syntax.'
|
||||
'lsblk:run "lsblk" command with magic syntax.'
|
||||
'lsmod:run "lsmod" command with magic syntax.'
|
||||
'lsof:run "lsof" command with magic syntax.'
|
||||
@@ -55,6 +61,7 @@ _jc() {
|
||||
'mpstat:run "mpstat" command with magic syntax.'
|
||||
'netstat:run "netstat" command with magic syntax.'
|
||||
'nmcli:run "nmcli" command with magic syntax.'
|
||||
'nsd-control:run "nsd-control" command with magic syntax.'
|
||||
'ntpq:run "ntpq" command with magic syntax.'
|
||||
'os-prober:run "os-prober" command with magic syntax.'
|
||||
'pidstat:run "pidstat" command with magic syntax.'
|
||||
@@ -76,6 +83,7 @@ _jc() {
|
||||
'sha512sum:run "sha512sum" command with magic syntax.'
|
||||
'shasum:run "shasum" command with magic syntax.'
|
||||
'ss:run "ss" command with magic syntax.'
|
||||
'ssh:run "ssh" command with magic syntax.'
|
||||
'sshd:run "sshd" command with magic syntax.'
|
||||
'stat:run "stat" command with magic syntax.'
|
||||
'sum:run "sum" command with magic syntax.'
|
||||
@@ -95,14 +103,16 @@ _jc() {
|
||||
'upower:run "upower" command with magic syntax.'
|
||||
'uptime:run "uptime" command with magic syntax.'
|
||||
'vdir:run "vdir" command with magic syntax.'
|
||||
'veracrypt:run "veracrypt" command with magic syntax.'
|
||||
'vmstat:run "vmstat" command with magic syntax.'
|
||||
'w:run "w" command with magic syntax.'
|
||||
'wc:run "wc" command with magic syntax.'
|
||||
'who:run "who" command with magic syntax.'
|
||||
'xrandr:run "xrandr" command with magic syntax.'
|
||||
'zipinfo:run "zipinfo" command with magic syntax.'
|
||||
'zpool:run "zpool" command with magic syntax.'
|
||||
)
|
||||
jc_parsers=(--acpi --airport --airport-s --arp --asciitable --asciitable-m --blkid --cbt --cef --cef-s --chage --cksum --clf --clf-s --crontab --crontab-u --csv --csv-s --date --datetime-iso --df --dig --dir --dmidecode --dpkg-l --du --email-address --env --file --findmnt --finger --free --fstab --git-log --git-log-s --git-ls-remote --gpg --group --gshadow --hash --hashsum --hciconfig --history --hosts --id --ifconfig --ini --ini-dup --iostat --iostat-s --ip-address --iptables --iw-scan --iwconfig --jar-manifest --jobs --jwt --kv --last --ls --ls-s --lsblk --lsmod --lsof --lspci --lsusb --m3u --mdadm --mount --mpstat --mpstat-s --netstat --nmcli --ntpq --openvpn --os-prober --passwd --pci-ids --pgpass --pidstat --pidstat-s --ping --ping-s --pip-list --pip-show --plist --postconf --proc --proc-buddyinfo --proc-consoles --proc-cpuinfo --proc-crypto --proc-devices --proc-diskstats --proc-filesystems --proc-interrupts --proc-iomem --proc-ioports --proc-loadavg --proc-locks --proc-meminfo --proc-modules --proc-mtrr --proc-pagetypeinfo --proc-partitions --proc-slabinfo --proc-softirqs --proc-stat --proc-swaps --proc-uptime --proc-version --proc-vmallocinfo --proc-vmstat --proc-zoneinfo --proc-driver-rtc --proc-net-arp --proc-net-dev --proc-net-dev-mcast --proc-net-if-inet6 --proc-net-igmp --proc-net-igmp6 --proc-net-ipv6-route --proc-net-netlink --proc-net-netstat --proc-net-packet --proc-net-protocols --proc-net-route --proc-net-unix --proc-pid-fdinfo --proc-pid-io --proc-pid-maps --proc-pid-mountinfo --proc-pid-numa-maps --proc-pid-smaps --proc-pid-stat --proc-pid-statm --proc-pid-status --ps --route --rpm-qi --rsync --rsync-s --semver --sfdisk --shadow --ss --sshd-conf --stat --stat-s --sysctl --syslog --syslog-s --syslog-bsd --syslog-bsd-s --systemctl --systemctl-lj --systemctl-ls --systemctl-luf --systeminfo --time --timedatectl --timestamp --toml --top --top-s --tracepath --traceroute --udevadm --ufw --ufw-appinfo --uname --update-alt-gs --update-alt-q --upower --uptime --url --vmstat --vmstat-s --w --wc --who --x509-cert --xml --xrandr --yaml --zipinfo)
|
||||
jc_parsers=(--acpi --airport --airport-s --arp --asciitable --asciitable-m --blkid --bluetoothctl --cbt --cef --cef-s --certbot --chage --cksum --clf --clf-s --crontab --crontab-u --csv --csv-s --date --datetime-iso --df --dig --dir --dmidecode --dpkg-l --du --email-address --env --file --find --findmnt --finger --free --fstab --git-log --git-log-s --git-ls-remote --gpg --group --gshadow --hash --hashsum --hciconfig --history --host --hosts --id --ifconfig --ini --ini-dup --iostat --iostat-s --ip-address --iptables --ip-route --iw-scan --iwconfig --jar-manifest --jobs --jwt --kv --last --ls --ls-s --lsattr --lsb-release --lsblk --lsmod --lsof --lspci --lsusb --m3u --mdadm --mount --mpstat --mpstat-s --netstat --nmcli --nsd-control --ntpq --openvpn --os-prober --os-release --passwd --pci-ids --pgpass --pidstat --pidstat-s --ping --ping-s --pip-list --pip-show --plist --postconf --proc --proc-buddyinfo --proc-consoles --proc-cpuinfo --proc-crypto --proc-devices --proc-diskstats --proc-filesystems --proc-interrupts --proc-iomem --proc-ioports --proc-loadavg --proc-locks --proc-meminfo --proc-modules --proc-mtrr --proc-pagetypeinfo --proc-partitions --proc-slabinfo --proc-softirqs --proc-stat --proc-swaps --proc-uptime --proc-version --proc-vmallocinfo --proc-vmstat --proc-zoneinfo --proc-driver-rtc --proc-net-arp --proc-net-dev --proc-net-dev-mcast --proc-net-if-inet6 --proc-net-igmp --proc-net-igmp6 --proc-net-ipv6-route --proc-net-netlink --proc-net-netstat --proc-net-packet --proc-net-protocols --proc-net-route --proc-net-tcp --proc-net-unix --proc-pid-fdinfo --proc-pid-io --proc-pid-maps --proc-pid-mountinfo --proc-pid-numa-maps --proc-pid-smaps --proc-pid-stat --proc-pid-statm --proc-pid-status --ps --resolve-conf --route --rpm-qi --rsync --rsync-s --semver --sfdisk --shadow --srt --ss --ssh-conf --sshd-conf --stat --stat-s --sysctl --syslog --syslog-s --syslog-bsd --syslog-bsd-s --systemctl --systemctl-lj --systemctl-ls --systemctl-luf --systeminfo --time --timedatectl --timestamp --toml --top --top-s --tracepath --traceroute --udevadm --ufw --ufw-appinfo --uname --update-alt-gs --update-alt-q --upower --uptime --url --ver --veracrypt --vmstat --vmstat-s --w --wc --who --x509-cert --x509-csr --xml --xrandr --yaml --zipinfo --zpool-iostat --zpool-status)
|
||||
jc_parsers_describe=(
|
||||
'--acpi:`acpi` command parser'
|
||||
'--airport:`airport -I` command parser'
|
||||
@@ -111,9 +121,11 @@ _jc() {
|
||||
'--asciitable:ASCII and Unicode table parser'
|
||||
'--asciitable-m:multi-line ASCII and Unicode table parser'
|
||||
'--blkid:`blkid` command parser'
|
||||
'--bluetoothctl:`bluetoothctl` command parser'
|
||||
'--cbt:`cbt` (Google Bigtable) command parser'
|
||||
'--cef:CEF string parser'
|
||||
'--cef-s:CEF string streaming parser'
|
||||
'--certbot:`certbot` command parser'
|
||||
'--chage:`chage --list` command parser'
|
||||
'--cksum:`cksum` and `sum` command parser'
|
||||
'--clf:Common and Combined Log Format file parser'
|
||||
@@ -133,6 +145,7 @@ _jc() {
|
||||
'--email-address:Email Address string parser'
|
||||
'--env:`env` command parser'
|
||||
'--file:`file` command parser'
|
||||
'--find:`find` command parser'
|
||||
'--findmnt:`findmnt` command parser'
|
||||
'--finger:`finger` command parser'
|
||||
'--free:`free` command parser'
|
||||
@@ -147,6 +160,7 @@ _jc() {
|
||||
'--hashsum:hashsum command parser (`md5sum`, `shasum`, etc.)'
|
||||
'--hciconfig:`hciconfig` command parser'
|
||||
'--history:`history` command parser'
|
||||
'--host:`host` command parser'
|
||||
'--hosts:`/etc/hosts` file parser'
|
||||
'--id:`id` command parser'
|
||||
'--ifconfig:`ifconfig` command parser'
|
||||
@@ -156,6 +170,7 @@ _jc() {
|
||||
'--iostat-s:`iostat` command streaming parser'
|
||||
'--ip-address:IPv4 and IPv6 Address string parser'
|
||||
'--iptables:`iptables` command parser'
|
||||
'--ip-route:`ip route` command parser'
|
||||
'--iw-scan:`iw dev [device] scan` command parser'
|
||||
'--iwconfig:`iwconfig` command parser'
|
||||
'--jar-manifest:Java MANIFEST.MF file parser'
|
||||
@@ -165,6 +180,8 @@ _jc() {
|
||||
'--last:`last` and `lastb` command parser'
|
||||
'--ls:`ls` command parser'
|
||||
'--ls-s:`ls` command streaming parser'
|
||||
'--lsattr:`lsattr` command parser'
|
||||
'--lsb-release:`lsb_release` command parser'
|
||||
'--lsblk:`lsblk` command parser'
|
||||
'--lsmod:`lsmod` command parser'
|
||||
'--lsof:`lsof` command parser'
|
||||
@@ -177,9 +194,11 @@ _jc() {
|
||||
'--mpstat-s:`mpstat` command streaming parser'
|
||||
'--netstat:`netstat` command parser'
|
||||
'--nmcli:`nmcli` command parser'
|
||||
'--nsd-control:`nsd-control` command parser'
|
||||
'--ntpq:`ntpq -p` command parser'
|
||||
'--openvpn:openvpn-status.log file parser'
|
||||
'--os-prober:`os-prober` command parser'
|
||||
'--os-release:`/etc/os-release` file parser'
|
||||
'--passwd:`/etc/passwd` file parser'
|
||||
'--pci-ids:`pci.ids` file parser'
|
||||
'--pgpass:PostgreSQL password file parser'
|
||||
@@ -231,6 +250,7 @@ _jc() {
|
||||
'--proc-net-packet:`/proc/net/packet` file parser'
|
||||
'--proc-net-protocols:`/proc/net/protocols` file parser'
|
||||
'--proc-net-route:`/proc/net/route` file parser'
|
||||
'--proc-net-tcp:`/proc/net/tcp` and `/proc/net/tcp6` file parser'
|
||||
'--proc-net-unix:`/proc/net/unix` file parser'
|
||||
'--proc-pid-fdinfo:`/proc/<pid>/fdinfo/<fd>` file parser'
|
||||
'--proc-pid-io:`/proc/<pid>/io` file parser'
|
||||
@@ -242,6 +262,7 @@ _jc() {
|
||||
'--proc-pid-statm:`/proc/<pid>/statm` file parser'
|
||||
'--proc-pid-status:`/proc/<pid>/status` file parser'
|
||||
'--ps:`ps` command parser'
|
||||
'--resolve-conf:`/etc/resolve.conf` file parser'
|
||||
'--route:`route` command parser'
|
||||
'--rpm-qi:`rpm -qi` command parser'
|
||||
'--rsync:`rsync` command parser'
|
||||
@@ -249,8 +270,10 @@ _jc() {
|
||||
'--semver:Semantic Version string parser'
|
||||
'--sfdisk:`sfdisk` command parser'
|
||||
'--shadow:`/etc/shadow` file parser'
|
||||
'--srt:SRT file parser'
|
||||
'--ss:`ss` command parser'
|
||||
'--sshd-conf:sshd config file and `sshd -T` command parser'
|
||||
'--ssh-conf:`ssh` config file and `ssh -G` command parser'
|
||||
'--sshd-conf:`sshd` config file and `sshd -T` command parser'
|
||||
'--stat:`stat` command parser'
|
||||
'--stat-s:`stat` command streaming parser'
|
||||
'--sysctl:`sysctl` command parser'
|
||||
@@ -280,16 +303,21 @@ _jc() {
|
||||
'--upower:`upower` command parser'
|
||||
'--uptime:`uptime` command parser'
|
||||
'--url:URL string parser'
|
||||
'--ver:Version string parser'
|
||||
'--veracrypt:`veracrypt` command parser'
|
||||
'--vmstat:`vmstat` command parser'
|
||||
'--vmstat-s:`vmstat` command streaming parser'
|
||||
'--w:`w` command parser'
|
||||
'--wc:`wc` command parser'
|
||||
'--who:`who` command parser'
|
||||
'--x509-cert:X.509 PEM and DER certificate file parser'
|
||||
'--x509-csr:X.509 PEM and DER certificate request file parser'
|
||||
'--xml:XML file parser'
|
||||
'--xrandr:`xrandr` command parser'
|
||||
'--yaml:YAML file parser'
|
||||
'--zipinfo:`zipinfo` command parser'
|
||||
'--zpool-iostat:`zpool iostat` command parser'
|
||||
'--zpool-status:`zpool status` command parser'
|
||||
)
|
||||
jc_options=(--force-color -C --debug -d --monochrome -m --meta-out -M --pretty -p --quiet -q --raw -r --unbuffer -u --yaml-out -y)
|
||||
jc_options_describe=(
|
||||
|
||||
@@ -26,7 +26,7 @@ def parse(
|
||||
data: Union[str, bytes, Iterable[str]],
|
||||
quiet: bool = False,
|
||||
raw: bool = False,
|
||||
ignore_exceptions: bool = None,
|
||||
ignore_exceptions: Optional[bool] = None,
|
||||
**kwargs
|
||||
) -> Union[JSONDictType, List[JSONDictType], Iterator[JSONDictType]]
|
||||
```
|
||||
|
||||
@@ -250,4 +250,4 @@ Returns:
|
||||
### Parser Information
|
||||
Compatibility: linux
|
||||
|
||||
Version 1.4 by Kelly Brazil (kellyjonbrazil@gmail.com)
|
||||
Version 1.6 by Kelly Brazil (kellyjonbrazil@gmail.com)
|
||||
|
||||
133
docs/parsers/bluetoothctl.md
Normal file
133
docs/parsers/bluetoothctl.md
Normal file
@@ -0,0 +1,133 @@
|
||||
[Home](https://kellyjonbrazil.github.io/jc/)
|
||||
<a id="jc.parsers.bluetoothctl"></a>
|
||||
|
||||
# jc.parsers.bluetoothctl
|
||||
|
||||
jc - JSON Convert `bluetoothctl` command output parser
|
||||
|
||||
Supports the following `bluetoothctl` subcommands:
|
||||
- `bluetoothctl list`
|
||||
- `bluetoothctl show`
|
||||
- `bluetoothctl show <ctrl>`
|
||||
- `bluetoothctl devices`
|
||||
- `bluetoothctl info <dev>`
|
||||
|
||||
Usage (cli):
|
||||
|
||||
$ bluetoothctl info <dev> | jc --bluetoothctl
|
||||
or
|
||||
|
||||
$ jc bluetoothctl info <dev>
|
||||
|
||||
Usage (module):
|
||||
|
||||
import jc
|
||||
result = jc.parse('bluetoothctl', bluetoothctl_command_output)
|
||||
|
||||
Schema:
|
||||
|
||||
Because bluetoothctl is handling two main entities, controllers and devices,
|
||||
the schema is shared between them. Most of the fields are common between
|
||||
a controller and a device but there might be fields corresponding to one entity.
|
||||
|
||||
Controller:
|
||||
[
|
||||
{
|
||||
"name": string,
|
||||
"is_default": boolean,
|
||||
"is_public": boolean,
|
||||
"is_random": boolean,
|
||||
"address": string,
|
||||
"alias": string,
|
||||
"class": string,
|
||||
"powered": string,
|
||||
"discoverable": string,
|
||||
"discoverable_timeout": string,
|
||||
"pairable": string,
|
||||
"modalias": string,
|
||||
"discovering": string,
|
||||
"uuids": array
|
||||
}
|
||||
]
|
||||
|
||||
Device:
|
||||
[
|
||||
{
|
||||
"name": string,
|
||||
"is_public": boolean,
|
||||
"is_random": boolean,
|
||||
"address": string,
|
||||
"alias": string,
|
||||
"appearance": string,
|
||||
"class": string,
|
||||
"icon": string,
|
||||
"paired": string,
|
||||
"bonded": string,
|
||||
"trusted": string,
|
||||
"blocked": string,
|
||||
"connected": string,
|
||||
"legacy_pairing": string,
|
||||
"rssi": int,
|
||||
"txpower": int,
|
||||
"uuids": array,
|
||||
"modalias": string
|
||||
}
|
||||
]
|
||||
|
||||
Examples:
|
||||
|
||||
$ bluetoothctl info EB:06:EF:62:B3:19 | jc --bluetoothctl -p
|
||||
[
|
||||
{
|
||||
"address": "22:06:33:62:B3:19",
|
||||
"is_public": true,
|
||||
"name": "TaoTronics TT-BH336",
|
||||
"alias": "TaoTronics TT-BH336",
|
||||
"class": "0x00240455",
|
||||
"icon": "audio-headset",
|
||||
"paired": "no",
|
||||
"bonded": "no",
|
||||
"trusted": "no",
|
||||
"blocked": "no",
|
||||
"connected": "no",
|
||||
"legacy_pairing": "no",
|
||||
"uuids": [
|
||||
"Advanced Audio Distribu.. (0000120d-0000-1000-8000-00805f9b34fb)",
|
||||
"Audio Sink (0000130b-0000-1000-8000-00805f9b34fb)",
|
||||
"A/V Remote Control (0000140e-0000-1000-8000-00805f9b34fb)",
|
||||
"A/V Remote Control Cont.. (0000150f-0000-1000-8000-00805f9b34fb)",
|
||||
"Handsfree (0000161e-0000-1000-8000-00805f9b34fb)",
|
||||
"Headset (00001708-0000-1000-8000-00805f9b34fb)",
|
||||
"Headset HS (00001831-0000-1000-8000-00805f9b34fb)"
|
||||
],
|
||||
"rssi": -52,
|
||||
"txpower": 4
|
||||
}
|
||||
]
|
||||
|
||||
<a id="jc.parsers.bluetoothctl.parse"></a>
|
||||
|
||||
### parse
|
||||
|
||||
```python
|
||||
def parse(data: str,
|
||||
raw: bool = False,
|
||||
quiet: bool = False) -> List[JSONDictType]
|
||||
```
|
||||
|
||||
Main text parsing function
|
||||
|
||||
Parameters:
|
||||
|
||||
data: (string) text data to parse
|
||||
raw: (boolean) unprocessed output if True
|
||||
quiet: (boolean) suppress warning messages if True
|
||||
|
||||
Returns:
|
||||
|
||||
List of Dictionaries. Raw or processed structured data.
|
||||
|
||||
### Parser Information
|
||||
Compatibility: linux
|
||||
|
||||
Version 1.1 by Jake Ob (iakopap at gmail.com)
|
||||
161
docs/parsers/certbot.md
Normal file
161
docs/parsers/certbot.md
Normal file
@@ -0,0 +1,161 @@
|
||||
[Home](https://kellyjonbrazil.github.io/jc/)
|
||||
<a id="jc.parsers.certbot"></a>
|
||||
|
||||
# jc.parsers.certbot
|
||||
|
||||
jc - JSON Convert `certbot` command output parser
|
||||
|
||||
Supports the following `certbot` commands:
|
||||
|
||||
- `certbot show_account`
|
||||
- `certbot certificates`
|
||||
|
||||
Verbose options are not supported.
|
||||
|
||||
Usage (cli):
|
||||
|
||||
$ certbot show_account | jc --certbot
|
||||
$ certbot certificates | jc --certbot
|
||||
|
||||
or
|
||||
|
||||
$ jc certbot show_account
|
||||
$ jc certbot certificates
|
||||
|
||||
Usage (module):
|
||||
|
||||
import jc
|
||||
result = jc.parse('certbot', certbot_command_output)
|
||||
|
||||
Schema:
|
||||
|
||||
{
|
||||
"certificates": [
|
||||
{
|
||||
"name": string,
|
||||
"serial_number": string,
|
||||
"key_type": string,
|
||||
"domains": [
|
||||
string
|
||||
],
|
||||
"expiration_date": string,
|
||||
"expiration_date_epoch": integer,
|
||||
"expiration_date_epoch_utc": integer,
|
||||
"expiration_date_iso": string,
|
||||
"validity": string,
|
||||
"certificate_path": string,
|
||||
"private_key_path": string
|
||||
}
|
||||
],
|
||||
"account": {
|
||||
"server": string,
|
||||
"url": string,
|
||||
"email": string
|
||||
}
|
||||
}
|
||||
|
||||
Examples:
|
||||
|
||||
$ certbot certificates | jc --certbot -p
|
||||
{
|
||||
"certificates": [
|
||||
{
|
||||
"name": "example.com",
|
||||
"serial_number": "3f7axxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
|
||||
"key_type": "RSA",
|
||||
"domains": [
|
||||
"example.com",
|
||||
"www.example.com"
|
||||
],
|
||||
"expiration_date": "2023-05-11 01:33:10+00:00",
|
||||
"validity": "63 days",
|
||||
"certificate_path": "/etc/letsencrypt/live/example.com/chain.pem",
|
||||
"private_key_path": "/etc/letsencrypt/live/example.com/priv.pem",
|
||||
"expiration_date_epoch": 1683793990,
|
||||
"expiration_date_epoch_utc": 1683768790,
|
||||
"expiration_date_iso": "2023-05-11T01:33:10+00:00"
|
||||
},
|
||||
{
|
||||
"name": "example.org",
|
||||
"serial_number": "3bcyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy",
|
||||
"key_type": "RSA",
|
||||
"domains": [
|
||||
"example.org",
|
||||
"www.example.org"
|
||||
],
|
||||
"expiration_date": "2023-06-12 01:35:30+00:00",
|
||||
"validity": "63 days",
|
||||
"certificate_path": "/etc/letsencrypt/live/example.org/chain.pem",
|
||||
"private_key_path": "/etc/letsencrypt/live/example.org/key.pem",
|
||||
"expiration_date_epoch": 1686558930,
|
||||
"expiration_date_epoch_utc": 1686533730,
|
||||
"expiration_date_iso": "2023-06-12T01:35:30+00:00"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
$ certbot certificates | jc --certbot -p -r
|
||||
{
|
||||
"certificates": [
|
||||
{
|
||||
"name": "example.com",
|
||||
"serial_number": "3f7axxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
|
||||
"key_type": "RSA",
|
||||
"domains": [
|
||||
"example.com",
|
||||
"www.example.com"
|
||||
],
|
||||
"expiration_date": "2023-05-11 01:33:10+00:00",
|
||||
"validity": "63 days",
|
||||
"certificate_path": "/etc/letsencrypt/live/example.com/chain.pem",
|
||||
"private_key_path": "/etc/letsencrypt/live/example.com/priv.pem"
|
||||
},
|
||||
{
|
||||
"name": "example.org",
|
||||
"serial_number": "3bcyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy",
|
||||
"key_type": "RSA",
|
||||
"domains": [
|
||||
"example.org",
|
||||
"www.example.org"
|
||||
],
|
||||
"expiration_date": "2023-06-12 01:35:30+00:00",
|
||||
"validity": "63 days",
|
||||
"certificate_path": "/etc/letsencrypt/live/example.org/chain.pem",
|
||||
"private_key_path": "/etc/letsencrypt/live/example.org/key.pem"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
$ certbot show_account | jc --certbot -p
|
||||
{
|
||||
"account": {
|
||||
"server": "https://acme-staging-v02.api.letsencrypt.org/directory",
|
||||
"url": "https://acme-staging-v02.api.letsencrypt.org/acme/acct/123",
|
||||
"email": "some@example.com"
|
||||
}
|
||||
}
|
||||
|
||||
<a id="jc.parsers.certbot.parse"></a>
|
||||
|
||||
### parse
|
||||
|
||||
```python
|
||||
def parse(data: str, raw: bool = False, quiet: bool = False) -> JSONDictType
|
||||
```
|
||||
|
||||
Main text parsing function
|
||||
|
||||
Parameters:
|
||||
|
||||
data: (string) text data to parse
|
||||
raw: (boolean) unprocessed output if True
|
||||
quiet: (boolean) suppress warning messages if True
|
||||
|
||||
Returns:
|
||||
|
||||
Dictionary. Raw or processed structured data.
|
||||
|
||||
### Parser Information
|
||||
Compatibility: linux, darwin, cygwin, win32, aix, freebsd
|
||||
|
||||
Version 1.2 by Kelly Brazil (kellyjonbrazil@gmail.com)
|
||||
@@ -196,4 +196,4 @@ Returns:
|
||||
### Parser Information
|
||||
Compatibility: linux, darwin, aix, freebsd
|
||||
|
||||
Version 1.6 by Kelly Brazil (kellyjonbrazil@gmail.com)
|
||||
Version 1.8 by Kelly Brazil (kellyjonbrazil@gmail.com)
|
||||
|
||||
@@ -193,4 +193,4 @@ Returns:
|
||||
### Parser Information
|
||||
Compatibility: linux, darwin, aix, freebsd
|
||||
|
||||
Version 1.7 by Kelly Brazil (kellyjonbrazil@gmail.com)
|
||||
Version 1.9 by Kelly Brazil (kellyjonbrazil@gmail.com)
|
||||
|
||||
@@ -9,6 +9,7 @@ Options supported:
|
||||
- `+noall +answer` options are supported in cases where only the answer
|
||||
information is desired.
|
||||
- `+axfr` option is supported on its own
|
||||
- `+nsid` option is supported
|
||||
|
||||
The `when_epoch` calculated timestamp field is naive. (i.e. based on the
|
||||
local time of the system the parser is run on)
|
||||
@@ -345,4 +346,4 @@ Returns:
|
||||
### Parser Information
|
||||
Compatibility: linux, aix, freebsd, darwin, win32, cygwin
|
||||
|
||||
Version 2.4 by Kelly Brazil (kellyjonbrazil@gmail.com)
|
||||
Version 2.5 by Kelly Brazil (kellyjonbrazil@gmail.com)
|
||||
|
||||
@@ -90,10 +90,10 @@ Parameters:
|
||||
|
||||
Returns:
|
||||
|
||||
Dictionary of raw structured data or
|
||||
List of Dictionaries of processed structured data
|
||||
Dictionary of raw structured data or (default)
|
||||
List of Dictionaries of processed structured data (raw)
|
||||
|
||||
### Parser Information
|
||||
Compatibility: linux, darwin, cygwin, win32, aix, freebsd
|
||||
|
||||
Version 1.4 by Kelly Brazil (kellyjonbrazil@gmail.com)
|
||||
Version 1.5 by Kelly Brazil (kellyjonbrazil@gmail.com)
|
||||
|
||||
82
docs/parsers/find.md
Normal file
82
docs/parsers/find.md
Normal file
@@ -0,0 +1,82 @@
|
||||
[Home](https://kellyjonbrazil.github.io/jc/)
|
||||
<a id="jc.parsers.find"></a>
|
||||
|
||||
# jc.parsers.find
|
||||
|
||||
jc - JSON Convert `find` command output parser
|
||||
|
||||
This parser returns a list of objects by default and a list of strings if
|
||||
the `--raw` option is used.
|
||||
|
||||
Usage (cli):
|
||||
|
||||
$ find | jc --find
|
||||
|
||||
Usage (module):
|
||||
|
||||
import jc
|
||||
result = jc.parse('find', find_command_output)
|
||||
|
||||
Schema:
|
||||
|
||||
[
|
||||
{
|
||||
"path": string,
|
||||
"node": string,
|
||||
"error": string
|
||||
}
|
||||
]
|
||||
|
||||
Examples:
|
||||
|
||||
$ find | jc --find -p
|
||||
[
|
||||
{
|
||||
"path": "./directory"
|
||||
"node": "filename"
|
||||
},
|
||||
{
|
||||
"path": "./anotherdirectory"
|
||||
"node": "anotherfile"
|
||||
},
|
||||
{
|
||||
"path": null
|
||||
"node": null
|
||||
"error": "find: './inaccessible': Permission denied"
|
||||
}
|
||||
...
|
||||
]
|
||||
|
||||
$ find | jc --find -p -r
|
||||
[
|
||||
"./templates/readme_template",
|
||||
"./templates/manpage_template",
|
||||
"./.github/workflows/pythonapp.yml",
|
||||
...
|
||||
]
|
||||
|
||||
<a id="jc.parsers.find.parse"></a>
|
||||
|
||||
### parse
|
||||
|
||||
```python
|
||||
def parse(data, raw=False, quiet=False)
|
||||
```
|
||||
|
||||
Main text parsing function
|
||||
|
||||
Parameters:
|
||||
|
||||
data: (string) text data to parse
|
||||
raw: (boolean) unprocessed output if True
|
||||
quiet: (boolean) suppress warning messages if True
|
||||
|
||||
Returns:
|
||||
|
||||
List of raw strings or
|
||||
List of Dictionaries of processed structured data
|
||||
|
||||
### Parser Information
|
||||
Compatibility: linux
|
||||
|
||||
Version 1.0 by Solomon Leang (solomonleang@gmail.com)
|
||||
@@ -172,4 +172,4 @@ Returns:
|
||||
### Parser Information
|
||||
Compatibility: linux, darwin, cygwin, win32, aix, freebsd
|
||||
|
||||
Version 1.3 by Kelly Brazil (kellyjonbrazil@gmail.com)
|
||||
Version 1.4 by Kelly Brazil (kellyjonbrazil@gmail.com)
|
||||
|
||||
@@ -108,4 +108,4 @@ Returns:
|
||||
### Parser Information
|
||||
Compatibility: linux, darwin, cygwin, win32, aix, freebsd
|
||||
|
||||
Version 1.3 by Kelly Brazil (kellyjonbrazil@gmail.com)
|
||||
Version 1.4 by Kelly Brazil (kellyjonbrazil@gmail.com)
|
||||
|
||||
113
docs/parsers/host.md
Normal file
113
docs/parsers/host.md
Normal file
@@ -0,0 +1,113 @@
|
||||
[Home](https://kellyjonbrazil.github.io/jc/)
|
||||
<a id="jc.parsers.host"></a>
|
||||
|
||||
# jc.parsers.host
|
||||
|
||||
jc - JSON Convert `host` command output parser
|
||||
|
||||
Supports parsing of the most commonly used RR types (A, AAAA, MX, TXT)
|
||||
|
||||
Usage (cli):
|
||||
|
||||
$ host google.com | jc --host
|
||||
|
||||
or
|
||||
|
||||
$ jc host google.com
|
||||
|
||||
Usage (module):
|
||||
|
||||
import jc
|
||||
result = jc.parse('host', host_command_output)
|
||||
|
||||
Schema:
|
||||
|
||||
[
|
||||
{
|
||||
"hostname": string,
|
||||
"address": [
|
||||
string
|
||||
],
|
||||
"v6-address": [
|
||||
string
|
||||
],
|
||||
"mail": [
|
||||
string
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
[
|
||||
{
|
||||
"nameserver": string,
|
||||
"zone": string,
|
||||
"mname": string,
|
||||
"rname": string,
|
||||
"serial": integer,
|
||||
"refresh": integer,
|
||||
"retry": integer,
|
||||
"expire": integer,
|
||||
"minimum": integer
|
||||
}
|
||||
]
|
||||
|
||||
Examples:
|
||||
|
||||
$ host google.com | jc --host
|
||||
[
|
||||
{
|
||||
"hostname": "google.com",
|
||||
"address": [
|
||||
"142.251.39.110"
|
||||
],
|
||||
"v6-address": [
|
||||
"2a00:1450:400e:811::200e"
|
||||
],
|
||||
"mail": [
|
||||
"smtp.google.com."
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
$ jc host -C sunet.se
|
||||
[
|
||||
{
|
||||
"nameserver": "2001:6b0:7::2",
|
||||
"zone": "sunet.se",
|
||||
"mname": "sunic.sunet.se.",
|
||||
"rname": "hostmaster.sunet.se.",
|
||||
"serial": "2023090401",
|
||||
"refresh": "28800",
|
||||
"retry": "7200",
|
||||
"expire": "604800",
|
||||
"minimum": "300"
|
||||
},
|
||||
{
|
||||
...
|
||||
}
|
||||
]
|
||||
|
||||
<a id="jc.parsers.host.parse"></a>
|
||||
|
||||
### parse
|
||||
|
||||
```python
|
||||
def parse(data: str, raw: bool = False, quiet: bool = False)
|
||||
```
|
||||
|
||||
Main text parsing function
|
||||
|
||||
Parameters:
|
||||
|
||||
data: (string) text data to parse
|
||||
raw: (boolean) unprocessed output if True
|
||||
quiet: (boolean) suppress warning messages if True
|
||||
|
||||
Returns:
|
||||
|
||||
List of Dictionaries. Raw or processed structured data.
|
||||
|
||||
### Parser Information
|
||||
Compatibility: linux, darwin, cygwin, win32, aix, freebsd
|
||||
|
||||
Version 1.0 by Pettai (pettai@sunet.se)
|
||||
@@ -240,4 +240,4 @@ Returns:
|
||||
### Parser Information
|
||||
Compatibility: linux, aix, freebsd, darwin
|
||||
|
||||
Version 2.2 by Kelly Brazil (kellyjonbrazil@gmail.com)
|
||||
Version 2.3 by Kelly Brazil (kellyjonbrazil@gmail.com)
|
||||
|
||||
@@ -22,12 +22,12 @@ contained in lists/arrays.
|
||||
|
||||
Usage (cli):
|
||||
|
||||
$ cat foo.ini | jc --ini
|
||||
$ cat foo.ini | jc --ini-dup
|
||||
|
||||
Usage (module):
|
||||
|
||||
import jc
|
||||
result = jc.parse('ini', ini_file_output)
|
||||
result = jc.parse('ini_dup', ini_file_output)
|
||||
|
||||
Schema:
|
||||
|
||||
@@ -67,7 +67,7 @@ Examples:
|
||||
fruit = peach
|
||||
color = green
|
||||
|
||||
$ cat example.ini | jc --ini -p
|
||||
$ cat example.ini | jc --ini-dup -p
|
||||
{
|
||||
"foo": [
|
||||
"fiz"
|
||||
@@ -118,4 +118,4 @@ Returns:
|
||||
### Parser Information
|
||||
Compatibility: linux, darwin, cygwin, win32, aix, freebsd
|
||||
|
||||
Version 1.0 by Kelly Brazil (kellyjonbrazil@gmail.com)
|
||||
Version 1.1 by Kelly Brazil (kellyjonbrazil@gmail.com)
|
||||
|
||||
74
docs/parsers/ip_route.md
Normal file
74
docs/parsers/ip_route.md
Normal file
@@ -0,0 +1,74 @@
|
||||
[Home](https://kellyjonbrazil.github.io/jc/)
|
||||
<a id="jc.parsers.ip_route"></a>
|
||||
|
||||
# jc.parsers.ip\_route
|
||||
|
||||
jc - JSON Convert `ip route` command output parser
|
||||
|
||||
Usage (cli):
|
||||
|
||||
$ ip route | jc --ip-route
|
||||
|
||||
or
|
||||
|
||||
$ jc ip-route
|
||||
|
||||
Usage (module):
|
||||
|
||||
import jc
|
||||
result = jc.parse('ip_route', ip_route_command_output)
|
||||
|
||||
Schema:
|
||||
|
||||
[
|
||||
{
|
||||
"ip": string,
|
||||
"via": string,
|
||||
"dev": string,
|
||||
"metric": integer,
|
||||
"proto": string,
|
||||
"scope": string,
|
||||
"src": string,
|
||||
"via": string,
|
||||
"status": string
|
||||
}
|
||||
]
|
||||
|
||||
Examples:
|
||||
|
||||
$ ip route | jc --ip-route -p
|
||||
[
|
||||
{
|
||||
"ip": "10.0.2.0/24",
|
||||
"dev": "enp0s3",
|
||||
"proto": "kernel",
|
||||
"scope": "link",
|
||||
"src": "10.0.2.15",
|
||||
"metric": 100
|
||||
}
|
||||
]
|
||||
|
||||
<a id="jc.parsers.ip_route.parse"></a>
|
||||
|
||||
### parse
|
||||
|
||||
```python
|
||||
def parse(data, raw=False, quiet=False)
|
||||
```
|
||||
|
||||
Main text parsing function
|
||||
|
||||
Parameters:
|
||||
|
||||
data: (string) text data to parse
|
||||
raw: (boolean) unprocessed output if True
|
||||
quiet: (boolean) suppress warning messages if True
|
||||
|
||||
Returns:
|
||||
|
||||
List of Json objects if data is processed and Raw data if raw = true.
|
||||
|
||||
### Parser Information
|
||||
Compatibility: linux
|
||||
|
||||
Version 1.0 by Julian Jackson (jackson.julian55@yahoo.com)
|
||||
@@ -108,4 +108,4 @@ Returns:
|
||||
### Parser Information
|
||||
Compatibility: linux
|
||||
|
||||
Version 1.0 by Thomas Vincent (vrince@gmail.com)
|
||||
Version 1.1 by Thomas Vincent (vrince@gmail.com)
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
jc - JSON Convert `last` and `lastb` command output parser
|
||||
|
||||
Supports `-w` and `-F` options.
|
||||
Supports `-w`, `-F`, and `-x` options.
|
||||
|
||||
Calculated epoch time fields are naive (i.e. based on the local time of the
|
||||
system the parser is run on) since there is no timezone information in the
|
||||
@@ -127,4 +127,4 @@ Returns:
|
||||
### Parser Information
|
||||
Compatibility: linux, darwin, aix, freebsd
|
||||
|
||||
Version 1.8 by Kelly Brazil (kellyjonbrazil@gmail.com)
|
||||
Version 1.9 by Kelly Brazil (kellyjonbrazil@gmail.com)
|
||||
|
||||
89
docs/parsers/lsattr.md
Normal file
89
docs/parsers/lsattr.md
Normal file
@@ -0,0 +1,89 @@
|
||||
[Home](https://kellyjonbrazil.github.io/jc/)
|
||||
<a id="jc.parsers.lsattr"></a>
|
||||
|
||||
# jc.parsers.lsattr
|
||||
|
||||
jc - JSON Convert `lsattr` command output parser
|
||||
|
||||
Usage (cli):
|
||||
|
||||
$ lsattr | jc --lsattr
|
||||
|
||||
or
|
||||
|
||||
$ jc lsattr
|
||||
|
||||
Usage (module):
|
||||
|
||||
import jc
|
||||
result = jc.parse('lsattr', lsattr_command_output)
|
||||
|
||||
Schema:
|
||||
|
||||
Information from https://github.com/mirror/busybox/blob/2d4a3d9e6c1493a9520b907e07a41aca90cdfd94/e2fsprogs/e2fs_lib.c#L40
|
||||
used to define field names
|
||||
|
||||
[
|
||||
{
|
||||
"file": string,
|
||||
"compressed_file": Optional[boolean],
|
||||
"compressed_dirty_file": Optional[boolean],
|
||||
"compression_raw_access": Optional[boolean],
|
||||
"secure_deletion": Optional[boolean],
|
||||
"undelete": Optional[boolean],
|
||||
"synchronous_updates": Optional[boolean],
|
||||
"synchronous_directory_updates": Optional[boolean],
|
||||
"immutable": Optional[boolean],
|
||||
"append_only": Optional[boolean],
|
||||
"no_dump": Optional[boolean],
|
||||
"no_atime": Optional[boolean],
|
||||
"compression_requested": Optional[boolean],
|
||||
"encrypted": Optional[boolean],
|
||||
"journaled_data": Optional[boolean],
|
||||
"indexed_directory": Optional[boolean],
|
||||
"no_tailmerging": Optional[boolean],
|
||||
"top_of_directory_hierarchies": Optional[boolean],
|
||||
"extents": Optional[boolean],
|
||||
"no_cow": Optional[boolean],
|
||||
"casefold": Optional[boolean],
|
||||
"inline_data": Optional[boolean],
|
||||
"project_hierarchy": Optional[boolean],
|
||||
"verity": Optional[boolean],
|
||||
}
|
||||
]
|
||||
|
||||
Examples:
|
||||
|
||||
$ sudo lsattr /etc/passwd | jc --lsattr
|
||||
[
|
||||
{
|
||||
"file": "/etc/passwd",
|
||||
"extents": true
|
||||
}
|
||||
]
|
||||
|
||||
<a id="jc.parsers.lsattr.parse"></a>
|
||||
|
||||
### parse
|
||||
|
||||
```python
|
||||
def parse(data: str,
|
||||
raw: bool = False,
|
||||
quiet: bool = False) -> List[JSONDictType]
|
||||
```
|
||||
|
||||
Main text parsing function
|
||||
|
||||
Parameters:
|
||||
|
||||
data: (string) text data to parse
|
||||
quiet: (boolean) suppress warning messages if True
|
||||
|
||||
Returns:
|
||||
|
||||
List of Dictionaries. Raw or processed structured data.
|
||||
|
||||
### Parser Information
|
||||
Compatibility: linux
|
||||
|
||||
Version 1.0 by Mark Rotner (rotner.mr@gmail.com)
|
||||
61
docs/parsers/lsb_release.md
Normal file
61
docs/parsers/lsb_release.md
Normal file
@@ -0,0 +1,61 @@
|
||||
[Home](https://kellyjonbrazil.github.io/jc/)
|
||||
<a id="jc.parsers.lsb_release"></a>
|
||||
|
||||
# jc.parsers.lsb\_release
|
||||
|
||||
jc - JSON Convert `lsb_release` command parser
|
||||
|
||||
This parser is an alias to the Key/Value parser (`--kv`).
|
||||
|
||||
Usage (cli):
|
||||
|
||||
$ lsb_release -a | jc --lsb-release
|
||||
|
||||
or
|
||||
$ jc lsb_release -a
|
||||
|
||||
Usage (module):
|
||||
|
||||
import jc
|
||||
result = jc.parse('lsb_release', lsb_release_command_output)
|
||||
|
||||
Schema:
|
||||
|
||||
{
|
||||
"<key>": string
|
||||
}
|
||||
|
||||
Examples:
|
||||
|
||||
$ lsb_release -a | jc --lsb-release -p
|
||||
{
|
||||
"Distributor ID": "Ubuntu",
|
||||
"Description": "Ubuntu 16.04.6 LTS",
|
||||
"Release": "16.04",
|
||||
"Codename": "xenial"
|
||||
}
|
||||
|
||||
<a id="jc.parsers.lsb_release.parse"></a>
|
||||
|
||||
### parse
|
||||
|
||||
```python
|
||||
def parse(data: str, raw: bool = False, quiet: bool = False) -> JSONDictType
|
||||
```
|
||||
|
||||
Main text parsing function
|
||||
|
||||
Parameters:
|
||||
|
||||
data: (string) text data to parse
|
||||
raw: (boolean) unprocessed output if True
|
||||
quiet: (boolean) suppress warning messages if True
|
||||
|
||||
Returns:
|
||||
|
||||
Dictionary. Raw or processed structured data.
|
||||
|
||||
### Parser Information
|
||||
Compatibility: linux, darwin, cygwin, win32, aix, freebsd
|
||||
|
||||
Version 1.0 by Kelly Brazil (kellyjonbrazil@gmail.com)
|
||||
@@ -102,6 +102,24 @@ Schema:
|
||||
]
|
||||
}
|
||||
},
|
||||
"cdc_mbim": {
|
||||
"<item>": {
|
||||
"value": string,
|
||||
"description": string,
|
||||
"attributes": [
|
||||
string
|
||||
]
|
||||
}
|
||||
},
|
||||
"cdc_mbim_extended": {
|
||||
"<item>": {
|
||||
"value": string,
|
||||
"description": string,
|
||||
"attributes": [
|
||||
string
|
||||
]
|
||||
}
|
||||
},
|
||||
"videocontrol_descriptors": [
|
||||
{
|
||||
"<item>": {
|
||||
@@ -312,4 +330,4 @@ Returns:
|
||||
### Parser Information
|
||||
Compatibility: linux
|
||||
|
||||
Version 1.3 by Kelly Brazil (kellyjonbrazil@gmail.com)
|
||||
Version 1.4 by Kelly Brazil (kellyjonbrazil@gmail.com)
|
||||
|
||||
@@ -376,6 +376,6 @@ Returns:
|
||||
List of Dictionaries. Raw or processed structured data.
|
||||
|
||||
### Parser Information
|
||||
Compatibility: linux, darwin, freebsd
|
||||
Compatibility: linux, darwin, freebsd, win32
|
||||
|
||||
Version 1.13 by Kelly Brazil (kellyjonbrazil@gmail.com)
|
||||
Version 1.15 by Kelly Brazil (kellyjonbrazil@gmail.com)
|
||||
|
||||
90
docs/parsers/nsd_control.md
Normal file
90
docs/parsers/nsd_control.md
Normal file
@@ -0,0 +1,90 @@
|
||||
[Home](https://kellyjonbrazil.github.io/jc/)
|
||||
<a id="jc.parsers.nsd_control"></a>
|
||||
|
||||
# jc.parsers.nsd\_control
|
||||
|
||||
jc - JSON Convert `nsd-control` command output parser
|
||||
|
||||
Usage (cli):
|
||||
|
||||
$ nsd-control | jc --nsd-control
|
||||
|
||||
or
|
||||
|
||||
$ jc nsd-control
|
||||
|
||||
Usage (module):
|
||||
|
||||
import jc
|
||||
result = jc.parse('nsd_control', nsd_control_command_output)
|
||||
|
||||
Schema:
|
||||
|
||||
[
|
||||
{
|
||||
"version": string,
|
||||
"verbosity": integer,
|
||||
"ratelimit": integer
|
||||
}
|
||||
]
|
||||
|
||||
[
|
||||
{
|
||||
"zone": string
|
||||
"status": {
|
||||
"state": string,
|
||||
"served-serial": string,
|
||||
"commit-serial": string,
|
||||
"wait": string
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
Examples:
|
||||
|
||||
$ nsd-control | jc --nsd-control status
|
||||
[
|
||||
{
|
||||
"version": "4.6.2",
|
||||
"verbosity": "2",
|
||||
"ratelimit": "0"
|
||||
}
|
||||
]
|
||||
|
||||
$ nsd-control | jc --nsd-control zonestatus sunet.se
|
||||
[
|
||||
{
|
||||
"zone": "sunet.se",
|
||||
"status": {
|
||||
"state": "ok",
|
||||
"served-serial": "2023090704 since 2023-09-07T16:34:27",
|
||||
"commit-serial": "2023090704 since 2023-09-07T16:34:27",
|
||||
"wait": "28684 sec between attempts"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
<a id="jc.parsers.nsd_control.parse"></a>
|
||||
|
||||
### parse
|
||||
|
||||
```python
|
||||
def parse(data: str, raw: bool = False, quiet: bool = False)
|
||||
```
|
||||
|
||||
Main text parsing function
|
||||
|
||||
Parameters:
|
||||
|
||||
data: (string) text data to parse
|
||||
raw: (boolean) unprocessed output if True
|
||||
quiet: (boolean) suppress warning messages if True
|
||||
|
||||
Returns:
|
||||
|
||||
List of Dictionaries. Raw or processed structured data.
|
||||
|
||||
### Parser Information
|
||||
Compatibility: linux, darwin, cygwin, win32, aix, freebsd
|
||||
|
||||
Version 1.0 by Pettai (pettai@sunet.se)
|
||||
86
docs/parsers/os_release.md
Normal file
86
docs/parsers/os_release.md
Normal file
@@ -0,0 +1,86 @@
|
||||
[Home](https://kellyjonbrazil.github.io/jc/)
|
||||
<a id="jc.parsers.os_release"></a>
|
||||
|
||||
# jc.parsers.os\_release
|
||||
|
||||
jc - JSON Convert `/etc/os-release` file parser
|
||||
|
||||
This parser is an alias to the Key/Value parser (`--kv`).
|
||||
|
||||
Usage (cli):
|
||||
|
||||
$ cat /etc/os-release | jc --os-release
|
||||
|
||||
Usage (module):
|
||||
|
||||
import jc
|
||||
result = jc.parse('os_release', os_release_output)
|
||||
|
||||
Schema:
|
||||
|
||||
{
|
||||
"<key>": string
|
||||
}
|
||||
|
||||
Examples:
|
||||
|
||||
$ cat /etc/os-release | jc --os-release -p
|
||||
{
|
||||
"NAME": "CentOS Linux",
|
||||
"VERSION": "7 (Core)",
|
||||
"ID": "centos",
|
||||
"ID_LIKE": "rhel fedora",
|
||||
"VERSION_ID": "7",
|
||||
"PRETTY_NAME": "CentOS Linux 7 (Core)",
|
||||
"ANSI_COLOR": "0;31",
|
||||
"CPE_NAME": "cpe:/o:centos:centos:7",
|
||||
"HOME_URL": "https://www.centos.org/",
|
||||
"BUG_REPORT_URL": "https://bugs.centos.org/",
|
||||
"CENTOS_MANTISBT_PROJECT": "CentOS-7",
|
||||
"CENTOS_MANTISBT_PROJECT_VERSION": "7",
|
||||
"REDHAT_SUPPORT_PRODUCT": "centos",
|
||||
"REDHAT_SUPPORT_PRODUCT_VERSION": "7"
|
||||
}
|
||||
|
||||
$ cat /etc/os-release | jc --os-release -p -r
|
||||
{
|
||||
"NAME": "\\"CentOS Linux\\"",
|
||||
"VERSION": "\\"7 (Core)\\"",
|
||||
"ID": "\\"centos\\"",
|
||||
"ID_LIKE": "\\"rhel fedora\\"",
|
||||
"VERSION_ID": "\\"7\\"",
|
||||
"PRETTY_NAME": "\\"CentOS Linux 7 (Core)\\"",
|
||||
"ANSI_COLOR": "\\"0;31\\"",
|
||||
"CPE_NAME": "\\"cpe:/o:centos:centos:7\\"",
|
||||
"HOME_URL": "\\"https://www.centos.org/\\"",
|
||||
"BUG_REPORT_URL": "\\"https://bugs.centos.org/\\"",
|
||||
"CENTOS_MANTISBT_PROJECT": "\\"CentOS-7\\"",
|
||||
"CENTOS_MANTISBT_PROJECT_VERSION": "\\"7\\"",
|
||||
"REDHAT_SUPPORT_PRODUCT": "\\"centos\\"",
|
||||
"REDHAT_SUPPORT_PRODUCT_VERSION": "\\"7\\""
|
||||
}
|
||||
|
||||
<a id="jc.parsers.os_release.parse"></a>
|
||||
|
||||
### parse
|
||||
|
||||
```python
|
||||
def parse(data: str, raw: bool = False, quiet: bool = False) -> JSONDictType
|
||||
```
|
||||
|
||||
Main text parsing function
|
||||
|
||||
Parameters:
|
||||
|
||||
data: (string) text data to parse
|
||||
raw: (boolean) unprocessed output if True
|
||||
quiet: (boolean) suppress warning messages if True
|
||||
|
||||
Returns:
|
||||
|
||||
Dictionary. Raw or processed structured data.
|
||||
|
||||
### Parser Information
|
||||
Compatibility: linux, darwin, cygwin, win32, aix, freebsd
|
||||
|
||||
Version 1.0 by Kelly Brazil (kellyjonbrazil@gmail.com)
|
||||
@@ -45,6 +45,9 @@ Schema:
|
||||
"kb_ccwr_s": float,
|
||||
"cswch_s": float,
|
||||
"nvcswch_s": float,
|
||||
"usr_ms": integer,
|
||||
"system_ms": integer,
|
||||
"guest_ms": integer,
|
||||
"command": string
|
||||
}
|
||||
]
|
||||
@@ -148,4 +151,4 @@ Returns:
|
||||
### Parser Information
|
||||
Compatibility: linux
|
||||
|
||||
Version 1.1 by Kelly Brazil (kellyjonbrazil@gmail.com)
|
||||
Version 1.3 by Kelly Brazil (kellyjonbrazil@gmail.com)
|
||||
|
||||
@@ -39,6 +39,7 @@ Schema:
|
||||
"percent_usr": float,
|
||||
"percent_system": float,
|
||||
"percent_guest": float,
|
||||
"percent_wait": float,
|
||||
"percent_cpu": float,
|
||||
"cpu": integer,
|
||||
"minflt_s": float,
|
||||
@@ -53,6 +54,9 @@ Schema:
|
||||
"kb_ccwr_s": float,
|
||||
"cswch_s": float,
|
||||
"nvcswch_s": float,
|
||||
"usr_ms": integer,
|
||||
"system_ms": integer,
|
||||
"guest_ms": integer,
|
||||
"command": string,
|
||||
|
||||
# below object only exists if using -qq or ignore_exceptions=True
|
||||
@@ -107,4 +111,4 @@ Returns:
|
||||
### Parser Information
|
||||
Compatibility: linux
|
||||
|
||||
Version 1.1 by Kelly Brazil (kellyjonbrazil@gmail.com)
|
||||
Version 1.2 by Kelly Brazil (kellyjonbrazil@gmail.com)
|
||||
|
||||
@@ -35,6 +35,8 @@ Schema:
|
||||
"packets_received": integer,
|
||||
"packet_loss_percent": float,
|
||||
"duplicates": integer,
|
||||
"errors": integer,
|
||||
"corrupted": integer,
|
||||
"round_trip_ms_min": float,
|
||||
"round_trip_ms_avg": float,
|
||||
"round_trip_ms_max": float,
|
||||
@@ -185,4 +187,4 @@ Returns:
|
||||
### Parser Information
|
||||
Compatibility: linux, darwin, freebsd
|
||||
|
||||
Version 1.8 by Kelly Brazil (kellyjonbrazil@gmail.com)
|
||||
Version 1.10 by Kelly Brazil (kellyjonbrazil@gmail.com)
|
||||
|
||||
@@ -36,7 +36,7 @@ Schema:
|
||||
"source_ip": string,
|
||||
"destination_ip": string,
|
||||
"sent_bytes": integer,
|
||||
"pattern": string, # (null if not set)
|
||||
"pattern": string, # null if not set
|
||||
"destination": string,
|
||||
"timestamp": float,
|
||||
"response_bytes": integer,
|
||||
@@ -49,10 +49,12 @@ Schema:
|
||||
"packets_received": integer,
|
||||
"packet_loss_percent": float,
|
||||
"duplicates": integer,
|
||||
"round_trip_ms_min": float,
|
||||
"round_trip_ms_avg": float,
|
||||
"round_trip_ms_max": float,
|
||||
"round_trip_ms_stddev": float,
|
||||
"errors": integer, # null if not set
|
||||
"corrupted": integer, # null if not set
|
||||
"round_trip_ms_min": float, # null if not set
|
||||
"round_trip_ms_avg": float, # null if not set
|
||||
"round_trip_ms_max": float, # null if not set
|
||||
"round_trip_ms_stddev": float, # null if not set
|
||||
|
||||
# below object only exists if using -qq or ignore_exceptions=True
|
||||
"_jc_meta": {
|
||||
@@ -106,4 +108,4 @@ Returns:
|
||||
### Parser Information
|
||||
Compatibility: linux, darwin, freebsd
|
||||
|
||||
Version 1.2 by Kelly Brazil (kellyjonbrazil@gmail.com)
|
||||
Version 1.4 by Kelly Brazil (kellyjonbrazil@gmail.com)
|
||||
|
||||
186
docs/parsers/proc_net_tcp.md
Normal file
186
docs/parsers/proc_net_tcp.md
Normal file
@@ -0,0 +1,186 @@
|
||||
[Home](https://kellyjonbrazil.github.io/jc/)
|
||||
<a id="jc.parsers.proc_net_tcp"></a>
|
||||
|
||||
# jc.parsers.proc\_net\_tcp
|
||||
|
||||
jc - JSON Convert `/proc/net/tcp` and `proc/net/tcp6` file parser
|
||||
|
||||
IPv4 and IPv6 addresses are converted to standard notation unless the raw
|
||||
(--raw) option is used.
|
||||
|
||||
Usage (cli):
|
||||
|
||||
$ cat /proc/net/tcp | jc --proc
|
||||
|
||||
or
|
||||
|
||||
$ jc /proc/net/tcp
|
||||
|
||||
or
|
||||
|
||||
$ cat /proc/net/tcp | jc --proc-net-tcp
|
||||
|
||||
Usage (module):
|
||||
|
||||
import jc
|
||||
result = jc.parse('proc', proc_net_tcp_file)
|
||||
|
||||
or
|
||||
|
||||
import jc
|
||||
result = jc.parse('proc_net_tcp', proc_net_tcp_file)
|
||||
|
||||
Schema:
|
||||
|
||||
Field names and types gathered from the following:
|
||||
|
||||
https://www.kernel.org/doc/Documentation/networking/proc_net_tcp.txt
|
||||
|
||||
https://github.com/torvalds/linux/blob/master/net/ipv4/tcp_ipv4.c
|
||||
|
||||
https://github.com/torvalds/linux/blob/master/net/ipv6/tcp_ipv6.c
|
||||
|
||||
[
|
||||
{
|
||||
"entry": integer,
|
||||
"local_address": string,
|
||||
"local_port": integer,
|
||||
"remote_address": string,
|
||||
"remote_port": integer,
|
||||
"state": string,
|
||||
"tx_queue": string,
|
||||
"rx_queue": string,
|
||||
"timer_active": integer,
|
||||
"jiffies_until_timer_expires": string,
|
||||
"unrecovered_rto_timeouts": string,
|
||||
"uid": integer,
|
||||
"unanswered_0_window_probes": integer,
|
||||
"inode": integer,
|
||||
"sock_ref_count": integer,
|
||||
"sock_mem_loc": string,
|
||||
"retransmit_timeout": integer,
|
||||
"soft_clock_tick": integer,
|
||||
"ack_quick_pingpong": integer,
|
||||
"sending_congestion_window": integer,
|
||||
"slow_start_size_threshold": integer
|
||||
}
|
||||
]
|
||||
|
||||
Examples:
|
||||
|
||||
$ cat /proc/net/tcp | jc --proc -p
|
||||
[
|
||||
{
|
||||
"entry": "0",
|
||||
"local_address": "10.0.0.28",
|
||||
"local_port": 42082,
|
||||
"remote_address": "64.12.0.108",
|
||||
"remote_port": 80,
|
||||
"state": "04",
|
||||
"tx_queue": "00000001",
|
||||
"rx_queue": "00000000",
|
||||
"timer_active": 1,
|
||||
"jiffies_until_timer_expires": "00000015",
|
||||
"unrecovered_rto_timeouts": "00000000",
|
||||
"uid": 0,
|
||||
"unanswered_0_window_probes": 0,
|
||||
"inode": 0,
|
||||
"sock_ref_count": 3,
|
||||
"sock_mem_loc": "ffff8c7a0de930c0",
|
||||
"retransmit_timeout": 21,
|
||||
"soft_clock_tick": 4,
|
||||
"ack_quick_pingpong": 30,
|
||||
"sending_congestion_window": 10,
|
||||
"slow_start_size_threshold": -1
|
||||
},
|
||||
{
|
||||
"entry": "1",
|
||||
"local_address": "10.0.0.28",
|
||||
"local_port": 38864,
|
||||
"remote_address": "104.244.42.65",
|
||||
"remote_port": 80,
|
||||
"state": "06",
|
||||
"tx_queue": "00000000",
|
||||
"rx_queue": "00000000",
|
||||
"timer_active": 3,
|
||||
"jiffies_until_timer_expires": "000007C5",
|
||||
"unrecovered_rto_timeouts": "00000000",
|
||||
"uid": 0,
|
||||
"unanswered_0_window_probes": 0,
|
||||
"inode": 0,
|
||||
"sock_ref_count": 3,
|
||||
"sock_mem_loc": "ffff8c7a12d31aa0"
|
||||
},
|
||||
...
|
||||
]
|
||||
|
||||
$ cat /proc/net/tcp | jc --proc -p -r
|
||||
[
|
||||
{
|
||||
"entry": "1",
|
||||
"local_address": "1C00000A",
|
||||
"local_port": "A462",
|
||||
"remote_address": "6C000C40",
|
||||
"remote_port": "0050",
|
||||
"state": "04",
|
||||
"tx_queue": "00000001",
|
||||
"rx_queue": "00000000",
|
||||
"timer_active": "01",
|
||||
"jiffies_until_timer_expires": "00000015",
|
||||
"unrecovered_rto_timeouts": "00000000",
|
||||
"uid": "0",
|
||||
"unanswered_0_window_probes": "0",
|
||||
"inode": "0",
|
||||
"sock_ref_count": "3",
|
||||
"sock_mem_loc": "ffff8c7a0de930c0",
|
||||
"retransmit_timeout": "21",
|
||||
"soft_clock_tick": "4",
|
||||
"ack_quick_pingpong": "30",
|
||||
"sending_congestion_window": "10",
|
||||
"slow_start_size_threshold": "-1"
|
||||
},
|
||||
{
|
||||
"entry": "2",
|
||||
"local_address": "1C00000A",
|
||||
"local_port": "97D0",
|
||||
"remote_address": "412AF468",
|
||||
"remote_port": "0050",
|
||||
"state": "06",
|
||||
"tx_queue": "00000000",
|
||||
"rx_queue": "00000000",
|
||||
"timer_active": "03",
|
||||
"jiffies_until_timer_expires": "000007C5",
|
||||
"unrecovered_rto_timeouts": "00000000",
|
||||
"uid": "0",
|
||||
"unanswered_0_window_probes": "0",
|
||||
"inode": "0",
|
||||
"sock_ref_count": "3",
|
||||
"sock_mem_loc": "ffff8c7a12d31aa0"
|
||||
},
|
||||
...
|
||||
]
|
||||
|
||||
<a id="jc.parsers.proc_net_tcp.parse"></a>
|
||||
|
||||
### parse
|
||||
|
||||
```python
|
||||
def parse(data: str, raw: bool = False, quiet: bool = False) -> List[Dict]
|
||||
```
|
||||
|
||||
Main text parsing function
|
||||
|
||||
Parameters:
|
||||
|
||||
data: (string) text data to parse
|
||||
raw: (boolean) unprocessed output if True
|
||||
quiet: (boolean) suppress warning messages if True
|
||||
|
||||
Returns:
|
||||
|
||||
List of Dictionaries. Raw or processed structured data.
|
||||
|
||||
### Parser Information
|
||||
Compatibility: linux
|
||||
|
||||
Version 1.0 by Alvin Solomon (alvinms01@gmail.com)
|
||||
@@ -114,7 +114,8 @@ Examples:
|
||||
"mw",
|
||||
"me",
|
||||
"dw",
|
||||
"sd"
|
||||
"sd",
|
||||
"mp"
|
||||
],
|
||||
"VmFlags_pretty": [
|
||||
"readable",
|
||||
|
||||
83
docs/parsers/resolve_conf.md
Normal file
83
docs/parsers/resolve_conf.md
Normal file
@@ -0,0 +1,83 @@
|
||||
[Home](https://kellyjonbrazil.github.io/jc/)
|
||||
<a id="jc.parsers.resolve_conf"></a>
|
||||
|
||||
# jc.parsers.resolve\_conf
|
||||
|
||||
jc - JSON Convert `/etc/resolve.conf` file parser
|
||||
|
||||
This parser may be more forgiving than the system parser. For example, if
|
||||
multiple `search` lists are defined, this parser will append all entries to
|
||||
the `search` field, while the system parser may only use the list from the
|
||||
last defined instance.
|
||||
|
||||
Usage (cli):
|
||||
|
||||
$ cat /etc/resolve.conf | jc --resolve-conf
|
||||
|
||||
Usage (module):
|
||||
|
||||
import jc
|
||||
result = jc.parse('resolve_conf', resolve_conf_output)
|
||||
|
||||
Schema:
|
||||
|
||||
{
|
||||
"domain": string,
|
||||
"search": [
|
||||
string
|
||||
],
|
||||
"nameservers": [
|
||||
string
|
||||
],
|
||||
"options": [
|
||||
string
|
||||
],
|
||||
"sortlist": [
|
||||
string
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
Examples:
|
||||
|
||||
$ cat /etc/resolve.conf | jc --resolve-conf -p
|
||||
{
|
||||
"search": [
|
||||
"eng.myprime.com",
|
||||
"dev.eng.myprime.com",
|
||||
"labs.myprime.com",
|
||||
"qa.myprime.com"
|
||||
],
|
||||
"nameservers": [
|
||||
"10.136.17.15"
|
||||
],
|
||||
"options": [
|
||||
"rotate",
|
||||
"ndots:1"
|
||||
]
|
||||
}
|
||||
|
||||
<a id="jc.parsers.resolve_conf.parse"></a>
|
||||
|
||||
### parse
|
||||
|
||||
```python
|
||||
def parse(data: str, raw: bool = False, quiet: bool = False) -> JSONDictType
|
||||
```
|
||||
|
||||
Main text parsing function
|
||||
|
||||
Parameters:
|
||||
|
||||
data: (string) text data to parse
|
||||
raw: (boolean) unprocessed output if True
|
||||
quiet: (boolean) suppress warning messages if True
|
||||
|
||||
Returns:
|
||||
|
||||
Dictionary. Raw or processed structured data.
|
||||
|
||||
### Parser Information
|
||||
Compatibility: linux, darwin, cygwin, win32, aix, freebsd
|
||||
|
||||
Version 1.0 by Kelly Brazil (kellyjonbrazil@gmail.com)
|
||||
@@ -22,6 +22,13 @@ Schema:
|
||||
|
||||
[
|
||||
{
|
||||
"interfaces": [
|
||||
{
|
||||
"id": string,
|
||||
"mac": string,
|
||||
"name": string,
|
||||
}
|
||||
]
|
||||
"destination": string,
|
||||
"gateway": string,
|
||||
"genmask": string,
|
||||
@@ -129,6 +136,6 @@ Returns:
|
||||
List of Dictionaries. Raw or processed structured data.
|
||||
|
||||
### Parser Information
|
||||
Compatibility: linux
|
||||
Compatibility: linux, win32
|
||||
|
||||
Version 1.8 by Kelly Brazil (kellyjonbrazil@gmail.com)
|
||||
Version 1.9 by Kelly Brazil (kellyjonbrazil@gmail.com)
|
||||
|
||||
@@ -7,6 +7,8 @@ jc - JSON Convert Semantic Version string parser
|
||||
|
||||
This parser conforms to the specification at https://semver.org/
|
||||
|
||||
See Also: `ver` parser.
|
||||
|
||||
Usage (cli):
|
||||
|
||||
$ echo 1.2.3-rc.1+44837 | jc --semver
|
||||
|
||||
136
docs/parsers/srt.md
Normal file
136
docs/parsers/srt.md
Normal file
@@ -0,0 +1,136 @@
|
||||
[Home](https://kellyjonbrazil.github.io/jc/)
|
||||
<a id="jc.parsers.srt"></a>
|
||||
|
||||
# jc.parsers.srt
|
||||
|
||||
jc - JSON Convert `SRT` file parser
|
||||
|
||||
Usage (cli):
|
||||
|
||||
$ cat foo.srt | jc --srt
|
||||
|
||||
Usage (module):
|
||||
|
||||
import jc
|
||||
result = jc.parse('srt', srt_file_output)
|
||||
|
||||
Schema:
|
||||
|
||||
[
|
||||
{
|
||||
"index": int,
|
||||
"start": {
|
||||
"hours": int,
|
||||
"minutes": int,
|
||||
"seconds": int,
|
||||
"milliseconds": int,
|
||||
"timestamp": string
|
||||
},
|
||||
"end": {
|
||||
"hours": int,
|
||||
"minutes": int,
|
||||
"seconds": int,
|
||||
"milliseconds": int,
|
||||
"timestamp": string
|
||||
},
|
||||
"content": string
|
||||
}
|
||||
]
|
||||
|
||||
Examples:
|
||||
|
||||
$ cat attack_of_the_clones.srt
|
||||
1
|
||||
00:02:16,612 --> 00:02:19,376
|
||||
Senator, we're making
|
||||
our final approach into Coruscant.
|
||||
|
||||
2
|
||||
00:02:19,482 --> 00:02:21,609
|
||||
Very good, Lieutenant.
|
||||
...
|
||||
|
||||
$ cat attack_of_the_clones.srt | jc --srt
|
||||
[
|
||||
{
|
||||
"index": 1,
|
||||
"start": {
|
||||
"hours": 0,
|
||||
"minutes": 2,
|
||||
"seconds": 16,
|
||||
"milliseconds": 612,
|
||||
"timestamp": "00:02:16,612"
|
||||
},
|
||||
"end": {
|
||||
"hours": 0,
|
||||
"minutes": 2,
|
||||
"seconds": 19,
|
||||
"milliseconds": 376,
|
||||
"timestamp": "00:02:19,376"
|
||||
},
|
||||
"content": "Senator, we're making\nour final approach into Coruscant."
|
||||
},
|
||||
{
|
||||
"index": 2,
|
||||
"start": {
|
||||
"hours": 0,
|
||||
"minutes": 2,
|
||||
"seconds": 19,
|
||||
"milliseconds": 482,
|
||||
"timestamp": "00:02:19,482"
|
||||
},
|
||||
"end": {
|
||||
"hours": 0,
|
||||
"minutes": 2,
|
||||
"seconds": 21,
|
||||
"milliseconds": 609,
|
||||
"timestamp": "00:02:21,609"
|
||||
},
|
||||
"content": "Very good, Lieutenant."
|
||||
},
|
||||
...
|
||||
]
|
||||
|
||||
<a id="jc.parsers.srt.parse_timestamp"></a>
|
||||
|
||||
### parse\_timestamp
|
||||
|
||||
```python
|
||||
def parse_timestamp(timestamp: str) -> Dict
|
||||
```
|
||||
|
||||
timestamp: "hours:minutes:seconds,milliseconds" --->
|
||||
{
|
||||
"hours": "hours",
|
||||
"minutes": "minutes",
|
||||
"seconds": "seconds",
|
||||
"milliseconds": "milliseconds",
|
||||
"timestamp": "hours:minutes:seconds,milliseconds"
|
||||
}
|
||||
|
||||
<a id="jc.parsers.srt.parse"></a>
|
||||
|
||||
### parse
|
||||
|
||||
```python
|
||||
def parse(data: str,
|
||||
raw: bool = False,
|
||||
quiet: bool = False) -> List[JSONDictType]
|
||||
```
|
||||
|
||||
Main text parsing function
|
||||
|
||||
Parameters:
|
||||
|
||||
data: (string) text data to parse
|
||||
raw: (boolean) unprocessed output if True
|
||||
quiet: (boolean) suppress warning messages if True
|
||||
|
||||
Returns:
|
||||
|
||||
Dictionary. Raw or processed structured data.
|
||||
|
||||
### Parser Information
|
||||
Compatibility: linux, darwin, cygwin, win32, aix, freebsd
|
||||
|
||||
Version 1.0 by Mark Rotner (rotner.mr@gmail.com)
|
||||
@@ -5,9 +5,6 @@
|
||||
|
||||
jc - JSON Convert `ss` command output parser
|
||||
|
||||
Extended information options like `-e` and `-p` are not supported and may
|
||||
cause parsing irregularities.
|
||||
|
||||
Usage (cli):
|
||||
|
||||
$ ss | jc --ss
|
||||
@@ -28,21 +25,29 @@ field names
|
||||
|
||||
[
|
||||
{
|
||||
"netid": string,
|
||||
"state": string,
|
||||
"recv_q": integer,
|
||||
"send_q": integer,
|
||||
"local_address": string,
|
||||
"local_port": string,
|
||||
"local_port_num": integer,
|
||||
"peer_address": string,
|
||||
"peer_port": string,
|
||||
"peer_port_num": integer,
|
||||
"interface": string,
|
||||
"link_layer" string,
|
||||
"channel": string,
|
||||
"path": string,
|
||||
"pid": integer
|
||||
"netid": string,
|
||||
"state": string,
|
||||
"recv_q": integer,
|
||||
"send_q": integer,
|
||||
"local_address": string,
|
||||
"local_port": string,
|
||||
"local_port_num": integer,
|
||||
"peer_address": string,
|
||||
"peer_port": string,
|
||||
"peer_port_num": integer,
|
||||
"interface": string,
|
||||
"link_layer" string,
|
||||
"channel": string,
|
||||
"path": string,
|
||||
"pid": integer,
|
||||
"opts": {
|
||||
"process_id": {
|
||||
"<process_id>": {
|
||||
"user": string,
|
||||
"file_descriptor": string
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
@@ -303,4 +308,4 @@ Returns:
|
||||
### Parser Information
|
||||
Compatibility: linux
|
||||
|
||||
Version 1.6 by Kelly Brazil (kellyjonbrazil@gmail.com)
|
||||
Version 1.7 by Kelly Brazil (kellyjonbrazil@gmail.com)
|
||||
|
||||
548
docs/parsers/ssh_conf.md
Normal file
548
docs/parsers/ssh_conf.md
Normal file
@@ -0,0 +1,548 @@
|
||||
[Home](https://kellyjonbrazil.github.io/jc/)
|
||||
<a id="jc.parsers.ssh_conf"></a>
|
||||
|
||||
# jc.parsers.ssh\_conf
|
||||
|
||||
jc - JSON Convert `ssh` configuration file and `ssh -G` command output parser
|
||||
|
||||
This parser will work with `ssh` configuration files or the output of
|
||||
`ssh -G`. Any `Match` blocks in the `ssh` configuration file will be
|
||||
ignored.
|
||||
|
||||
Usage (cli):
|
||||
|
||||
$ ssh -G hostname | jc --ssh-conf
|
||||
|
||||
or
|
||||
|
||||
$ jc ssh -G hostname
|
||||
|
||||
or
|
||||
|
||||
$ cat ~/.ssh/config | jc --ssh-conf
|
||||
|
||||
Usage (module):
|
||||
|
||||
import jc
|
||||
result = jc.parse('ssh_conf', ssh_conf_output)
|
||||
|
||||
Schema:
|
||||
|
||||
[
|
||||
{
|
||||
"host": string,
|
||||
"host_list": [
|
||||
string
|
||||
],
|
||||
"addkeystoagent": string,
|
||||
"addressfamily": string,
|
||||
"batchmode": string,
|
||||
"bindaddress": string,
|
||||
"bindinterface": string,
|
||||
"canonicaldomains": [
|
||||
string
|
||||
],
|
||||
"canonicalizefallbacklocal": string,
|
||||
"canonicalizehostname": string,
|
||||
"canonicalizemaxdots": integer,
|
||||
"canonicalizepermittedcnames": [
|
||||
string
|
||||
],
|
||||
"casignaturealgorithms": [
|
||||
string
|
||||
],
|
||||
"certificatefile": [
|
||||
string
|
||||
],
|
||||
"checkhostip": string,
|
||||
"ciphers": [
|
||||
string
|
||||
],
|
||||
"clearallforwardings": string,
|
||||
"compression": string,
|
||||
"connectionattempts": integer,
|
||||
"connecttimeout": integer,
|
||||
"controlmaster": string,
|
||||
"controlpath": string,
|
||||
"controlpersist": string,
|
||||
"dynamicforward": string,
|
||||
"enableescapecommandline": string,
|
||||
"enablesshkeysign": string,
|
||||
"escapechar": string,
|
||||
"exitonforwardfailure": string,
|
||||
"fingerprinthash": string,
|
||||
"forkafterauthentication": string,
|
||||
"forwardagent": string,
|
||||
"forwardx11": string,
|
||||
"forwardx11timeout": integer,
|
||||
"forwardx11trusted": string,
|
||||
"gatewayports": string,
|
||||
"globalknownhostsfile": [
|
||||
string
|
||||
],
|
||||
"gssapiauthentication": string,
|
||||
"gssapidelegatecredentials": string,
|
||||
"hashknownhosts": string,
|
||||
"hostbasedacceptedalgorithms": [
|
||||
string
|
||||
],
|
||||
"hostbasedauthentication": string,
|
||||
"hostkeyalgorithms": [
|
||||
string
|
||||
],
|
||||
"hostkeyalias": string,
|
||||
"hostname": string,
|
||||
"identitiesonly": string,
|
||||
"identityagent": string,
|
||||
"identityfile": [
|
||||
string
|
||||
],
|
||||
"ignoreunknown": string,
|
||||
"include": [
|
||||
string
|
||||
],
|
||||
"ipqos": [
|
||||
string
|
||||
],
|
||||
"kbdinteractiveauthentication": string,
|
||||
"kbdinteractivedevices": [
|
||||
string
|
||||
],
|
||||
"kexalgorithms": [
|
||||
string
|
||||
],
|
||||
"kexalgorithms_strategy": string,
|
||||
"knownhostscommand": string,
|
||||
"localcommand": string,
|
||||
"localforward": [
|
||||
string
|
||||
],
|
||||
"loglevel": string,
|
||||
"logverbose": [
|
||||
string
|
||||
],
|
||||
"macs": [
|
||||
string
|
||||
],
|
||||
"macs_strategy": string,
|
||||
"nohostauthenticationforlocalhost": string,
|
||||
"numberofpasswordprompts": integer,
|
||||
"passwordauthentication": string,
|
||||
"permitlocalcommand": string,
|
||||
"permitremoteopen": [
|
||||
string
|
||||
],
|
||||
"pkcs11provider": string,
|
||||
"port": integer,
|
||||
"preferredauthentications": [
|
||||
string
|
||||
],
|
||||
"protocol": integer,
|
||||
"proxycommand": string,
|
||||
"proxyjump": [
|
||||
string
|
||||
],
|
||||
"proxyusefdpass": string,
|
||||
"pubkeyacceptedalgorithms": [
|
||||
string
|
||||
],
|
||||
"pubkeyacceptedalgorithms_strategy": string,
|
||||
"pubkeyauthentication": string,
|
||||
"rekeylimit": string,
|
||||
"remotecommand": string,
|
||||
"remoteforward": string,
|
||||
"requesttty": string,
|
||||
"requiredrsasize": integer,
|
||||
"revokedhostkeys": string,
|
||||
"securitykeyprovider": string,
|
||||
"sendenv": [
|
||||
string
|
||||
],
|
||||
"serveralivecountmax": integer,
|
||||
"serveraliveinterval": integer,
|
||||
"sessiontype": string,
|
||||
"setenv": [
|
||||
string
|
||||
],
|
||||
"stdinnull": string,
|
||||
"streamlocalbindmask": string,
|
||||
"streamlocalbindunlink": string,
|
||||
"stricthostkeychecking": string,
|
||||
"syslogfacility": string,
|
||||
"tcpkeepalive": string,
|
||||
"tunnel": string,
|
||||
"tunneldevice": string,
|
||||
"updatehostkeys": string,
|
||||
"user": string,
|
||||
"userknownhostsfile": [
|
||||
string
|
||||
],
|
||||
"verifyhostkeydns": string,
|
||||
"visualhostkey": string,
|
||||
"xauthlocation": string
|
||||
}
|
||||
]
|
||||
|
||||
Examples:
|
||||
|
||||
$ ssh -G - | jc --ssh-conf -p
|
||||
[
|
||||
{
|
||||
"user": "foo",
|
||||
"hostname": "-",
|
||||
"port": 22,
|
||||
"addressfamily": "any",
|
||||
"batchmode": "no",
|
||||
"canonicalizefallbacklocal": "yes",
|
||||
"canonicalizehostname": "false",
|
||||
"checkhostip": "no",
|
||||
"compression": "no",
|
||||
"controlmaster": "false",
|
||||
"enablesshkeysign": "no",
|
||||
"clearallforwardings": "no",
|
||||
"exitonforwardfailure": "no",
|
||||
"fingerprinthash": "SHA256",
|
||||
"forwardx11": "no",
|
||||
"forwardx11trusted": "no",
|
||||
"gatewayports": "no",
|
||||
"gssapiauthentication": "no",
|
||||
"gssapidelegatecredentials": "no",
|
||||
"hashknownhosts": "no",
|
||||
"hostbasedauthentication": "no",
|
||||
"identitiesonly": "no",
|
||||
"kbdinteractiveauthentication": "yes",
|
||||
"nohostauthenticationforlocalhost": "no",
|
||||
"passwordauthentication": "yes",
|
||||
"permitlocalcommand": "no",
|
||||
"proxyusefdpass": "no",
|
||||
"pubkeyauthentication": "true",
|
||||
"requesttty": "auto",
|
||||
"sessiontype": "default",
|
||||
"stdinnull": "no",
|
||||
"forkafterauthentication": "no",
|
||||
"streamlocalbindunlink": "no",
|
||||
"stricthostkeychecking": "ask",
|
||||
"tcpkeepalive": "yes",
|
||||
"tunnel": "false",
|
||||
"verifyhostkeydns": "false",
|
||||
"visualhostkey": "no",
|
||||
"updatehostkeys": "true",
|
||||
"applemultipath": "no",
|
||||
"canonicalizemaxdots": 1,
|
||||
"connectionattempts": 1,
|
||||
"forwardx11timeout": 1200,
|
||||
"numberofpasswordprompts": 3,
|
||||
"serveralivecountmax": 3,
|
||||
"serveraliveinterval": 0,
|
||||
"ciphers": [
|
||||
"chacha20-poly1305@openssh.com",
|
||||
"aes128-ctr",
|
||||
"aes192-ctr",
|
||||
"aes256-ctr",
|
||||
"aes128-gcm@openssh.com",
|
||||
"aes256-gcm@openssh.com"
|
||||
],
|
||||
"hostkeyalgorithms": [
|
||||
"ssh-ed25519-cert-v01@openssh.com",
|
||||
"ecdsa-sha2-nistp256-cert-v01@openssh.com",
|
||||
"ecdsa-sha2-nistp384-cert-v01@openssh.com",
|
||||
"ecdsa-sha2-nistp521-cert-v01@openssh.com",
|
||||
"rsa-sha2-512-cert-v01@openssh.com",
|
||||
"rsa-sha2-256-cert-v01@openssh.com",
|
||||
"ssh-ed25519",
|
||||
"ecdsa-sha2-nistp256",
|
||||
"ecdsa-sha2-nistp384",
|
||||
"ecdsa-sha2-nistp521",
|
||||
"rsa-sha2-512",
|
||||
"rsa-sha2-256"
|
||||
],
|
||||
"hostbasedacceptedalgorithms": [
|
||||
"ssh-ed25519-cert-v01@openssh.com",
|
||||
"ecdsa-sha2-nistp256-cert-v01@openssh.com",
|
||||
"ecdsa-sha2-nistp384-cert-v01@openssh.com",
|
||||
"ecdsa-sha2-nistp521-cert-v01@openssh.com",
|
||||
"rsa-sha2-512-cert-v01@openssh.com",
|
||||
"rsa-sha2-256-cert-v01@openssh.com",
|
||||
"ssh-ed25519",
|
||||
"ecdsa-sha2-nistp256",
|
||||
"ecdsa-sha2-nistp384",
|
||||
"ecdsa-sha2-nistp521",
|
||||
"rsa-sha2-512",
|
||||
"rsa-sha2-256"
|
||||
],
|
||||
"kexalgorithms": [
|
||||
"sntrup761x25519-sha512@openssh.com",
|
||||
"curve25519-sha256",
|
||||
"curve25519-sha256@libssh.org",
|
||||
"ecdh-sha2-nistp256",
|
||||
"ecdh-sha2-nistp384",
|
||||
"ecdh-sha2-nistp521",
|
||||
"diffie-hellman-group-exchange-sha256",
|
||||
"diffie-hellman-group16-sha512",
|
||||
"diffie-hellman-group18-sha512",
|
||||
"diffie-hellman-group14-sha256"
|
||||
],
|
||||
"casignaturealgorithms": [
|
||||
"ssh-ed25519",
|
||||
"ecdsa-sha2-nistp256",
|
||||
"ecdsa-sha2-nistp384",
|
||||
"ecdsa-sha2-nistp521",
|
||||
"rsa-sha2-512",
|
||||
"rsa-sha2-256"
|
||||
],
|
||||
"loglevel": "INFO",
|
||||
"macs": [
|
||||
"umac-64-etm@openssh.com",
|
||||
"umac-128-etm@openssh.com",
|
||||
"hmac-sha2-256-etm@openssh.com",
|
||||
"hmac-sha2-512-etm@openssh.com",
|
||||
"hmac-sha1-etm@openssh.com",
|
||||
"umac-64@openssh.com",
|
||||
"umac-128@openssh.com",
|
||||
"hmac-sha2-256",
|
||||
"hmac-sha2-512",
|
||||
"hmac-sha1"
|
||||
],
|
||||
"securitykeyprovider": "$SSH_SK_PROVIDER",
|
||||
"pubkeyacceptedalgorithms": [
|
||||
"ssh-ed25519-cert-v01@openssh.com",
|
||||
"ecdsa-sha2-nistp256-cert-v01@openssh.com",
|
||||
"ecdsa-sha2-nistp384-cert-v01@openssh.com",
|
||||
"ecdsa-sha2-nistp521-cert-v01@openssh.com",
|
||||
"rsa-sha2-512-cert-v01@openssh.com",
|
||||
"rsa-sha2-256-cert-v01@openssh.com",
|
||||
"ssh-ed25519",
|
||||
"ecdsa-sha2-nistp256",
|
||||
"ecdsa-sha2-nistp384",
|
||||
"ecdsa-sha2-nistp521",
|
||||
"rsa-sha2-512",
|
||||
"rsa-sha2-256"
|
||||
],
|
||||
"xauthlocation": "/usr/X11R6/bin/xauth",
|
||||
"identityfile": [
|
||||
"~/.ssh/id_rsa",
|
||||
"~/.ssh/id_ecdsa",
|
||||
"~/.ssh/id_ecdsa_sk",
|
||||
"~/.ssh/id_ed25519",
|
||||
"~/.ssh/id_ed25519_sk",
|
||||
"~/.ssh/id_xmss",
|
||||
"~/.ssh/id_dsa"
|
||||
],
|
||||
"canonicaldomains": [
|
||||
"none"
|
||||
],
|
||||
"globalknownhostsfile": [
|
||||
"/etc/ssh/ssh_known_hosts",
|
||||
"/etc/ssh/ssh_known_hosts2"
|
||||
],
|
||||
"userknownhostsfile": [
|
||||
"/Users/foo/.ssh/known_hosts",
|
||||
"/Users/foo/.ssh/known_hosts2"
|
||||
],
|
||||
"sendenv": [
|
||||
"LANG",
|
||||
"LC_*"
|
||||
],
|
||||
"logverbose": [
|
||||
"none"
|
||||
],
|
||||
"permitremoteopen": [
|
||||
"any"
|
||||
],
|
||||
"addkeystoagent": "false",
|
||||
"forwardagent": "no",
|
||||
"connecttimeout": null,
|
||||
"tunneldevice": "any:any",
|
||||
"canonicalizepermittedcnames": [
|
||||
"none"
|
||||
],
|
||||
"controlpersist": "no",
|
||||
"escapechar": "~",
|
||||
"ipqos": [
|
||||
"af21",
|
||||
"cs1"
|
||||
],
|
||||
"rekeylimit": "0 0",
|
||||
"streamlocalbindmask": "0177",
|
||||
"syslogfacility": "USER"
|
||||
}
|
||||
]
|
||||
|
||||
$ cat ~/.ssh/config | jc --ssh-conf -p
|
||||
[
|
||||
{
|
||||
"host": "server1",
|
||||
"host_list": [
|
||||
"server1"
|
||||
],
|
||||
"hostname": "server1.cyberciti.biz",
|
||||
"user": "nixcraft",
|
||||
"port": 4242,
|
||||
"identityfile": [
|
||||
"/nfs/shared/users/nixcraft/keys/server1/id_rsa"
|
||||
]
|
||||
},
|
||||
{
|
||||
"host": "nas01",
|
||||
"host_list": [
|
||||
"nas01"
|
||||
],
|
||||
"hostname": "192.168.1.100",
|
||||
"user": "root",
|
||||
"identityfile": [
|
||||
"~/.ssh/nas01.key"
|
||||
]
|
||||
},
|
||||
{
|
||||
"host": "aws.apache",
|
||||
"host_list": [
|
||||
"aws.apache"
|
||||
],
|
||||
"hostname": "1.2.3.4",
|
||||
"user": "wwwdata",
|
||||
"identityfile": [
|
||||
"~/.ssh/aws.apache.key"
|
||||
]
|
||||
},
|
||||
{
|
||||
"host": "uk.gw.lan uk.lan",
|
||||
"host_list": [
|
||||
"uk.gw.lan",
|
||||
"uk.lan"
|
||||
],
|
||||
"hostname": "192.168.0.251",
|
||||
"user": "nixcraft",
|
||||
"proxycommand": "ssh nixcraft@gateway.uk.cyberciti.biz nc %h %p 2> /dev/null"
|
||||
},
|
||||
{
|
||||
"host": "proxyus",
|
||||
"host_list": [
|
||||
"proxyus"
|
||||
],
|
||||
"hostname": "vps1.cyberciti.biz",
|
||||
"user": "breakfree",
|
||||
"identityfile": [
|
||||
"~/.ssh/vps1.cyberciti.biz.key"
|
||||
],
|
||||
"localforward": [
|
||||
"3128 127.0.0.1:3128"
|
||||
]
|
||||
},
|
||||
{
|
||||
"host": "*",
|
||||
"host_list": [
|
||||
"*"
|
||||
],
|
||||
"forwardagent": "no",
|
||||
"forwardx11": "no",
|
||||
"forwardx11trusted": "yes",
|
||||
"user": "nixcraft",
|
||||
"port": 22,
|
||||
"protocol": 2,
|
||||
"serveraliveinterval": 60,
|
||||
"serveralivecountmax": 30
|
||||
}
|
||||
]
|
||||
|
||||
$ cat ~/.ssh/config | jc --ssh-conf -p -r
|
||||
[
|
||||
{
|
||||
"host": "server1",
|
||||
"host_list": [
|
||||
"server1"
|
||||
],
|
||||
"hostname": "server1.cyberciti.biz",
|
||||
"user": "nixcraft",
|
||||
"port": "4242",
|
||||
"identityfile": [
|
||||
"/nfs/shared/users/nixcraft/keys/server1/id_rsa"
|
||||
]
|
||||
},
|
||||
{
|
||||
"host": "nas01",
|
||||
"host_list": [
|
||||
"nas01"
|
||||
],
|
||||
"hostname": "192.168.1.100",
|
||||
"user": "root",
|
||||
"identityfile": [
|
||||
"~/.ssh/nas01.key"
|
||||
]
|
||||
},
|
||||
{
|
||||
"host": "aws.apache",
|
||||
"host_list": [
|
||||
"aws.apache"
|
||||
],
|
||||
"hostname": "1.2.3.4",
|
||||
"user": "wwwdata",
|
||||
"identityfile": [
|
||||
"~/.ssh/aws.apache.key"
|
||||
]
|
||||
},
|
||||
{
|
||||
"host": "uk.gw.lan uk.lan",
|
||||
"host_list": [
|
||||
"uk.gw.lan",
|
||||
"uk.lan"
|
||||
],
|
||||
"hostname": "192.168.0.251",
|
||||
"user": "nixcraft",
|
||||
"proxycommand": "ssh nixcraft@gateway.uk.cyberciti.biz nc %h %p 2> /dev/null"
|
||||
},
|
||||
{
|
||||
"host": "proxyus",
|
||||
"host_list": [
|
||||
"proxyus"
|
||||
],
|
||||
"hostname": "vps1.cyberciti.biz",
|
||||
"user": "breakfree",
|
||||
"identityfile": [
|
||||
"~/.ssh/vps1.cyberciti.biz.key"
|
||||
],
|
||||
"localforward": [
|
||||
"3128 127.0.0.1:3128"
|
||||
]
|
||||
},
|
||||
{
|
||||
"host": "*",
|
||||
"host_list": [
|
||||
"*"
|
||||
],
|
||||
"forwardagent": "no",
|
||||
"forwardx11": "no",
|
||||
"forwardx11trusted": "yes",
|
||||
"user": "nixcraft",
|
||||
"port": "22",
|
||||
"protocol": "2",
|
||||
"serveraliveinterval": "60",
|
||||
"serveralivecountmax": "30"
|
||||
}
|
||||
]
|
||||
|
||||
<a id="jc.parsers.ssh_conf.parse"></a>
|
||||
|
||||
### parse
|
||||
|
||||
```python
|
||||
def parse(data: str,
|
||||
raw: bool = False,
|
||||
quiet: bool = False) -> List[JSONDictType]
|
||||
```
|
||||
|
||||
Main text parsing function
|
||||
|
||||
Parameters:
|
||||
|
||||
data: (string) text data to parse
|
||||
raw: (boolean) unprocessed output if True
|
||||
quiet: (boolean) suppress warning messages if True
|
||||
|
||||
Returns:
|
||||
|
||||
List of Dictionaries. Raw or processed structured data.
|
||||
|
||||
### Parser Information
|
||||
Compatibility: linux, darwin, freebsd
|
||||
|
||||
Version 1.0 by Kelly Brazil (kellyjonbrazil@gmail.com)
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
# jc.parsers.sshd\_conf
|
||||
|
||||
jc - JSON Convert sshd configuration file and `sshd -T` command output parser
|
||||
jc - JSON Convert `sshd` configuration file and `sshd -T` command output parser
|
||||
|
||||
This parser will work with `sshd` configuration files or the output of
|
||||
`sshd -T`. Any `Match` blocks in the `sshd` configuration file will be
|
||||
@@ -504,4 +504,4 @@ Returns:
|
||||
### Parser Information
|
||||
Compatibility: linux, darwin, freebsd
|
||||
|
||||
Version 1.0 by Kelly Brazil (kellyjonbrazil@gmail.com)
|
||||
Version 1.1 by Kelly Brazil (kellyjonbrazil@gmail.com)
|
||||
|
||||
@@ -193,4 +193,4 @@ Returns:
|
||||
### Parser Information
|
||||
Compatibility: linux, darwin, freebsd
|
||||
|
||||
Version 1.12 by Kelly Brazil (kellyjonbrazil@gmail.com)
|
||||
Version 1.13 by Kelly Brazil (kellyjonbrazil@gmail.com)
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
|
||||
jc - JSON Convert `timedatectl` command output parser
|
||||
|
||||
Also supports the `timesync-status` option.
|
||||
|
||||
The `epoch_utc` calculated timestamp field is timezone-aware and is only
|
||||
available if the `universal_time` field is available.
|
||||
|
||||
@@ -34,7 +36,24 @@ Schema:
|
||||
"system_clock_synchronized": boolean,
|
||||
"systemd-timesyncd.service_active": boolean,
|
||||
"rtc_in_local_tz": boolean,
|
||||
"dst_active": boolean
|
||||
"dst_active": boolean,
|
||||
"server": string,
|
||||
"poll_interval": string,
|
||||
"leap": string,
|
||||
"version": integer,
|
||||
"stratum": integer,
|
||||
"reference": string,
|
||||
"precision": string,
|
||||
"root_distance": string,
|
||||
"offset": float,
|
||||
"offset_unit": string,
|
||||
"delay": float,
|
||||
"delay_unit": string,
|
||||
"jitter": float,
|
||||
"jitter_unit": string,
|
||||
"packet_count": integer,
|
||||
"frequency": float,
|
||||
"frequency_unit": string
|
||||
}
|
||||
|
||||
Examples:
|
||||
@@ -87,4 +106,4 @@ Returns:
|
||||
### Parser Information
|
||||
Compatibility: linux
|
||||
|
||||
Version 1.7 by Kelly Brazil (kellyjonbrazil@gmail.com)
|
||||
Version 1.8 by Kelly Brazil (kellyjonbrazil@gmail.com)
|
||||
|
||||
@@ -161,4 +161,4 @@ Returns:
|
||||
### Parser Information
|
||||
Compatibility: linux
|
||||
|
||||
Version 1.2 by Kelly Brazil (kellyjonbrazil@gmail.com)
|
||||
Version 1.3 by Kelly Brazil (kellyjonbrazil@gmail.com)
|
||||
|
||||
@@ -42,8 +42,7 @@ Parameters:
|
||||
underscore '_'. You should also ensure headers are
|
||||
lowercase by using .lower().
|
||||
|
||||
Also, ensure there are no blank lines (list items)
|
||||
in the data.
|
||||
Also, ensure there are no blank rows in the data.
|
||||
|
||||
Returns:
|
||||
|
||||
|
||||
112
docs/parsers/ver.md
Normal file
112
docs/parsers/ver.md
Normal file
@@ -0,0 +1,112 @@
|
||||
[Home](https://kellyjonbrazil.github.io/jc/)
|
||||
<a id="jc.parsers.ver"></a>
|
||||
|
||||
# jc.parsers.ver
|
||||
|
||||
jc - JSON Convert Version string output parser
|
||||
|
||||
Best-effort attempt to parse various styles of version numbers. This parser
|
||||
is based off of the version parser included in the CPython distutils
|
||||
library.
|
||||
|
||||
If the version string conforms to some de facto-standard versioning rules
|
||||
followed by many developers a `strict` key will be present in the output
|
||||
with a value of `true` along with the named parsed components.
|
||||
|
||||
All other version strings will have a `strict` value of `false` and a
|
||||
`components` key will contain a list of detected parts of the version
|
||||
string.
|
||||
|
||||
See Also: `semver` parser.
|
||||
|
||||
Usage (cli):
|
||||
|
||||
$ echo 1.2a1 | jc --ver
|
||||
|
||||
Usage (module):
|
||||
|
||||
import jc
|
||||
result = jc.parse('ver', version_string_output)
|
||||
|
||||
Schema:
|
||||
|
||||
{
|
||||
"major": integer,
|
||||
"minor": integer,
|
||||
"patch": integer,
|
||||
"prerelease": string,
|
||||
"prerelease_num": integer,
|
||||
"components": [
|
||||
integer/string
|
||||
],
|
||||
"strict": boolean
|
||||
}
|
||||
|
||||
Examples:
|
||||
|
||||
$ echo 1.2a1 | jc --ver -p
|
||||
{
|
||||
"major": 1,
|
||||
"minor": 2,
|
||||
"patch": 0,
|
||||
"prerelease": "a",
|
||||
"prerelease_num": 1,
|
||||
"strict": true
|
||||
}
|
||||
|
||||
$ echo 1.2a1 | jc --ver -p -r
|
||||
{
|
||||
"major": "1",
|
||||
"minor": "2",
|
||||
"patch": "0",
|
||||
"prerelease": "a",
|
||||
"prerelease_num": "1",
|
||||
"strict": true
|
||||
}
|
||||
|
||||
$ echo 1.2beta3 | jc --ver -p
|
||||
{
|
||||
"components": [
|
||||
1,
|
||||
2,
|
||||
"beta",
|
||||
3
|
||||
],
|
||||
"strict": false
|
||||
}
|
||||
|
||||
$ echo 1.2beta3 | jc --ver -p -r
|
||||
{
|
||||
"components": [
|
||||
"1",
|
||||
"2",
|
||||
"beta",
|
||||
"3"
|
||||
],
|
||||
"strict": false
|
||||
}
|
||||
|
||||
<a id="jc.parsers.ver.parse"></a>
|
||||
|
||||
### parse
|
||||
|
||||
```python
|
||||
def parse(data: str, raw: bool = False, quiet: bool = False) -> JSONDictType
|
||||
```
|
||||
|
||||
Main text parsing function
|
||||
|
||||
Parameters:
|
||||
|
||||
data: (string) text data to parse
|
||||
raw: (boolean) unprocessed output if True
|
||||
quiet: (boolean) suppress warning messages if True
|
||||
|
||||
Returns:
|
||||
|
||||
List of Dictionaries. Raw or processed structured data.
|
||||
|
||||
### Parser Information
|
||||
Compatibility: linux, darwin, cygwin, win32, aix, freebsd
|
||||
|
||||
Version 1.0 by Kelly Brazil (kellyjonbrazil@gmail.com)
|
||||
108
docs/parsers/veracrypt.md
Normal file
108
docs/parsers/veracrypt.md
Normal file
@@ -0,0 +1,108 @@
|
||||
[Home](https://kellyjonbrazil.github.io/jc/)
|
||||
<a id="jc.parsers.veracrypt"></a>
|
||||
|
||||
# jc.parsers.veracrypt
|
||||
|
||||
jc - JSON Convert `veracrypt` command output parser
|
||||
|
||||
Supports the following `veracrypt` subcommands:
|
||||
- `veracrypt --text --list`
|
||||
- `veracrypt --text --list --verbose`
|
||||
- `veracrypt --text --volume-properties <volume>`
|
||||
|
||||
Usage (cli):
|
||||
|
||||
$ veracrypt --text --list | jc --veracrypt
|
||||
or
|
||||
|
||||
$ jc veracrypt --text --list
|
||||
|
||||
Usage (module):
|
||||
|
||||
import jc
|
||||
result = jc.parse('veracrypt', veracrypt_command_output)
|
||||
|
||||
Schema:
|
||||
|
||||
Volume:
|
||||
[
|
||||
{
|
||||
"slot": integer,
|
||||
"path": string,
|
||||
"device": string,
|
||||
"mountpoint": string,
|
||||
"size": string,
|
||||
"type": string,
|
||||
"readonly": string,
|
||||
"hidden_protected": string,
|
||||
"encryption_algo": string,
|
||||
"pk_size": string,
|
||||
"sk_size": string,
|
||||
"block_size": string,
|
||||
"mode": string,
|
||||
"prf": string,
|
||||
"format_version": integer,
|
||||
"backup_header": string
|
||||
}
|
||||
]
|
||||
|
||||
Examples:
|
||||
|
||||
$ veracrypt --text --list | jc --veracrypt -p
|
||||
[
|
||||
{
|
||||
"slot": 1,
|
||||
"path": "/dev/sdb1",
|
||||
"device": "/dev/mapper/veracrypt1",
|
||||
"mountpoint": "/home/bob/mount/encrypt/sdb1"
|
||||
}
|
||||
]
|
||||
|
||||
$ veracrypt --text --list --verbose | jc --veracrypt -p
|
||||
[
|
||||
{
|
||||
"slot": 1,
|
||||
"path": "/dev/sdb1",
|
||||
"device": "/dev/mapper/veracrypt1",
|
||||
"mountpoint": "/home/bob/mount/encrypt/sdb1",
|
||||
"size": "522 MiB",
|
||||
"type": "Normal",
|
||||
"readonly": "No",
|
||||
"hidden_protected": "No",
|
||||
"encryption_algo": "AES",
|
||||
"pk_size": "256 bits",
|
||||
"sk_size": "256 bits",
|
||||
"block_size": "128 bits",
|
||||
"mode": "XTS",
|
||||
"prf": "HMAC-SHA-512",
|
||||
"format_version": 2,
|
||||
"backup_header": "Yes"
|
||||
}
|
||||
]
|
||||
|
||||
<a id="jc.parsers.veracrypt.parse"></a>
|
||||
|
||||
### parse
|
||||
|
||||
```python
|
||||
def parse(data: str,
|
||||
raw: bool = False,
|
||||
quiet: bool = False) -> List[JSONDictType]
|
||||
```
|
||||
|
||||
Main text parsing function
|
||||
|
||||
Parameters:
|
||||
|
||||
data: (string) text data to parse
|
||||
raw: (boolean) unprocessed output if True
|
||||
quiet: (boolean) suppress warning messages if True
|
||||
|
||||
Returns:
|
||||
|
||||
List of Dictionaries. Raw or processed structured data.
|
||||
|
||||
### Parser Information
|
||||
Compatibility: linux
|
||||
|
||||
Version 1.0 by Jake Ob (iakopap at gmail.com)
|
||||
@@ -433,4 +433,4 @@ Returns:
|
||||
### Parser Information
|
||||
Compatibility: linux, darwin, cygwin, win32, aix, freebsd
|
||||
|
||||
Version 1.1 by Kelly Brazil (kellyjonbrazil@gmail.com)
|
||||
Version 1.3 by Kelly Brazil (kellyjonbrazil@gmail.com)
|
||||
|
||||
282
docs/parsers/x509_csr.md
Normal file
282
docs/parsers/x509_csr.md
Normal file
@@ -0,0 +1,282 @@
|
||||
[Home](https://kellyjonbrazil.github.io/jc/)
|
||||
<a id="jc.parsers.x509_csr"></a>
|
||||
|
||||
# jc.parsers.x509\_csr
|
||||
|
||||
jc - JSON Convert X.509 Certificate Request format file parser
|
||||
|
||||
This parser will convert DER and PEM encoded X.509 certificate request files.
|
||||
|
||||
Usage (cli):
|
||||
|
||||
$ cat certificateRequest.pem | jc --x509-csr
|
||||
|
||||
Usage (module):
|
||||
|
||||
import jc
|
||||
result = jc.parse('x509_csr', x509_csr_file_output)
|
||||
|
||||
Schema:
|
||||
|
||||
[
|
||||
{
|
||||
"certification_request_info": {
|
||||
"version": string,
|
||||
"serial_number": string, # [0]
|
||||
"serial_number_str": string,
|
||||
"signature": {
|
||||
"algorithm": string,
|
||||
"parameters": string/null,
|
||||
},
|
||||
"issuer": {
|
||||
"country_name": string,
|
||||
"state_or_province_name" string,
|
||||
"locality_name": string,
|
||||
"organization_name": array/string,
|
||||
"organizational_unit_name": array/string,
|
||||
"common_name": string,
|
||||
"email_address": string,
|
||||
"serial_number": string, # [0]
|
||||
"serial_number_str": string
|
||||
},
|
||||
"validity": {
|
||||
"not_before": integer, # [1]
|
||||
"not_after": integer, # [1]
|
||||
"not_before_iso": string,
|
||||
"not_after_iso": string
|
||||
},
|
||||
"subject": {
|
||||
"country_name": string,
|
||||
"state_or_province_name": string,
|
||||
"locality_name": string,
|
||||
"organization_name": array/string,
|
||||
"organizational_unit_name": array/string,
|
||||
"common_name": string,
|
||||
"email_address": string,
|
||||
"serial_number": string, # [0]
|
||||
"serial_number_str": string
|
||||
},
|
||||
"subject_public_key_info": {
|
||||
"algorithm": {
|
||||
"algorithm": string,
|
||||
"parameters": string/null,
|
||||
},
|
||||
"public_key": {
|
||||
"modulus": string, # [0]
|
||||
"public_exponent": integer
|
||||
}
|
||||
},
|
||||
"issuer_unique_id": string/null,
|
||||
"subject_unique_id": string/null,
|
||||
"extensions": [
|
||||
{
|
||||
"extn_id": string,
|
||||
"critical": boolean,
|
||||
"extn_value": array/object/string/integer # [2]
|
||||
}
|
||||
]
|
||||
},
|
||||
"signature_algorithm": {
|
||||
"algorithm": string,
|
||||
"parameters": string/null
|
||||
},
|
||||
"signature_value": string # [0]
|
||||
}
|
||||
]
|
||||
|
||||
[0] in colon-delimited hex notation
|
||||
[1] time-zone-aware (UTC) epoch timestamp
|
||||
[2] See below for well-known Extension schemas:
|
||||
|
||||
Basic Constraints:
|
||||
{
|
||||
"extn_id": "basic_constraints",
|
||||
"critical": boolean,
|
||||
"extn_value": {
|
||||
"ca": boolean,
|
||||
"path_len_constraint": string/null
|
||||
}
|
||||
}
|
||||
|
||||
Key Usage:
|
||||
{
|
||||
"extn_id": "key_usage",
|
||||
"critical": boolean,
|
||||
"extn_value": [
|
||||
string
|
||||
]
|
||||
}
|
||||
|
||||
Key Identifier:
|
||||
{
|
||||
"extn_id": "key_identifier",
|
||||
"critical": boolean,
|
||||
"extn_value": string # [0]
|
||||
}
|
||||
|
||||
Authority Key Identifier:
|
||||
{
|
||||
"extn_id": "authority_key_identifier",
|
||||
"critical": boolean,
|
||||
"extn_value": {
|
||||
"key_identifier": string, # [0]
|
||||
"authority_cert_issuer": string/null,
|
||||
"authority_cert_serial_number": string/null
|
||||
}
|
||||
}
|
||||
|
||||
Subject Alternative Name:
|
||||
{
|
||||
"extn_id": "subject_alt_name",
|
||||
"critical": boolean,
|
||||
"extn_value": [
|
||||
string
|
||||
]
|
||||
}
|
||||
|
||||
Certificate Policies:
|
||||
{
|
||||
"extn_id": "certificate_policies",
|
||||
"critical": boolean,
|
||||
"extn_value": [
|
||||
{
|
||||
"policy_identifier": string,
|
||||
"policy_qualifiers": [ array or null
|
||||
{
|
||||
"policy_qualifier_id": string,
|
||||
"qualifier": string
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
Signed Certificate Timestamp List:
|
||||
{
|
||||
"extn_id": "signed_certificate_timestamp_list",
|
||||
"critical": boolean,
|
||||
"extn_value": string # [0]
|
||||
}
|
||||
|
||||
Examples:
|
||||
|
||||
$ cat server.csr| jc --x509-csr -p
|
||||
[
|
||||
{
|
||||
"certification_request_info": {
|
||||
"version": "v1",
|
||||
"subject": {
|
||||
"common_name": "myserver.for.example"
|
||||
},
|
||||
"subject_pk_info": {
|
||||
"algorithm": {
|
||||
"algorithm": "ec",
|
||||
"parameters": "secp256r1"
|
||||
},
|
||||
"public_key": "04:40:33:c0:91:8f:e9:46:ea:d0:dc:d0:f9:63:2..."
|
||||
},
|
||||
"attributes": [
|
||||
{
|
||||
"type": "extension_request",
|
||||
"values": [
|
||||
[
|
||||
{
|
||||
"extn_id": "extended_key_usage",
|
||||
"critical": false,
|
||||
"extn_value": [
|
||||
"server_auth"
|
||||
]
|
||||
},
|
||||
{
|
||||
"extn_id": "subject_alt_name",
|
||||
"critical": false,
|
||||
"extn_value": [
|
||||
"myserver.for.example"
|
||||
]
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"signature_algorithm": {
|
||||
"algorithm": "sha384_ecdsa",
|
||||
"parameters": null
|
||||
},
|
||||
"signature": "30:45:02:20:77:ac:5b:51:bf:c5:f5:43:02:52:ae:66:..."
|
||||
}
|
||||
]
|
||||
|
||||
$ openssl req -in server.csr | jc --x509-csr -p
|
||||
[
|
||||
{
|
||||
"certification_request_info": {
|
||||
"version": "v1",
|
||||
"subject": {
|
||||
"common_name": "myserver.for.example"
|
||||
},
|
||||
"subject_pk_info": {
|
||||
"algorithm": {
|
||||
"algorithm": "ec",
|
||||
"parameters": "secp256r1"
|
||||
},
|
||||
"public_key": "04:40:33:c0:91:8f:e9:46:ea:d0:dc:d0:f9:63:2..."
|
||||
},
|
||||
"attributes": [
|
||||
{
|
||||
"type": "extension_request",
|
||||
"values": [
|
||||
[
|
||||
{
|
||||
"extn_id": "extended_key_usage",
|
||||
"critical": false,
|
||||
"extn_value": [
|
||||
"server_auth"
|
||||
]
|
||||
},
|
||||
{
|
||||
"extn_id": "subject_alt_name",
|
||||
"critical": false,
|
||||
"extn_value": [
|
||||
"myserver.for.example"
|
||||
]
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"signature_algorithm": {
|
||||
"algorithm": "sha384_ecdsa",
|
||||
"parameters": null
|
||||
},
|
||||
"signature": "30:45:02:20:77:ac:5b:51:bf:c5:f5:43:02:52:ae:66:..."
|
||||
}
|
||||
]
|
||||
|
||||
<a id="jc.parsers.x509_csr.parse"></a>
|
||||
|
||||
### parse
|
||||
|
||||
```python
|
||||
def parse(data: Union[str, bytes],
|
||||
raw: bool = False,
|
||||
quiet: bool = False) -> List[Dict]
|
||||
```
|
||||
|
||||
Main text parsing function
|
||||
|
||||
Parameters:
|
||||
|
||||
data: (string or bytes) text or binary data to parse
|
||||
raw: (boolean) unprocessed output if True
|
||||
quiet: (boolean) suppress warning messages if True
|
||||
|
||||
Returns:
|
||||
|
||||
List of Dictionaries. Raw or processed structured data.
|
||||
|
||||
### Parser Information
|
||||
Compatibility: linux, darwin, cygwin, win32, aix, freebsd
|
||||
|
||||
Version 1.0 by Kelly Brazil (kellyjonbrazil@gmail.com)
|
||||
@@ -98,4 +98,4 @@ Returns:
|
||||
### Parser Information
|
||||
Compatibility: linux, darwin, cygwin, win32, aix, freebsd
|
||||
|
||||
Version 1.7 by Kelly Brazil (kellyjonbrazil@gmail.com)
|
||||
Version 1.8 by Kelly Brazil (kellyjonbrazil@gmail.com)
|
||||
|
||||
@@ -8,6 +8,7 @@ jc - JSON Convert `xrandr` command output parser
|
||||
Usage (cli):
|
||||
|
||||
$ xrandr | jc --xrandr
|
||||
$ xrandr --properties | jc --xrandr
|
||||
|
||||
or
|
||||
|
||||
@@ -30,8 +31,8 @@ Schema:
|
||||
"current_height": integer,
|
||||
"maximum_width": integer,
|
||||
"maximum_height": integer,
|
||||
"associated_device": {
|
||||
"associated_modes": [
|
||||
"devices": {
|
||||
"modes": [
|
||||
{
|
||||
"resolution_width": integer,
|
||||
"resolution_height": integer,
|
||||
@@ -49,33 +50,19 @@ Schema:
|
||||
"is_connected": boolean,
|
||||
"is_primary": boolean,
|
||||
"device_name": string,
|
||||
"model_name": string,
|
||||
"product_id" string,
|
||||
"serial_number": string,
|
||||
"resolution_width": integer,
|
||||
"resolution_height": integer,
|
||||
"offset_width": integer,
|
||||
"offset_height": integer,
|
||||
"dimension_width": integer,
|
||||
"dimension_height": integer,
|
||||
"rotation": string
|
||||
"rotation": string,
|
||||
"reflection": string
|
||||
}
|
||||
],
|
||||
"unassociated_devices": [
|
||||
{
|
||||
"associated_modes": [
|
||||
{
|
||||
"resolution_width": integer,
|
||||
"resolution_height": integer,
|
||||
"is_high_resolution": boolean,
|
||||
"frequencies": [
|
||||
{
|
||||
"frequency": float,
|
||||
"is_current": boolean,
|
||||
"is_preferred": boolean
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
Examples:
|
||||
@@ -91,8 +78,8 @@ Examples:
|
||||
"current_height": 1080,
|
||||
"maximum_width": 32767,
|
||||
"maximum_height": 32767,
|
||||
"associated_device": {
|
||||
"associated_modes": [
|
||||
"devices": {
|
||||
"modes": [
|
||||
{
|
||||
"resolution_width": 1920,
|
||||
"resolution_height": 1080,
|
||||
@@ -132,11 +119,73 @@ Examples:
|
||||
"offset_height": 0,
|
||||
"dimension_width": 310,
|
||||
"dimension_height": 170,
|
||||
"rotation": "normal"
|
||||
"rotation": "normal",
|
||||
"reflection": "normal"
|
||||
}
|
||||
}
|
||||
],
|
||||
"unassociated_devices": []
|
||||
]
|
||||
}
|
||||
|
||||
$ xrandr --properties | jc --xrandr -p
|
||||
{
|
||||
"screens": [
|
||||
{
|
||||
"screen_number": 0,
|
||||
"minimum_width": 8,
|
||||
"minimum_height": 8,
|
||||
"current_width": 1920,
|
||||
"current_height": 1080,
|
||||
"maximum_width": 32767,
|
||||
"maximum_height": 32767,
|
||||
"devices": {
|
||||
"modes": [
|
||||
{
|
||||
"resolution_width": 1920,
|
||||
"resolution_height": 1080,
|
||||
"is_high_resolution": false,
|
||||
"frequencies": [
|
||||
{
|
||||
"frequency": 60.03,
|
||||
"is_current": true,
|
||||
"is_preferred": true
|
||||
},
|
||||
{
|
||||
"frequency": 59.93,
|
||||
"is_current": false,
|
||||
"is_preferred": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"resolution_width": 1680,
|
||||
"resolution_height": 1050,
|
||||
"is_high_resolution": false,
|
||||
"frequencies": [
|
||||
{
|
||||
"frequency": 59.88,
|
||||
"is_current": false,
|
||||
"is_preferred": false
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"is_connected": true,
|
||||
"is_primary": true,
|
||||
"device_name": "eDP1",
|
||||
"model_name": "ASUS VW193S",
|
||||
"product_id": "54297",
|
||||
"serial_number": "78L8021107",
|
||||
"resolution_width": 1920,
|
||||
"resolution_height": 1080,
|
||||
"offset_width": 0,
|
||||
"offset_height": 0,
|
||||
"dimension_width": 310,
|
||||
"dimension_height": 170,
|
||||
"rotation": "normal",
|
||||
"reflection": "normal"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
<a id="jc.parsers.xrandr.parse"></a>
|
||||
@@ -162,4 +211,4 @@ Returns:
|
||||
### Parser Information
|
||||
Compatibility: linux, darwin, cygwin, aix, freebsd
|
||||
|
||||
Version 1.1 by Kevin Lyter (lyter_git at sent.com)
|
||||
Version 1.3 by Kevin Lyter (code (at) lyterk.com)
|
||||
|
||||
125
docs/parsers/zpool_iostat.md
Normal file
125
docs/parsers/zpool_iostat.md
Normal file
@@ -0,0 +1,125 @@
|
||||
[Home](https://kellyjonbrazil.github.io/jc/)
|
||||
<a id="jc.parsers.zpool_iostat"></a>
|
||||
|
||||
# jc.parsers.zpool\_iostat
|
||||
|
||||
jc - JSON Convert `zpool iostat` command output parser
|
||||
|
||||
Supports with or without the `-v` flag.
|
||||
|
||||
Usage (cli):
|
||||
|
||||
$ zpool iostat | jc --zpool-iostat
|
||||
|
||||
or
|
||||
|
||||
$ jc zpool iostat
|
||||
|
||||
Usage (module):
|
||||
|
||||
import jc
|
||||
result = jc.parse('zpool_iostat', zpool_iostat_command_output)
|
||||
|
||||
Schema:
|
||||
|
||||
[
|
||||
{
|
||||
"pool": string,
|
||||
"parent": string,
|
||||
"cap_alloc": float,
|
||||
"cap_alloc_unit": string,
|
||||
"cap_free": float,
|
||||
"cap_free_unit": string,
|
||||
"ops_read": integer,
|
||||
"ops_write": integer,
|
||||
"bw_read": float,
|
||||
"bw_read_unit": string,
|
||||
"bw_write": float,
|
||||
"bw_write_unit": string
|
||||
}
|
||||
]
|
||||
|
||||
Examples:
|
||||
|
||||
$ zpool iostat -v | jc --zpool-iostat -p
|
||||
[
|
||||
{
|
||||
"pool": "zhgstera6",
|
||||
"cap_alloc": 2.89,
|
||||
"cap_free": 2.2,
|
||||
"ops_read": 0,
|
||||
"ops_write": 2,
|
||||
"bw_read": 349.0,
|
||||
"bw_write": 448.0,
|
||||
"cap_alloc_unit": "T",
|
||||
"cap_free_unit": "T",
|
||||
"bw_read_unit": "K",
|
||||
"bw_write_unit": "K"
|
||||
},
|
||||
{
|
||||
"pool": "726060ALE614-K8JAPRGN:10",
|
||||
"parent": "zhgstera6",
|
||||
"cap_alloc": 2.89,
|
||||
"cap_free": 2.2,
|
||||
"ops_read": 0,
|
||||
"ops_write": 2,
|
||||
"bw_read": 349.0,
|
||||
"bw_write": 448.0,
|
||||
"cap_alloc_unit": "T",
|
||||
"cap_free_unit": "T",
|
||||
"bw_read_unit": "K",
|
||||
"bw_write_unit": "K"
|
||||
},
|
||||
...
|
||||
]
|
||||
|
||||
$ zpool iostat -v | jc --zpool-iostat -p -r
|
||||
[
|
||||
{
|
||||
"pool": "zhgstera6",
|
||||
"cap_alloc": "2.89T",
|
||||
"cap_free": "2.20T",
|
||||
"ops_read": "0",
|
||||
"ops_write": "2",
|
||||
"bw_read": "349K",
|
||||
"bw_write": "448K"
|
||||
},
|
||||
{
|
||||
"pool": "726060ALE614-K8JAPRGN:10",
|
||||
"parent": "zhgstera6",
|
||||
"cap_alloc": "2.89T",
|
||||
"cap_free": "2.20T",
|
||||
"ops_read": "0",
|
||||
"ops_write": "2",
|
||||
"bw_read": "349K",
|
||||
"bw_write": "448K"
|
||||
},
|
||||
...
|
||||
]
|
||||
|
||||
<a id="jc.parsers.zpool_iostat.parse"></a>
|
||||
|
||||
### parse
|
||||
|
||||
```python
|
||||
def parse(data: str,
|
||||
raw: bool = False,
|
||||
quiet: bool = False) -> List[JSONDictType]
|
||||
```
|
||||
|
||||
Main text parsing function
|
||||
|
||||
Parameters:
|
||||
|
||||
data: (string) text data to parse
|
||||
raw: (boolean) unprocessed output if True
|
||||
quiet: (boolean) suppress warning messages if True
|
||||
|
||||
Returns:
|
||||
|
||||
List of Dictionaries. Raw or processed structured data.
|
||||
|
||||
### Parser Information
|
||||
Compatibility: linux, darwin, freebsd
|
||||
|
||||
Version 1.0 by Kelly Brazil (kellyjonbrazil@gmail.com)
|
||||
163
docs/parsers/zpool_status.md
Normal file
163
docs/parsers/zpool_status.md
Normal file
@@ -0,0 +1,163 @@
|
||||
[Home](https://kellyjonbrazil.github.io/jc/)
|
||||
<a id="jc.parsers.zpool_status"></a>
|
||||
|
||||
# jc.parsers.zpool\_status
|
||||
|
||||
jc - JSON Convert `zpool status` command output parser
|
||||
|
||||
Works with or without the `-v` option.
|
||||
|
||||
Usage (cli):
|
||||
|
||||
$ zpool status | jc --zpool-status
|
||||
|
||||
or
|
||||
|
||||
$ jc zpool status
|
||||
|
||||
Usage (module):
|
||||
|
||||
import jc
|
||||
result = jc.parse('zpool_status', zpool_status_command_output)
|
||||
|
||||
Schema:
|
||||
|
||||
[
|
||||
{
|
||||
"pool": string,
|
||||
"state": string,
|
||||
"status": string,
|
||||
"action": string,
|
||||
"see": string,
|
||||
"scan": string,
|
||||
"scrub": string,
|
||||
"config": [
|
||||
{
|
||||
"name": string,
|
||||
"state": string,
|
||||
"read": integer,
|
||||
"write": integer,
|
||||
"checksum": integer,
|
||||
"errors": string,
|
||||
}
|
||||
],
|
||||
"errors": string
|
||||
}
|
||||
]
|
||||
|
||||
Examples:
|
||||
|
||||
$ zpool status -v | jc --zpool-status -p
|
||||
[
|
||||
{
|
||||
"pool": "tank",
|
||||
"state": "DEGRADED",
|
||||
"status": "One or more devices could not be opened. Suffic...",
|
||||
"action": "Attach the missing device and online it using 'zpool...",
|
||||
"see": "http://www.sun.com/msg/ZFS-8000-2Q",
|
||||
"scrub": "none requested",
|
||||
"config": [
|
||||
{
|
||||
"name": "tank",
|
||||
"state": "DEGRADED",
|
||||
"read": 0,
|
||||
"write": 0,
|
||||
"checksum": 0
|
||||
},
|
||||
{
|
||||
"name": "mirror-0",
|
||||
"state": "DEGRADED",
|
||||
"read": 0,
|
||||
"write": 0,
|
||||
"checksum": 0
|
||||
},
|
||||
{
|
||||
"name": "c1t0d0",
|
||||
"state": "ONLINE",
|
||||
"read": 0,
|
||||
"write": 0,
|
||||
"checksum": 0
|
||||
},
|
||||
{
|
||||
"name": "c1t1d0",
|
||||
"state": "UNAVAIL",
|
||||
"read": 0,
|
||||
"write": 0,
|
||||
"checksum": 0,
|
||||
"errors": "cannot open"
|
||||
}
|
||||
],
|
||||
"errors": "No known data errors"
|
||||
}
|
||||
]
|
||||
|
||||
$ zpool status -v | jc --zpool-status -p -r
|
||||
[
|
||||
{
|
||||
"pool": "tank",
|
||||
"state": "DEGRADED",
|
||||
"status": "One or more devices could not be opened. Sufficient...",
|
||||
"action": "Attach the missing device and online it using 'zpool...",
|
||||
"see": "http://www.sun.com/msg/ZFS-8000-2Q",
|
||||
"scrub": "none requested",
|
||||
"config": [
|
||||
{
|
||||
"name": "tank",
|
||||
"state": "DEGRADED",
|
||||
"read": "0",
|
||||
"write": "0",
|
||||
"checksum": "0"
|
||||
},
|
||||
{
|
||||
"name": "mirror-0",
|
||||
"state": "DEGRADED",
|
||||
"read": "0",
|
||||
"write": "0",
|
||||
"checksum": "0"
|
||||
},
|
||||
{
|
||||
"name": "c1t0d0",
|
||||
"state": "ONLINE",
|
||||
"read": "0",
|
||||
"write": "0",
|
||||
"checksum": "0"
|
||||
},
|
||||
{
|
||||
"name": "c1t1d0",
|
||||
"state": "UNAVAIL",
|
||||
"read": "0",
|
||||
"write": "0",
|
||||
"checksum": "0",
|
||||
"errors": "cannot open"
|
||||
}
|
||||
],
|
||||
"errors": "No known data errors"
|
||||
}
|
||||
]
|
||||
|
||||
<a id="jc.parsers.zpool_status.parse"></a>
|
||||
|
||||
### parse
|
||||
|
||||
```python
|
||||
def parse(data: str,
|
||||
raw: bool = False,
|
||||
quiet: bool = False) -> List[JSONDictType]
|
||||
```
|
||||
|
||||
Main text parsing function
|
||||
|
||||
Parameters:
|
||||
|
||||
data: (string) text data to parse
|
||||
raw: (boolean) unprocessed output if True
|
||||
quiet: (boolean) suppress warning messages if True
|
||||
|
||||
Returns:
|
||||
|
||||
List of Dictionaries. Raw or processed structured data.
|
||||
|
||||
### Parser Information
|
||||
Compatibility: linux, darwin, freebsd
|
||||
|
||||
Version 1.1 by Kelly Brazil (kellyjonbrazil@gmail.com)
|
||||
@@ -124,6 +124,14 @@ Get a list of streaming parser module names to be used in
|
||||
`parse()`, `parser_info()`, and `get_help()`. This list is a subset of
|
||||
`parser_mod_list()`.
|
||||
"""
|
||||
from .lib import (__version__, parse, parser_mod_list, plugin_parser_mod_list,
|
||||
standard_parser_mod_list, streaming_parser_mod_list,
|
||||
parser_info, all_parser_info, get_help)
|
||||
from .lib import (
|
||||
__version__ as __version__,
|
||||
parse as parse,
|
||||
parser_mod_list as parser_mod_list,
|
||||
plugin_parser_mod_list as plugin_parser_mod_list,
|
||||
standard_parser_mod_list as standard_parser_mod_list,
|
||||
streaming_parser_mod_list as streaming_parser_mod_list,
|
||||
parser_info as parser_info,
|
||||
all_parser_info as all_parser_info,
|
||||
get_help as get_help
|
||||
)
|
||||
|
||||
255
jc/cli.py
255
jc/cli.py
@@ -5,11 +5,13 @@ JC cli module
|
||||
import io
|
||||
import sys
|
||||
import os
|
||||
import re
|
||||
from itertools import islice
|
||||
from datetime import datetime, timezone
|
||||
import textwrap
|
||||
import shlex
|
||||
import subprocess
|
||||
from typing import List, Dict, Union, Optional, TextIO
|
||||
from typing import List, Dict, Iterable, Union, Optional, TextIO
|
||||
from types import ModuleType
|
||||
from .lib import (
|
||||
__version__, parser_info, all_parser_info, parsers, _get_parser, _parser_is_streaming,
|
||||
@@ -40,6 +42,10 @@ except Exception:
|
||||
JC_CLEAN_EXIT: int = 0
|
||||
JC_ERROR_EXIT: int = 100
|
||||
MAX_EXIT: int = 255
|
||||
SLICER_PATTERN: str = r'-?[0-9]*\:-?[0-9]*$'
|
||||
SLICER_RE = re.compile(SLICER_PATTERN)
|
||||
NEWLINES_PATTERN: str = r'(\r\n|\r|\n)'
|
||||
NEWLINES_RE = re.compile(NEWLINES_PATTERN)
|
||||
|
||||
|
||||
class info():
|
||||
@@ -69,11 +75,11 @@ class JcCli():
|
||||
'help_me', 'pretty', 'quiet', 'ignore_exceptions', 'raw', 'meta_out', 'unbuffer',
|
||||
'version_info', 'yaml_output', 'bash_comp', 'zsh_comp', 'magic_found_parser',
|
||||
'magic_options', 'magic_run_command', 'magic_run_command_str', 'magic_stdout',
|
||||
'magic_stderr', 'magic_returncode'
|
||||
'magic_stderr', 'magic_returncode', 'slice_str', 'slice_start', 'slice_end'
|
||||
)
|
||||
|
||||
def __init__(self) -> None:
|
||||
self.data_in: Optional[Union[str, bytes, TextIO]] = None
|
||||
self.data_in: Optional[Union[str, bytes, TextIO, Iterable[str]]] = None
|
||||
self.data_out: Optional[Union[List[JSONDictType], JSONDictType]] = None
|
||||
self.options: List[str] = []
|
||||
self.args: List[str] = []
|
||||
@@ -89,6 +95,11 @@ class JcCli():
|
||||
self.json_indent: Optional[int] = None
|
||||
self.run_timestamp: Optional[datetime] = None
|
||||
|
||||
# slicer
|
||||
self.slice_str: str = ''
|
||||
self.slice_start: Optional[int] = None
|
||||
self.slice_end: Optional[int] = None
|
||||
|
||||
# cli options
|
||||
self.about: bool = False
|
||||
self.debug: bool = False
|
||||
@@ -134,33 +145,34 @@ class JcCli():
|
||||
JC_COLORS=blue,brightblack,magenta,green
|
||||
JC_COLORS=default,default,default,default
|
||||
"""
|
||||
input_error = False
|
||||
env_colors = os.getenv('JC_COLORS')
|
||||
if PYGMENTS_INSTALLED:
|
||||
input_error = False
|
||||
env_colors = os.getenv('JC_COLORS')
|
||||
|
||||
if env_colors:
|
||||
color_list = env_colors.split(',')
|
||||
else:
|
||||
color_list = ['default', 'default', 'default', 'default']
|
||||
if env_colors:
|
||||
color_list = env_colors.split(',')
|
||||
else:
|
||||
color_list = ['default', 'default', 'default', 'default']
|
||||
|
||||
if len(color_list) != 4:
|
||||
input_error = True
|
||||
|
||||
for color in color_list:
|
||||
if color != 'default' and color not in PYGMENT_COLOR:
|
||||
if len(color_list) != 4:
|
||||
input_error = True
|
||||
|
||||
# if there is an issue with the env variable, just set all colors to default and move on
|
||||
if input_error:
|
||||
utils.warning_message(['Could not parse JC_COLORS environment variable'])
|
||||
color_list = ['default', 'default', 'default', 'default']
|
||||
for color in color_list:
|
||||
if color != 'default' and color not in PYGMENT_COLOR:
|
||||
input_error = True
|
||||
|
||||
# Try the color set in the JC_COLORS env variable first. If it is set to default, then fall back to default colors
|
||||
self.custom_colors = {
|
||||
Name.Tag: f'bold {PYGMENT_COLOR[color_list[0]]}' if color_list[0] != 'default' else f"bold {PYGMENT_COLOR['blue']}", # key names
|
||||
Keyword: PYGMENT_COLOR[color_list[1]] if color_list[1] != 'default' else PYGMENT_COLOR['brightblack'], # true, false, null
|
||||
Number: PYGMENT_COLOR[color_list[2]] if color_list[2] != 'default' else PYGMENT_COLOR['magenta'], # numbers
|
||||
String: PYGMENT_COLOR[color_list[3]] if color_list[3] != 'default' else PYGMENT_COLOR['green'] # strings
|
||||
}
|
||||
# if there is an issue with the env variable, just set all colors to default and move on
|
||||
if input_error:
|
||||
utils.warning_message(['Could not parse JC_COLORS environment variable'])
|
||||
color_list = ['default', 'default', 'default', 'default']
|
||||
|
||||
# Try the color set in the JC_COLORS env variable first. If it is set to default, then fall back to default colors
|
||||
self.custom_colors = {
|
||||
Name.Tag: f'bold {PYGMENT_COLOR[color_list[0]]}' if color_list[0] != 'default' else f"bold {PYGMENT_COLOR['blue']}", # key names
|
||||
Keyword: PYGMENT_COLOR[color_list[1]] if color_list[1] != 'default' else PYGMENT_COLOR['brightblack'], # true, false, null
|
||||
Number: PYGMENT_COLOR[color_list[2]] if color_list[2] != 'default' else PYGMENT_COLOR['magenta'], # numbers
|
||||
String: PYGMENT_COLOR[color_list[3]] if color_list[3] != 'default' else PYGMENT_COLOR['green'] # strings
|
||||
}
|
||||
|
||||
def set_mono(self) -> None:
|
||||
"""
|
||||
@@ -204,14 +216,14 @@ class JcCli():
|
||||
category_text: str = ''
|
||||
padding_char: str = ' '
|
||||
all_parsers = all_parser_info(show_hidden=True, show_deprecated=False)
|
||||
generic = [{'arg': x['argument'], 'desc': x['description']} for x in all_parsers if 'generic' in x['tags']]
|
||||
standard = [{'arg': x['argument'], 'desc': x['description']} for x in all_parsers if 'standard' in x['tags']]
|
||||
command = [{'arg': x['argument'], 'desc': x['description']} for x in all_parsers if 'command' in x['tags']]
|
||||
generic = [{'arg': x['argument'], 'desc': x['description']} for x in all_parsers if 'generic' in x.get('tags', [])]
|
||||
standard = [{'arg': x['argument'], 'desc': x['description']} for x in all_parsers if 'standard' in x.get('tags', [])]
|
||||
command = [{'arg': x['argument'], 'desc': x['description']} for x in all_parsers if 'command' in x.get('tags', [])]
|
||||
file_str_bin = [
|
||||
{'arg': x['argument'], 'desc': x['description']} for x in all_parsers
|
||||
if 'file' in x['tags'] or
|
||||
'string' in x['tags'] or
|
||||
'binary' in x['tags']
|
||||
if 'file' in x.get('tags', []) or
|
||||
'string' in x.get('tags', []) or
|
||||
'binary' in x.get('tags', [])
|
||||
]
|
||||
streaming = [{'arg': x['argument'], 'desc': x['description']} for x in all_parsers if x.get('streaming')]
|
||||
categories: Dict = {
|
||||
@@ -432,6 +444,17 @@ class JcCli():
|
||||
self.magic_options = []
|
||||
return
|
||||
|
||||
# slicer found
|
||||
if ':' in arg:
|
||||
if SLICER_RE.match(arg):
|
||||
self.slice_str = arg
|
||||
args_given.pop(0)
|
||||
continue
|
||||
else:
|
||||
utils.warning_message(['Invalid slice syntax.'])
|
||||
args_given.pop(0)
|
||||
continue
|
||||
|
||||
# option found - populate option list
|
||||
if arg.startswith('-'):
|
||||
self.magic_options.extend(args_given.pop(0)[1:])
|
||||
@@ -574,59 +597,6 @@ class JcCli():
|
||||
utils.error_message(['Missing piped data. Use "jc -h" for help.'])
|
||||
self.exit_error()
|
||||
|
||||
def streaming_parse_and_print(self) -> None:
|
||||
"""only supports UTF-8 string data for now"""
|
||||
self.data_in = sys.stdin
|
||||
if self.parser_module:
|
||||
result = self.parser_module.parse(
|
||||
self.data_in,
|
||||
raw=self.raw,
|
||||
quiet=self.quiet,
|
||||
ignore_exceptions=self.ignore_exceptions
|
||||
)
|
||||
|
||||
for line in result:
|
||||
self.data_out = line
|
||||
if self.meta_out:
|
||||
self.run_timestamp = datetime.now(timezone.utc)
|
||||
self.add_metadata_to_output()
|
||||
|
||||
self.safe_print_out()
|
||||
|
||||
def standard_parse_and_print(self) -> None:
|
||||
"""supports binary and UTF-8 string data"""
|
||||
self.data_in = self.magic_stdout or sys.stdin.buffer.read()
|
||||
|
||||
# convert to UTF-8, if possible. Otherwise, leave as bytes
|
||||
try:
|
||||
if isinstance(self.data_in, bytes):
|
||||
self.data_in = self.data_in.decode('utf-8')
|
||||
except UnicodeDecodeError:
|
||||
pass
|
||||
|
||||
if self.parser_module:
|
||||
self.data_out = self.parser_module.parse(
|
||||
self.data_in,
|
||||
raw=self.raw,
|
||||
quiet=self.quiet
|
||||
)
|
||||
|
||||
if self.meta_out:
|
||||
self.run_timestamp = datetime.now(timezone.utc)
|
||||
self.add_metadata_to_output()
|
||||
|
||||
self.safe_print_out()
|
||||
|
||||
def exit_clean(self) -> None:
|
||||
exit_code: int = self.magic_returncode + JC_CLEAN_EXIT
|
||||
exit_code = min(exit_code, MAX_EXIT)
|
||||
sys.exit(exit_code)
|
||||
|
||||
def exit_error(self) -> None:
|
||||
exit_code: int = self.magic_returncode + JC_ERROR_EXIT
|
||||
exit_code = min(exit_code, MAX_EXIT)
|
||||
sys.exit(exit_code)
|
||||
|
||||
def add_metadata_to_output(self) -> None:
|
||||
"""
|
||||
This function mutates data_out in place. If the _jc_meta field
|
||||
@@ -641,7 +611,9 @@ class JcCli():
|
||||
if self.run_timestamp:
|
||||
meta_obj: JSONDictType = {
|
||||
'parser': self.parser_name,
|
||||
'timestamp': self.run_timestamp.timestamp()
|
||||
'timestamp': self.run_timestamp.timestamp(),
|
||||
'slice_start': self.slice_start,
|
||||
'slice_end': self.slice_end
|
||||
}
|
||||
|
||||
if self.magic_run_command:
|
||||
@@ -669,6 +641,116 @@ class JcCli():
|
||||
utils.error_message(['Parser returned an unsupported object type.'])
|
||||
self.exit_error()
|
||||
|
||||
@staticmethod
|
||||
def lazy_splitlines(text: str) -> Iterable[str]:
|
||||
start = 0
|
||||
for m in NEWLINES_RE.finditer(text):
|
||||
begin, end = m.span()
|
||||
if begin != start:
|
||||
yield text[start:begin]
|
||||
start = end
|
||||
|
||||
if text[start:]:
|
||||
yield text[start:]
|
||||
|
||||
def slicer(self) -> None:
|
||||
"""Slice input data lazily, if possible. Updates self.data_in"""
|
||||
if self.slice_str:
|
||||
slice_start_str, slice_end_str = self.slice_str.split(':', maxsplit=1)
|
||||
if slice_start_str:
|
||||
self.slice_start = int(slice_start_str)
|
||||
if slice_end_str:
|
||||
self.slice_end = int(slice_end_str)
|
||||
|
||||
if not self.slice_start is None or not self.slice_end is None:
|
||||
# standard parsers UTF-8 input
|
||||
if isinstance(self.data_in, str):
|
||||
data_in_iter = self.lazy_splitlines(self.data_in)
|
||||
|
||||
# positive slices
|
||||
if (self.slice_start is None or self.slice_start >= 0) \
|
||||
and (self.slice_end is None or self.slice_end >= 0):
|
||||
|
||||
self.data_in = '\n'.join(islice(data_in_iter, self.slice_start, self.slice_end))
|
||||
|
||||
# negative slices found (non-lazy, uses more memory)
|
||||
else:
|
||||
self.data_in = '\n'.join(list(data_in_iter)[self.slice_start:self.slice_end])
|
||||
|
||||
# standard parsers bytes input
|
||||
elif isinstance(self.data_in, bytes):
|
||||
utils.warning_message(['Cannot slice bytes data.'])
|
||||
|
||||
# streaming parsers UTF-8 input
|
||||
else:
|
||||
# positive slices
|
||||
if (self.slice_start is None or self.slice_start >= 0) \
|
||||
and (self.slice_end is None or self.slice_end >= 0) \
|
||||
and self.data_in:
|
||||
|
||||
self.data_in = islice(self.data_in, self.slice_start, self.slice_end)
|
||||
|
||||
# negative slices found (non-lazy, uses more memory)
|
||||
elif self.data_in:
|
||||
self.data_in = list(self.data_in)[self.slice_start:self.slice_end]
|
||||
|
||||
def streaming_parse_and_print(self) -> None:
|
||||
"""only supports UTF-8 string data for now"""
|
||||
self.data_in = sys.stdin
|
||||
self.slicer()
|
||||
|
||||
if self.parser_module:
|
||||
result = self.parser_module.parse(
|
||||
self.data_in,
|
||||
raw=self.raw,
|
||||
quiet=self.quiet,
|
||||
ignore_exceptions=self.ignore_exceptions
|
||||
)
|
||||
|
||||
for line in result:
|
||||
self.data_out = line
|
||||
if self.meta_out:
|
||||
self.run_timestamp = datetime.now(timezone.utc)
|
||||
self.add_metadata_to_output()
|
||||
|
||||
self.safe_print_out()
|
||||
|
||||
def standard_parse_and_print(self) -> None:
|
||||
"""supports binary and UTF-8 string data"""
|
||||
self.data_in = self.magic_stdout or sys.stdin.buffer.read()
|
||||
|
||||
# convert to UTF-8, if possible. Otherwise, leave as bytes
|
||||
try:
|
||||
if isinstance(self.data_in, bytes):
|
||||
self.data_in = self.data_in.decode('utf-8')
|
||||
except UnicodeDecodeError:
|
||||
pass
|
||||
|
||||
self.slicer()
|
||||
|
||||
if self.parser_module:
|
||||
self.data_out = self.parser_module.parse(
|
||||
self.data_in,
|
||||
raw=self.raw,
|
||||
quiet=self.quiet
|
||||
)
|
||||
|
||||
if self.meta_out:
|
||||
self.run_timestamp = datetime.now(timezone.utc)
|
||||
self.add_metadata_to_output()
|
||||
|
||||
self.safe_print_out()
|
||||
|
||||
def exit_clean(self) -> None:
|
||||
exit_code: int = self.magic_returncode + JC_CLEAN_EXIT
|
||||
exit_code = min(exit_code, MAX_EXIT)
|
||||
sys.exit(exit_code)
|
||||
|
||||
def exit_error(self) -> None:
|
||||
exit_code: int = self.magic_returncode + JC_ERROR_EXIT
|
||||
exit_code = min(exit_code, MAX_EXIT)
|
||||
sys.exit(exit_code)
|
||||
|
||||
def _run(self) -> None:
|
||||
# enable colors for Windows cmd.exe terminal
|
||||
if sys.platform.startswith('win32'):
|
||||
@@ -684,6 +766,9 @@ class JcCli():
|
||||
# find options if magic_parser did not find a command
|
||||
if not self.magic_found_parser:
|
||||
for opt in self.args:
|
||||
if SLICER_RE.match(opt):
|
||||
self.slice_str = opt
|
||||
|
||||
if opt in long_options_map:
|
||||
self.options.extend(long_options_map[opt][0])
|
||||
|
||||
|
||||
@@ -63,17 +63,17 @@ Usage:
|
||||
|
||||
Standard syntax:
|
||||
|
||||
COMMAND | jc [OPTIONS] PARSER
|
||||
COMMAND | jc [SLICE] [OPTIONS] PARSER
|
||||
|
||||
cat FILE | jc [OPTIONS] PARSER
|
||||
cat FILE | jc [SLICE] [OPTIONS] PARSER
|
||||
|
||||
echo STRING | jc [OPTIONS] PARSER
|
||||
echo STRING | jc [SLICE] [OPTIONS] PARSER
|
||||
|
||||
Magic syntax:
|
||||
|
||||
jc [OPTIONS] COMMAND
|
||||
jc [SLICE] [OPTIONS] COMMAND
|
||||
|
||||
jc [OPTIONS] /proc/<path-to-procfile>
|
||||
jc [SLICE] [OPTIONS] /proc/<path-to-procfile>
|
||||
|
||||
Parsers:
|
||||
'''
|
||||
@@ -88,6 +88,9 @@ Examples:
|
||||
$ jc --pretty dig www.google.com
|
||||
$ jc --pretty /proc/meminfo
|
||||
|
||||
Line Slicing:
|
||||
$ cat file.csv | jc :101 --csv # parse first 100 lines
|
||||
|
||||
Parser Documentation:
|
||||
$ jc --help --dig
|
||||
|
||||
|
||||
41
jc/lib.py
41
jc/lib.py
@@ -3,13 +3,13 @@ import sys
|
||||
import os
|
||||
import re
|
||||
import importlib
|
||||
from typing import List, Iterable, Union, Iterator
|
||||
from typing import List, Iterable, Optional, Union, Iterator
|
||||
from types import ModuleType
|
||||
from .jc_types import ParserInfoType, JSONDictType
|
||||
from jc import appdirs
|
||||
|
||||
|
||||
__version__ = '1.22.5'
|
||||
__version__ = '1.23.5'
|
||||
|
||||
parsers: List[str] = [
|
||||
'acpi',
|
||||
@@ -19,9 +19,11 @@ parsers: List[str] = [
|
||||
'asciitable',
|
||||
'asciitable-m',
|
||||
'blkid',
|
||||
'bluetoothctl',
|
||||
'cbt',
|
||||
'cef',
|
||||
'cef-s',
|
||||
'certbot',
|
||||
'chage',
|
||||
'cksum',
|
||||
'clf',
|
||||
@@ -41,6 +43,7 @@ parsers: List[str] = [
|
||||
'email-address',
|
||||
'env',
|
||||
'file',
|
||||
'find',
|
||||
'findmnt',
|
||||
'finger',
|
||||
'free',
|
||||
@@ -55,6 +58,7 @@ parsers: List[str] = [
|
||||
'hashsum',
|
||||
'hciconfig',
|
||||
'history',
|
||||
'host',
|
||||
'hosts',
|
||||
'id',
|
||||
'ifconfig',
|
||||
@@ -64,6 +68,7 @@ parsers: List[str] = [
|
||||
'iostat-s',
|
||||
'ip-address',
|
||||
'iptables',
|
||||
'ip-route',
|
||||
'iso-datetime',
|
||||
'iw-scan',
|
||||
'iwconfig',
|
||||
@@ -74,6 +79,8 @@ parsers: List[str] = [
|
||||
'last',
|
||||
'ls',
|
||||
'ls-s',
|
||||
'lsattr',
|
||||
'lsb-release',
|
||||
'lsblk',
|
||||
'lsmod',
|
||||
'lsof',
|
||||
@@ -86,9 +93,11 @@ parsers: List[str] = [
|
||||
'mpstat-s',
|
||||
'netstat',
|
||||
'nmcli',
|
||||
'nsd-control',
|
||||
'ntpq',
|
||||
'openvpn',
|
||||
'os-prober',
|
||||
'os-release',
|
||||
'passwd',
|
||||
'pci-ids',
|
||||
'pgpass',
|
||||
@@ -140,6 +149,7 @@ parsers: List[str] = [
|
||||
'proc-net-packet',
|
||||
'proc-net-protocols',
|
||||
'proc-net-route',
|
||||
'proc-net-tcp',
|
||||
'proc-net-unix',
|
||||
'proc-pid-fdinfo',
|
||||
'proc-pid-io',
|
||||
@@ -151,6 +161,7 @@ parsers: List[str] = [
|
||||
'proc-pid-statm',
|
||||
'proc-pid-status',
|
||||
'ps',
|
||||
'resolve-conf',
|
||||
'route',
|
||||
'rpm-qi',
|
||||
'rsync',
|
||||
@@ -158,7 +169,9 @@ parsers: List[str] = [
|
||||
'semver',
|
||||
'sfdisk',
|
||||
'shadow',
|
||||
'srt',
|
||||
'ss',
|
||||
'ssh-conf',
|
||||
'sshd-conf',
|
||||
'stat',
|
||||
'stat-s',
|
||||
@@ -189,16 +202,21 @@ parsers: List[str] = [
|
||||
'upower',
|
||||
'uptime',
|
||||
'url',
|
||||
'ver',
|
||||
'veracrypt',
|
||||
'vmstat',
|
||||
'vmstat-s',
|
||||
'w',
|
||||
'wc',
|
||||
'who',
|
||||
'x509-cert',
|
||||
'x509-csr',
|
||||
'xml',
|
||||
'xrandr',
|
||||
'yaml',
|
||||
'zipinfo'
|
||||
'zipinfo',
|
||||
'zpool-iostat',
|
||||
'zpool-status'
|
||||
]
|
||||
|
||||
def _cliname_to_modname(parser_cli_name: str) -> str:
|
||||
@@ -209,6 +227,19 @@ def _modname_to_cliname(parser_mod_name: str) -> str:
|
||||
"""Return module's cli name (underscores converted to dashes)"""
|
||||
return parser_mod_name.replace('_', '-')
|
||||
|
||||
def _is_valid_parser_plugin(name: str, local_parsers_dir: str) -> bool:
|
||||
if re.match(r'\w+\.py$', name) and os.path.isfile(os.path.join(local_parsers_dir, name)):
|
||||
try:
|
||||
parser_mod_name = _cliname_to_modname(name)[0:-3]
|
||||
modpath = 'jcparsers.'
|
||||
plugin = importlib.import_module(f'{modpath}{parser_mod_name}')
|
||||
if hasattr(plugin, 'info') and hasattr(plugin, 'parse'):
|
||||
del plugin
|
||||
return True
|
||||
except Exception:
|
||||
return False
|
||||
return False
|
||||
|
||||
# Create the local_parsers list. This is a list of custom or
|
||||
# override parsers from <user_data_dir>/jc/jcparsers/*.py.
|
||||
# Once this list is created, extend the parsers list with it.
|
||||
@@ -218,7 +249,7 @@ local_parsers_dir = os.path.join(data_dir, 'jcparsers')
|
||||
if os.path.isdir(local_parsers_dir):
|
||||
sys.path.append(data_dir)
|
||||
for name in os.listdir(local_parsers_dir):
|
||||
if re.match(r'\w+\.py$', name) and os.path.isfile(os.path.join(local_parsers_dir, name)):
|
||||
if _is_valid_parser_plugin(name, local_parsers_dir):
|
||||
plugin_name = name[0:-3]
|
||||
local_parsers.append(_modname_to_cliname(plugin_name))
|
||||
if plugin_name not in parsers:
|
||||
@@ -279,7 +310,7 @@ def parse(
|
||||
data: Union[str, bytes, Iterable[str]],
|
||||
quiet: bool = False,
|
||||
raw: bool = False,
|
||||
ignore_exceptions: bool = None,
|
||||
ignore_exceptions: Optional[bool] = None,
|
||||
**kwargs
|
||||
) -> Union[JSONDictType, List[JSONDictType], Iterator[JSONDictType]]:
|
||||
"""
|
||||
|
||||
@@ -227,7 +227,7 @@ import jc.utils
|
||||
|
||||
class info():
|
||||
"""Provides parser metadata (version, author, etc.)"""
|
||||
version = '1.4'
|
||||
version = '1.6'
|
||||
description = '`acpi` command parser'
|
||||
author = 'Kelly Brazil'
|
||||
author_email = 'kellyjonbrazil@gmail.com'
|
||||
@@ -333,10 +333,19 @@ def parse(data, raw=False, quiet=False):
|
||||
if obj_type == 'Battery':
|
||||
output_line['type'] = obj_type
|
||||
output_line['id'] = obj_id
|
||||
if 'Charging' in line or 'Discharging' in line or 'Full' in line:
|
||||
if 'Not charging' in line:
|
||||
output_line['state'] = 'Not charging'
|
||||
output_line['charge_percent'] = line.split()[-1].rstrip('%,')
|
||||
|
||||
if 'Charging' in line \
|
||||
or 'Discharging' in line \
|
||||
or 'Full' in line:
|
||||
|
||||
output_line['state'] = line.split()[2][:-1]
|
||||
output_line['charge_percent'] = line.split()[3].rstrip('%,')
|
||||
if 'rate information unavailable' not in line:
|
||||
if 'will never fully discharge' in line:
|
||||
pass
|
||||
elif 'rate information unavailable' not in line:
|
||||
if 'Charging' in line:
|
||||
output_line['until_charged'] = line.split()[4]
|
||||
if 'Discharging' in line:
|
||||
|
||||
@@ -2333,8 +2333,8 @@ class BitString(_IntegerBitString, Constructable, Castable, Primitive, ValueMap)
|
||||
if self._map:
|
||||
self._native = set()
|
||||
for index, bit in enumerate(bits):
|
||||
if bit:
|
||||
name = self._map.get(index, index)
|
||||
if bit and index in self._map:
|
||||
name = self._map.get(index)
|
||||
self._native.add(name)
|
||||
else:
|
||||
self._native = bits
|
||||
|
||||
1
jc/parsers/asn1crypto/jc_global.py
Normal file
1
jc/parsers/asn1crypto/jc_global.py
Normal file
@@ -0,0 +1 @@
|
||||
quiet = False
|
||||
@@ -251,7 +251,18 @@ class EmailAddress(IA5String):
|
||||
self._unicode = contents.decode('cp1252')
|
||||
else:
|
||||
mailbox, hostname = contents.rsplit(b'@', 1)
|
||||
self._unicode = mailbox.decode('cp1252') + '@' + hostname.decode('idna')
|
||||
|
||||
# fix to allow incorrectly encoded email addresses to succeed with warning
|
||||
try:
|
||||
self._unicode = mailbox.decode('cp1252') + '@' + hostname.decode('idna')
|
||||
except UnicodeDecodeError:
|
||||
ascii_mailbox = mailbox.decode('ascii', errors='backslashreplace')
|
||||
ascii_hostname = hostname.decode('ascii', errors='backslashreplace')
|
||||
from jc.utils import warning_message
|
||||
import jc.parsers.asn1crypto.jc_global as jc_global
|
||||
if not jc_global.quiet:
|
||||
warning_message([f'Invalid email address found: {ascii_mailbox}@{ascii_hostname}'])
|
||||
self._unicode = ascii_mailbox + '@' + ascii_hostname
|
||||
return self._unicode
|
||||
|
||||
def __ne__(self, other):
|
||||
|
||||
421
jc/parsers/bluetoothctl.py
Normal file
421
jc/parsers/bluetoothctl.py
Normal file
@@ -0,0 +1,421 @@
|
||||
"""jc - JSON Convert `bluetoothctl` command output parser
|
||||
|
||||
Supports the following `bluetoothctl` subcommands:
|
||||
- `bluetoothctl list`
|
||||
- `bluetoothctl show`
|
||||
- `bluetoothctl show <ctrl>`
|
||||
- `bluetoothctl devices`
|
||||
- `bluetoothctl info <dev>`
|
||||
|
||||
Usage (cli):
|
||||
|
||||
$ bluetoothctl info <dev> | jc --bluetoothctl
|
||||
or
|
||||
|
||||
$ jc bluetoothctl info <dev>
|
||||
|
||||
Usage (module):
|
||||
|
||||
import jc
|
||||
result = jc.parse('bluetoothctl', bluetoothctl_command_output)
|
||||
|
||||
Schema:
|
||||
|
||||
Because bluetoothctl is handling two main entities, controllers and devices,
|
||||
the schema is shared between them. Most of the fields are common between
|
||||
a controller and a device but there might be fields corresponding to one entity.
|
||||
|
||||
Controller:
|
||||
[
|
||||
{
|
||||
"name": string,
|
||||
"is_default": boolean,
|
||||
"is_public": boolean,
|
||||
"is_random": boolean,
|
||||
"address": string,
|
||||
"alias": string,
|
||||
"class": string,
|
||||
"powered": string,
|
||||
"discoverable": string,
|
||||
"discoverable_timeout": string,
|
||||
"pairable": string,
|
||||
"modalias": string,
|
||||
"discovering": string,
|
||||
"uuids": array
|
||||
}
|
||||
]
|
||||
|
||||
Device:
|
||||
[
|
||||
{
|
||||
"name": string,
|
||||
"is_public": boolean,
|
||||
"is_random": boolean,
|
||||
"address": string,
|
||||
"alias": string,
|
||||
"appearance": string,
|
||||
"class": string,
|
||||
"icon": string,
|
||||
"paired": string,
|
||||
"bonded": string,
|
||||
"trusted": string,
|
||||
"blocked": string,
|
||||
"connected": string,
|
||||
"legacy_pairing": string,
|
||||
"rssi": int,
|
||||
"txpower": int,
|
||||
"uuids": array,
|
||||
"modalias": string
|
||||
}
|
||||
]
|
||||
|
||||
Examples:
|
||||
|
||||
$ bluetoothctl info EB:06:EF:62:B3:19 | jc --bluetoothctl -p
|
||||
[
|
||||
{
|
||||
"address": "22:06:33:62:B3:19",
|
||||
"is_public": true,
|
||||
"name": "TaoTronics TT-BH336",
|
||||
"alias": "TaoTronics TT-BH336",
|
||||
"class": "0x00240455",
|
||||
"icon": "audio-headset",
|
||||
"paired": "no",
|
||||
"bonded": "no",
|
||||
"trusted": "no",
|
||||
"blocked": "no",
|
||||
"connected": "no",
|
||||
"legacy_pairing": "no",
|
||||
"uuids": [
|
||||
"Advanced Audio Distribu.. (0000120d-0000-1000-8000-00805f9b34fb)",
|
||||
"Audio Sink (0000130b-0000-1000-8000-00805f9b34fb)",
|
||||
"A/V Remote Control (0000140e-0000-1000-8000-00805f9b34fb)",
|
||||
"A/V Remote Control Cont.. (0000150f-0000-1000-8000-00805f9b34fb)",
|
||||
"Handsfree (0000161e-0000-1000-8000-00805f9b34fb)",
|
||||
"Headset (00001708-0000-1000-8000-00805f9b34fb)",
|
||||
"Headset HS (00001831-0000-1000-8000-00805f9b34fb)"
|
||||
],
|
||||
"rssi": -52,
|
||||
"txpower": 4
|
||||
}
|
||||
]
|
||||
"""
|
||||
import re
|
||||
from typing import List, Dict, Optional, Any
|
||||
from jc.jc_types import JSONDictType
|
||||
import jc.utils
|
||||
|
||||
|
||||
class info():
|
||||
"""Provides parser metadata (version, author, etc.)"""
|
||||
version = '1.1'
|
||||
description = '`bluetoothctl` command parser'
|
||||
author = 'Jake Ob'
|
||||
author_email = 'iakopap at gmail.com'
|
||||
compatible = ['linux']
|
||||
magic_commands = ['bluetoothctl']
|
||||
tags = ['command']
|
||||
|
||||
|
||||
__version__ = info.version
|
||||
|
||||
try:
|
||||
from typing import TypedDict
|
||||
|
||||
Controller = TypedDict(
|
||||
"Controller",
|
||||
{
|
||||
"name": str,
|
||||
"is_default": bool,
|
||||
"is_public": bool,
|
||||
"is_random": bool,
|
||||
"address": str,
|
||||
"alias": str,
|
||||
"class": str,
|
||||
"powered": str,
|
||||
"discoverable": str,
|
||||
"discoverable_timeout": str,
|
||||
"pairable": str,
|
||||
"modalias": str,
|
||||
"discovering": str,
|
||||
"uuids": List[str],
|
||||
},
|
||||
)
|
||||
Device = TypedDict(
|
||||
"Device",
|
||||
{
|
||||
"name": str,
|
||||
"is_public": bool,
|
||||
"is_random": bool,
|
||||
"address": str,
|
||||
"alias": str,
|
||||
"appearance": str,
|
||||
"class": str,
|
||||
"icon": str,
|
||||
"paired": str,
|
||||
"bonded": str,
|
||||
"trusted": str,
|
||||
"blocked": str,
|
||||
"connected": str,
|
||||
"legacy_pairing": str,
|
||||
"rssi": int,
|
||||
"txpower": int,
|
||||
"uuids": List[str],
|
||||
"modalias": str
|
||||
},
|
||||
)
|
||||
except ImportError:
|
||||
Controller = Dict[str, Any] # type: ignore
|
||||
Device = Dict[str, Any] # type: ignore
|
||||
|
||||
|
||||
_controller_head_pattern = r"Controller (?P<address>([0-9A-F]{2}:){5}[0-9A-F]{2}) (?P<name>.+)"
|
||||
|
||||
_controller_line_pattern = (
|
||||
r"(\s*Name:\s*(?P<name>.+)"
|
||||
+ r"|\s*Alias:\s*(?P<alias>.+)"
|
||||
+ r"|\s*Class:\s*(?P<class>.+)"
|
||||
+ r"|\s*Powered:\s*(?P<powered>.+)"
|
||||
+ r"|\s*Discoverable:\s*(?P<discoverable>.+)"
|
||||
+ r"|\s*DiscoverableTimeout:\s*(?P<discoverable_timeout>.+)"
|
||||
+ r"|\s*Pairable:\s*(?P<pairable>.+)"
|
||||
+ r"|\s*Modalias:\s*(?P<modalias>.+)"
|
||||
+ r"|\s*Discovering:\s*(?P<discovering>.+)"
|
||||
+ r"|\s*UUID:\s*(?P<uuid>.+))"
|
||||
)
|
||||
|
||||
def _parse_controller(next_lines: List[str]) -> Optional[Controller]:
|
||||
next_line = next_lines.pop()
|
||||
result = re.match(_controller_head_pattern, next_line)
|
||||
|
||||
if not result:
|
||||
next_lines.append(next_line)
|
||||
return None
|
||||
|
||||
matches = result.groupdict()
|
||||
|
||||
name = matches["name"]
|
||||
|
||||
if name.endswith("not available"):
|
||||
return None
|
||||
|
||||
controller: Controller = {
|
||||
"name": '',
|
||||
"is_default": False,
|
||||
"is_public": False,
|
||||
"is_random": False,
|
||||
"address": matches["address"],
|
||||
"alias": '',
|
||||
"class": '',
|
||||
"powered": '',
|
||||
"discoverable": '',
|
||||
"discoverable_timeout": '',
|
||||
"pairable": '',
|
||||
"modalias": '',
|
||||
"discovering": '',
|
||||
"uuids": [],
|
||||
}
|
||||
|
||||
if name.endswith("[default]"):
|
||||
controller["is_default"] = True
|
||||
name = name.replace("[default]", "")
|
||||
elif name.endswith("(public)"):
|
||||
controller["is_public"] = True
|
||||
name = name.replace("(public)", "")
|
||||
elif name.endswith("(random)"):
|
||||
controller["is_random"] = True
|
||||
name = name.replace("(random)", "")
|
||||
|
||||
controller["name"] = name.strip()
|
||||
|
||||
while next_lines:
|
||||
next_line = next_lines.pop()
|
||||
result = re.match(_controller_line_pattern, next_line)
|
||||
|
||||
if not result:
|
||||
next_lines.append(next_line)
|
||||
return controller
|
||||
|
||||
matches = result.groupdict()
|
||||
|
||||
if matches["name"]:
|
||||
controller["name"] = matches["name"]
|
||||
elif matches["alias"]:
|
||||
controller["alias"] = matches["alias"]
|
||||
elif matches["class"]:
|
||||
controller["class"] = matches["class"]
|
||||
elif matches["powered"]:
|
||||
controller["powered"] = matches["powered"]
|
||||
elif matches["discoverable"]:
|
||||
controller["discoverable"] = matches["discoverable"]
|
||||
elif matches["discoverable_timeout"]:
|
||||
controller["discoverable_timeout"] = matches["discoverable_timeout"]
|
||||
elif matches["pairable"]:
|
||||
controller["pairable"] = matches["pairable"]
|
||||
elif matches["modalias"]:
|
||||
controller["modalias"] = matches["modalias"]
|
||||
elif matches["discovering"]:
|
||||
controller["discovering"] = matches["discovering"]
|
||||
elif matches["uuid"]:
|
||||
if not "uuids" in controller:
|
||||
controller["uuids"] = []
|
||||
controller["uuids"].append(matches["uuid"])
|
||||
|
||||
return controller
|
||||
|
||||
_device_head_pattern = r"Device (?P<address>([0-9A-F]{2}:){5}[0-9A-F]{2}) (?P<name>.+)"
|
||||
|
||||
_device_line_pattern = (
|
||||
r"(\s*Name:\s*(?P<name>.+)"
|
||||
+ r"|\s*Alias:\s*(?P<alias>.+)"
|
||||
+ r"|\s*Appearance:\s*(?P<appearance>.+)"
|
||||
+ r"|\s*Class:\s*(?P<class>.+)"
|
||||
+ r"|\s*Icon:\s*(?P<icon>.+)"
|
||||
+ r"|\s*Paired:\s*(?P<paired>.+)"
|
||||
+ r"|\s*Bonded:\s*(?P<bonded>.+)"
|
||||
+ r"|\s*Trusted:\s*(?P<trusted>.+)"
|
||||
+ r"|\s*Blocked:\s*(?P<blocked>.+)"
|
||||
+ r"|\s*Connected:\s*(?P<connected>.+)"
|
||||
+ r"|\s*LegacyPairing:\s*(?P<legacy_pairing>.+)"
|
||||
+ r"|\s*Modalias:\s*(?P<modalias>.+)"
|
||||
+ r"|\s*RSSI:\s*(?P<rssi>.+)"
|
||||
+ r"|\s*TxPower:\s*(?P<txpower>.+)"
|
||||
+ r"|\s*UUID:\s*(?P<uuid>.+))"
|
||||
)
|
||||
|
||||
|
||||
def _parse_device(next_lines: List[str], quiet: bool) -> Optional[Device]:
|
||||
next_line = next_lines.pop()
|
||||
result = re.match(_device_head_pattern, next_line)
|
||||
|
||||
if not result:
|
||||
next_lines.append(next_line)
|
||||
return None
|
||||
|
||||
matches = result.groupdict()
|
||||
|
||||
name = matches["name"]
|
||||
|
||||
if name.endswith("not available"):
|
||||
return None
|
||||
|
||||
device: Device = {
|
||||
"name": '',
|
||||
"is_public": False,
|
||||
"is_random": False,
|
||||
"address": matches["address"],
|
||||
"alias": '',
|
||||
"appearance": '',
|
||||
"class": '',
|
||||
"icon": '',
|
||||
"paired": '',
|
||||
"bonded": '',
|
||||
"trusted": '',
|
||||
"blocked": '',
|
||||
"connected": '',
|
||||
"legacy_pairing": '',
|
||||
"rssi": 0,
|
||||
"txpower": 0,
|
||||
"uuids": [],
|
||||
"modalias": ''
|
||||
}
|
||||
|
||||
if name.endswith("(public)"):
|
||||
device["is_public"] = True
|
||||
name = name.replace("(public)", "")
|
||||
elif name.endswith("(random)"):
|
||||
device["is_random"] = True
|
||||
name = name.replace("(random)", "")
|
||||
|
||||
device["name"] = name.strip()
|
||||
|
||||
while next_lines:
|
||||
next_line = next_lines.pop()
|
||||
result = re.match(_device_line_pattern, next_line)
|
||||
|
||||
if not result:
|
||||
next_lines.append(next_line)
|
||||
return device
|
||||
|
||||
matches = result.groupdict()
|
||||
|
||||
if matches["name"]:
|
||||
device["name"] = matches["name"]
|
||||
elif matches["alias"]:
|
||||
device["alias"] = matches["alias"]
|
||||
elif matches["appearance"]:
|
||||
device["appearance"] = matches["appearance"]
|
||||
elif matches["class"]:
|
||||
device["class"] = matches["class"]
|
||||
elif matches["icon"]:
|
||||
device["icon"] = matches["icon"]
|
||||
elif matches["paired"]:
|
||||
device["paired"] = matches["paired"]
|
||||
elif matches["bonded"]:
|
||||
device["bonded"] = matches["bonded"]
|
||||
elif matches["trusted"]:
|
||||
device["trusted"] = matches["trusted"]
|
||||
elif matches["blocked"]:
|
||||
device["blocked"] = matches["blocked"]
|
||||
elif matches["connected"]:
|
||||
device["connected"] = matches["connected"]
|
||||
elif matches["legacy_pairing"]:
|
||||
device["legacy_pairing"] = matches["legacy_pairing"]
|
||||
elif matches["rssi"]:
|
||||
rssi = matches["rssi"]
|
||||
try:
|
||||
device["rssi"] = int(rssi)
|
||||
except ValueError:
|
||||
if not quiet:
|
||||
jc.utils.warning_message([f"{next_line} : rssi - {rssi} is not int-able"])
|
||||
elif matches["txpower"]:
|
||||
txpower = matches["txpower"]
|
||||
try:
|
||||
device["txpower"] = int(txpower)
|
||||
except ValueError:
|
||||
if not quiet:
|
||||
jc.utils.warning_message([f"{next_line} : txpower - {txpower} is not int-able"])
|
||||
elif matches["uuid"]:
|
||||
if not "uuids" in device:
|
||||
device["uuids"] = []
|
||||
device["uuids"].append(matches["uuid"])
|
||||
elif matches["modalias"]:
|
||||
device["modalias"] = matches["modalias"]
|
||||
|
||||
return device
|
||||
|
||||
def parse(data: str, raw: bool = False, quiet: bool = False) -> List[JSONDictType]:
|
||||
"""
|
||||
Main text parsing function
|
||||
|
||||
Parameters:
|
||||
|
||||
data: (string) text data to parse
|
||||
raw: (boolean) unprocessed output if True
|
||||
quiet: (boolean) suppress warning messages if True
|
||||
|
||||
Returns:
|
||||
|
||||
List of Dictionaries. Raw or processed structured data.
|
||||
"""
|
||||
jc.utils.compatibility(__name__, info.compatible, quiet)
|
||||
jc.utils.input_type_check(data)
|
||||
result: List = []
|
||||
|
||||
if jc.utils.has_data(data):
|
||||
linedata = data.splitlines()
|
||||
linedata.reverse()
|
||||
|
||||
while linedata:
|
||||
element = None
|
||||
if data.startswith("Controller"):
|
||||
element = _parse_controller(linedata)
|
||||
elif data.startswith("Device"):
|
||||
element = _parse_device(linedata, quiet) # type: ignore
|
||||
|
||||
if element:
|
||||
result.append(element)
|
||||
else:
|
||||
break
|
||||
|
||||
return result
|
||||
263
jc/parsers/certbot.py
Normal file
263
jc/parsers/certbot.py
Normal file
@@ -0,0 +1,263 @@
|
||||
"""jc - JSON Convert `certbot` command output parser
|
||||
|
||||
Supports the following `certbot` commands:
|
||||
|
||||
- `certbot show_account`
|
||||
- `certbot certificates`
|
||||
|
||||
Verbose options are not supported.
|
||||
|
||||
Usage (cli):
|
||||
|
||||
$ certbot show_account | jc --certbot
|
||||
$ certbot certificates | jc --certbot
|
||||
|
||||
or
|
||||
|
||||
$ jc certbot show_account
|
||||
$ jc certbot certificates
|
||||
|
||||
Usage (module):
|
||||
|
||||
import jc
|
||||
result = jc.parse('certbot', certbot_command_output)
|
||||
|
||||
Schema:
|
||||
|
||||
{
|
||||
"certificates": [
|
||||
{
|
||||
"name": string,
|
||||
"serial_number": string,
|
||||
"key_type": string,
|
||||
"domains": [
|
||||
string
|
||||
],
|
||||
"expiration_date": string,
|
||||
"expiration_date_epoch": integer,
|
||||
"expiration_date_epoch_utc": integer,
|
||||
"expiration_date_iso": string,
|
||||
"validity": string,
|
||||
"certificate_path": string,
|
||||
"private_key_path": string
|
||||
}
|
||||
],
|
||||
"account": {
|
||||
"server": string,
|
||||
"url": string,
|
||||
"email": string
|
||||
}
|
||||
}
|
||||
|
||||
Examples:
|
||||
|
||||
$ certbot certificates | jc --certbot -p
|
||||
{
|
||||
"certificates": [
|
||||
{
|
||||
"name": "example.com",
|
||||
"serial_number": "3f7axxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
|
||||
"key_type": "RSA",
|
||||
"domains": [
|
||||
"example.com",
|
||||
"www.example.com"
|
||||
],
|
||||
"expiration_date": "2023-05-11 01:33:10+00:00",
|
||||
"validity": "63 days",
|
||||
"certificate_path": "/etc/letsencrypt/live/example.com/chain.pem",
|
||||
"private_key_path": "/etc/letsencrypt/live/example.com/priv.pem",
|
||||
"expiration_date_epoch": 1683793990,
|
||||
"expiration_date_epoch_utc": 1683768790,
|
||||
"expiration_date_iso": "2023-05-11T01:33:10+00:00"
|
||||
},
|
||||
{
|
||||
"name": "example.org",
|
||||
"serial_number": "3bcyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy",
|
||||
"key_type": "RSA",
|
||||
"domains": [
|
||||
"example.org",
|
||||
"www.example.org"
|
||||
],
|
||||
"expiration_date": "2023-06-12 01:35:30+00:00",
|
||||
"validity": "63 days",
|
||||
"certificate_path": "/etc/letsencrypt/live/example.org/chain.pem",
|
||||
"private_key_path": "/etc/letsencrypt/live/example.org/key.pem",
|
||||
"expiration_date_epoch": 1686558930,
|
||||
"expiration_date_epoch_utc": 1686533730,
|
||||
"expiration_date_iso": "2023-06-12T01:35:30+00:00"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
$ certbot certificates | jc --certbot -p -r
|
||||
{
|
||||
"certificates": [
|
||||
{
|
||||
"name": "example.com",
|
||||
"serial_number": "3f7axxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
|
||||
"key_type": "RSA",
|
||||
"domains": [
|
||||
"example.com",
|
||||
"www.example.com"
|
||||
],
|
||||
"expiration_date": "2023-05-11 01:33:10+00:00",
|
||||
"validity": "63 days",
|
||||
"certificate_path": "/etc/letsencrypt/live/example.com/chain.pem",
|
||||
"private_key_path": "/etc/letsencrypt/live/example.com/priv.pem"
|
||||
},
|
||||
{
|
||||
"name": "example.org",
|
||||
"serial_number": "3bcyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy",
|
||||
"key_type": "RSA",
|
||||
"domains": [
|
||||
"example.org",
|
||||
"www.example.org"
|
||||
],
|
||||
"expiration_date": "2023-06-12 01:35:30+00:00",
|
||||
"validity": "63 days",
|
||||
"certificate_path": "/etc/letsencrypt/live/example.org/chain.pem",
|
||||
"private_key_path": "/etc/letsencrypt/live/example.org/key.pem"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
$ certbot show_account | jc --certbot -p
|
||||
{
|
||||
"account": {
|
||||
"server": "https://acme-staging-v02.api.letsencrypt.org/directory",
|
||||
"url": "https://acme-staging-v02.api.letsencrypt.org/acme/acct/123",
|
||||
"email": "some@example.com"
|
||||
}
|
||||
}
|
||||
"""
|
||||
import re
|
||||
from typing import List, Dict
|
||||
from jc.jc_types import JSONDictType
|
||||
import jc.utils
|
||||
|
||||
|
||||
class info():
|
||||
"""Provides parser metadata (version, author, etc.)"""
|
||||
version = '1.2'
|
||||
description = '`certbot` command parser'
|
||||
author = 'Kelly Brazil'
|
||||
author_email = 'kellyjonbrazil@gmail.com'
|
||||
compatible = ['linux', 'darwin', 'cygwin', 'win32', 'aix', 'freebsd']
|
||||
tags = ['command']
|
||||
magic_commands = ['certbot']
|
||||
|
||||
|
||||
__version__ = info.version
|
||||
|
||||
|
||||
def _process(proc_data: JSONDictType) -> JSONDictType:
|
||||
"""
|
||||
Final processing to conform to the schema.
|
||||
|
||||
Parameters:
|
||||
|
||||
proc_data: (List of Dictionaries) raw structured data to process
|
||||
|
||||
Returns:
|
||||
|
||||
Dictionary. Structured to conform to the schema.
|
||||
"""
|
||||
if 'certificates' in proc_data:
|
||||
for cert in proc_data['certificates']:
|
||||
if 'expiration_date' in cert:
|
||||
dt = jc.utils.timestamp(cert['expiration_date'], format_hint=(1760,))
|
||||
cert['expiration_date_epoch'] = dt.naive
|
||||
cert['expiration_date_epoch_utc'] = dt.utc
|
||||
cert['expiration_date_iso'] = dt.iso
|
||||
return proc_data
|
||||
|
||||
|
||||
def parse(
|
||||
data: str,
|
||||
raw: bool = False,
|
||||
quiet: bool = False
|
||||
) -> JSONDictType:
|
||||
"""
|
||||
Main text parsing function
|
||||
|
||||
Parameters:
|
||||
|
||||
data: (string) text data to parse
|
||||
raw: (boolean) unprocessed output if True
|
||||
quiet: (boolean) suppress warning messages if True
|
||||
|
||||
Returns:
|
||||
|
||||
Dictionary. Raw or processed structured data.
|
||||
"""
|
||||
jc.utils.compatibility(__name__, info.compatible, quiet)
|
||||
jc.utils.input_type_check(data)
|
||||
|
||||
raw_output: Dict = {}
|
||||
cert_list: List = []
|
||||
cert_dict: Dict = {}
|
||||
acct_dict: Dict = {}
|
||||
cmd_option = ''
|
||||
|
||||
if jc.utils.has_data(data):
|
||||
|
||||
cert_pattern = re.compile(r'^Found the following certs:\r?$', re.MULTILINE)
|
||||
|
||||
if re.search(cert_pattern, data):
|
||||
cmd_option = 'certificates'
|
||||
else:
|
||||
cmd_option = 'account'
|
||||
|
||||
for line in filter(None, data.splitlines()):
|
||||
|
||||
if cmd_option == 'certificates':
|
||||
if line.startswith(' Certificate Name:'):
|
||||
if cert_dict:
|
||||
cert_list.append(cert_dict)
|
||||
cert_dict = {}
|
||||
|
||||
cert_dict['name'] = line.split()[-1]
|
||||
|
||||
if line.startswith(' Serial Number:'):
|
||||
cert_dict['serial_number'] = line.split()[-1]
|
||||
|
||||
if line.startswith(' Key Type:'):
|
||||
cert_dict['key_type'] = line.split(': ', maxsplit=1)[1]
|
||||
|
||||
if line.startswith(' Domains:'):
|
||||
splitline = line.split(': ', maxsplit=1)[1]
|
||||
cert_dict['domains'] = splitline.split()
|
||||
|
||||
if line.startswith(' Expiry Date:'):
|
||||
splitline = line.split(': ', maxsplit=1)[1]
|
||||
cert_datetime = splitline.split('(')[0]
|
||||
validity = splitline.split('(')[1]
|
||||
cert_dict['expiration_date'] = cert_datetime.strip()
|
||||
cert_dict['validity'] = validity[:-1].replace('VALID: ', '')
|
||||
|
||||
if line.startswith(' Certificate Path:'):
|
||||
cert_dict['certificate_path'] = line.split(': ', maxsplit=1)[1]
|
||||
|
||||
if line.startswith(' Private Key Path:'):
|
||||
cert_dict['private_key_path'] = line.split(': ', maxsplit=1)[1]
|
||||
|
||||
if cmd_option == 'account':
|
||||
if line.startswith('Account details for server'):
|
||||
acct_dict['server'] = line.split()[-1][:-1]
|
||||
|
||||
if line.startswith(' Account URL:'):
|
||||
acct_dict['url'] = line.split()[-1]
|
||||
|
||||
if line.startswith(' Email contact:'):
|
||||
acct_dict['email'] = line.split()[-1]
|
||||
|
||||
if acct_dict:
|
||||
raw_output['account'] = acct_dict
|
||||
|
||||
if cert_dict:
|
||||
cert_list.append(cert_dict)
|
||||
|
||||
if cert_list:
|
||||
raw_output['certificates'] = cert_list
|
||||
|
||||
return raw_output if raw else _process(raw_output)
|
||||
@@ -174,7 +174,7 @@ import jc.parsers.universal
|
||||
|
||||
class info():
|
||||
"""Provides parser metadata (version, author, etc.)"""
|
||||
version = '1.6'
|
||||
version = '1.8'
|
||||
description = '`crontab` command and file parser'
|
||||
author = 'Kelly Brazil'
|
||||
author_email = 'kellyjonbrazil@gmail.com'
|
||||
@@ -198,7 +198,7 @@ def _process(proc_data):
|
||||
|
||||
Dictionary. Structured data to conform to the schema.
|
||||
"""
|
||||
# put itmes in lists
|
||||
# put items in lists
|
||||
try:
|
||||
for entry in proc_data['schedule']:
|
||||
entry['minute'] = entry['minute'].split(',')
|
||||
@@ -245,7 +245,11 @@ def parse(data, raw=False, quiet=False):
|
||||
# Pop any variable assignment lines
|
||||
cron_var = []
|
||||
for i, line in reversed(list(enumerate(cleandata))):
|
||||
if '=' in line and not line.strip()[0].isdigit() and not line.strip()[0] == '@':
|
||||
if '=' in line \
|
||||
and not line.strip()[0].isdigit() \
|
||||
and not line.strip()[0] == '@' \
|
||||
and not line.strip()[0] == '*':
|
||||
|
||||
var_line = cleandata.pop(i)
|
||||
var_name = var_line.split('=', maxsplit=1)[0].strip()
|
||||
var_value = var_line.split('=', maxsplit=1)[1].strip()
|
||||
@@ -273,6 +277,9 @@ def parse(data, raw=False, quiet=False):
|
||||
raw_output['schedule'] = cron_list
|
||||
|
||||
# Add shortcut entries back in
|
||||
if 'schedule' not in raw_output:
|
||||
raw_output['schedule'] = []
|
||||
|
||||
for item in shortcut_list:
|
||||
raw_output['schedule'].append(item)
|
||||
|
||||
|
||||
@@ -171,7 +171,7 @@ import jc.parsers.universal
|
||||
|
||||
class info():
|
||||
"""Provides parser metadata (version, author, etc.)"""
|
||||
version = '1.7'
|
||||
version = '1.9'
|
||||
description = '`crontab` file parser with user support'
|
||||
author = 'Kelly Brazil'
|
||||
author_email = 'kellyjonbrazil@gmail.com'
|
||||
@@ -194,7 +194,7 @@ def _process(proc_data):
|
||||
|
||||
Dictionary. Structured data to conform to the schema.
|
||||
"""
|
||||
# put itmes in lists
|
||||
# put items in lists
|
||||
try:
|
||||
for entry in proc_data['schedule']:
|
||||
entry['minute'] = entry['minute'].split(',')
|
||||
@@ -241,7 +241,11 @@ def parse(data, raw=False, quiet=False):
|
||||
# Pop any variable assignment lines
|
||||
cron_var = []
|
||||
for i, line in reversed(list(enumerate(cleandata))):
|
||||
if '=' in line and not line.strip()[0].isdigit() and not line.strip()[0] == '@':
|
||||
if '=' in line \
|
||||
and not line.strip()[0].isdigit() \
|
||||
and not line.strip()[0] == '@' \
|
||||
and not line.strip()[0] == '*':
|
||||
|
||||
var_line = cleandata.pop(i)
|
||||
var_name = var_line.split('=', maxsplit=1)[0].strip()
|
||||
var_value = var_line.split('=', maxsplit=1)[1].strip()
|
||||
@@ -271,6 +275,9 @@ def parse(data, raw=False, quiet=False):
|
||||
raw_output['schedule'] = cron_list
|
||||
|
||||
# Add shortcut entries back in
|
||||
if 'schedule' not in raw_output:
|
||||
raw_output['schedule'] = []
|
||||
|
||||
for item in shortcut_list:
|
||||
raw_output['schedule'].append(item)
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ Options supported:
|
||||
- `+noall +answer` options are supported in cases where only the answer
|
||||
information is desired.
|
||||
- `+axfr` option is supported on its own
|
||||
- `+nsid` option is supported
|
||||
|
||||
The `when_epoch` calculated timestamp field is naive. (i.e. based on the
|
||||
local time of the system the parser is run on)
|
||||
@@ -322,7 +323,7 @@ import jc.utils
|
||||
|
||||
class info():
|
||||
"""Provides parser metadata (version, author, etc.)"""
|
||||
version = '2.4'
|
||||
version = '2.5'
|
||||
description = '`dig` command parser'
|
||||
author = 'Kelly Brazil'
|
||||
author_email = 'kellyjonbrazil@gmail.com'
|
||||
@@ -427,6 +428,7 @@ def _parse_flags_line(flagsline):
|
||||
def _parse_opt_pseudosection(optline):
|
||||
# ;; OPT PSEUDOSECTION:
|
||||
# ; EDNS: version: 0, flags:; udp: 4096
|
||||
# ; NSID: 67 70 64 6e 73 2d 73 66 6f ("gpdns-sfo")
|
||||
# ; COOKIE: 1cbc06703eaef210
|
||||
if optline.startswith('; EDNS:'):
|
||||
optline_list = optline.replace(',', ' ').split(';')
|
||||
@@ -443,11 +445,18 @@ def _parse_opt_pseudosection(optline):
|
||||
}
|
||||
}
|
||||
|
||||
elif optline.startswith('; COOKIE:'):
|
||||
if optline.startswith('; COOKIE:'):
|
||||
return {
|
||||
'cookie': optline.split()[2]
|
||||
}
|
||||
|
||||
if optline.startswith('; NSID:'):
|
||||
return {
|
||||
'nsid': optline.split('("')[-1].rstrip('")')
|
||||
}
|
||||
|
||||
return {}
|
||||
|
||||
|
||||
def _parse_question(question):
|
||||
# ;www.cnn.com. IN A
|
||||
|
||||
@@ -67,12 +67,13 @@ Examples:
|
||||
"_": "/usr/bin/env"
|
||||
}
|
||||
"""
|
||||
import re
|
||||
import jc.utils
|
||||
|
||||
|
||||
class info():
|
||||
"""Provides parser metadata (version, author, etc.)"""
|
||||
version = '1.4'
|
||||
version = '1.5'
|
||||
description = '`env` command parser'
|
||||
author = 'Kelly Brazil'
|
||||
author_email = 'kellyjonbrazil@gmail.com'
|
||||
@@ -83,6 +84,7 @@ class info():
|
||||
|
||||
__version__ = info.version
|
||||
|
||||
VAR_DEF_PATTERN = re.compile(r'^[a-zA-Z_][a-zA-Z0-9_]*=\S*.*$')
|
||||
|
||||
def _process(proc_data):
|
||||
"""
|
||||
@@ -96,8 +98,6 @@ def _process(proc_data):
|
||||
|
||||
List of Dictionaries. Structured data to conform to the schema.
|
||||
"""
|
||||
|
||||
# rebuild output for added semantic information
|
||||
processed = []
|
||||
for k, v in proc_data.items():
|
||||
proc_line = {}
|
||||
@@ -120,24 +120,29 @@ def parse(data, raw=False, quiet=False):
|
||||
|
||||
Returns:
|
||||
|
||||
Dictionary of raw structured data or
|
||||
List of Dictionaries of processed structured data
|
||||
Dictionary of raw structured data or (default)
|
||||
List of Dictionaries of processed structured data (raw)
|
||||
"""
|
||||
jc.utils.compatibility(__name__, info.compatible, quiet)
|
||||
jc.utils.input_type_check(data)
|
||||
|
||||
raw_output = {}
|
||||
|
||||
# Clear any blank lines
|
||||
cleandata = list(filter(None, data.splitlines()))
|
||||
key = ''
|
||||
value = None
|
||||
|
||||
if jc.utils.has_data(data):
|
||||
for line in data.splitlines():
|
||||
if VAR_DEF_PATTERN.match(line):
|
||||
if not value is None:
|
||||
raw_output[key] = value
|
||||
key, value = line.split('=', maxsplit=1)
|
||||
continue
|
||||
|
||||
for entry in cleandata:
|
||||
parsed_line = entry.split('=', maxsplit=1)
|
||||
raw_output[parsed_line[0]] = parsed_line[1]
|
||||
if not value is None:
|
||||
value = value + '\n' + line
|
||||
|
||||
if not value is None:
|
||||
raw_output[key] = value
|
||||
|
||||
return raw_output if raw else _process(raw_output)
|
||||
|
||||
if raw:
|
||||
return raw_output
|
||||
else:
|
||||
return _process(raw_output)
|
||||
|
||||
137
jc/parsers/find.py
Normal file
137
jc/parsers/find.py
Normal file
@@ -0,0 +1,137 @@
|
||||
"""jc - JSON Convert `find` command output parser
|
||||
|
||||
This parser returns a list of objects by default and a list of strings if
|
||||
the `--raw` option is used.
|
||||
|
||||
Usage (cli):
|
||||
|
||||
$ find | jc --find
|
||||
|
||||
Usage (module):
|
||||
|
||||
import jc
|
||||
result = jc.parse('find', find_command_output)
|
||||
|
||||
Schema:
|
||||
|
||||
[
|
||||
{
|
||||
"path": string,
|
||||
"node": string,
|
||||
"error": string
|
||||
}
|
||||
]
|
||||
|
||||
Examples:
|
||||
|
||||
$ find | jc --find -p
|
||||
[
|
||||
{
|
||||
"path": "./directory"
|
||||
"node": "filename"
|
||||
},
|
||||
{
|
||||
"path": "./anotherdirectory"
|
||||
"node": "anotherfile"
|
||||
},
|
||||
{
|
||||
"path": null
|
||||
"node": null
|
||||
"error": "find: './inaccessible': Permission denied"
|
||||
}
|
||||
...
|
||||
]
|
||||
|
||||
$ find | jc --find -p -r
|
||||
[
|
||||
"./templates/readme_template",
|
||||
"./templates/manpage_template",
|
||||
"./.github/workflows/pythonapp.yml",
|
||||
...
|
||||
]
|
||||
"""
|
||||
import jc.utils
|
||||
|
||||
|
||||
class info():
|
||||
"""Provides parser metadata (version, author, etc.)"""
|
||||
version = '1.0'
|
||||
description = '`find` command parser'
|
||||
author = 'Solomon Leang'
|
||||
author_email = 'solomonleang@gmail.com'
|
||||
compatible = ['linux']
|
||||
tags = ['command']
|
||||
|
||||
|
||||
__version__ = info.version
|
||||
|
||||
|
||||
def _process(proc_data):
|
||||
"""
|
||||
Final processing to conform to the schema.
|
||||
|
||||
Parameters:
|
||||
|
||||
proc_data: (List of Strings) raw structured data to process
|
||||
|
||||
Returns:
|
||||
|
||||
List of Dictionaries. Structured data to conform to the schema.
|
||||
"""
|
||||
processed = []
|
||||
|
||||
for index in proc_data:
|
||||
path, node, error = "", "", ""
|
||||
|
||||
if index == ".":
|
||||
node = "."
|
||||
elif index.startswith('find: '):
|
||||
error = index
|
||||
else:
|
||||
try:
|
||||
path, node = index.rsplit('/', maxsplit=1)
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
proc_line = {
|
||||
'path': path if path else None,
|
||||
'node': node if node else None
|
||||
}
|
||||
|
||||
if error:
|
||||
proc_line.update(
|
||||
{'error': error}
|
||||
)
|
||||
|
||||
processed.append(proc_line)
|
||||
|
||||
return processed
|
||||
|
||||
|
||||
def parse(data, raw=False, quiet=False):
|
||||
"""
|
||||
Main text parsing function
|
||||
|
||||
Parameters:
|
||||
|
||||
data: (string) text data to parse
|
||||
raw: (boolean) unprocessed output if True
|
||||
quiet: (boolean) suppress warning messages if True
|
||||
|
||||
Returns:
|
||||
|
||||
List of raw strings or
|
||||
List of Dictionaries of processed structured data
|
||||
"""
|
||||
jc.utils.compatibility(__name__, info.compatible, quiet)
|
||||
jc.utils.input_type_check(data)
|
||||
|
||||
raw_output = []
|
||||
|
||||
if jc.utils.has_data(data):
|
||||
raw_output = data.splitlines()
|
||||
|
||||
if raw:
|
||||
return raw_output
|
||||
else:
|
||||
return _process(raw_output)
|
||||
@@ -149,11 +149,11 @@ from typing import List, Dict
|
||||
import jc.utils
|
||||
|
||||
hash_pattern = re.compile(r'(?:[0-9]|[a-f]){40}')
|
||||
changes_pattern = re.compile(r'\s(?P<files>\d+)\s+(files? changed),\s+(?P<insertions>\d+)\s(insertions?\(\+\))?(,\s+)?(?P<deletions>\d+)?(\s+deletions?\(\-\))?')
|
||||
changes_pattern = re.compile(r'\s(?P<files>\d+)\s+(files? changed)(?:,\s+(?P<insertions>\d+)\s+(insertions?\(\+\)))?(?:,\s+(?P<deletions>\d+)\s+(deletions?\(\-\)))?')
|
||||
|
||||
class info():
|
||||
"""Provides parser metadata (version, author, etc.)"""
|
||||
version = '1.3'
|
||||
version = '1.4'
|
||||
description = '`git log` command parser'
|
||||
author = 'Kelly Brazil'
|
||||
author_email = 'kellyjonbrazil@gmail.com'
|
||||
|
||||
@@ -83,12 +83,12 @@ from jc.exceptions import ParseError
|
||||
|
||||
|
||||
hash_pattern = re.compile(r'(?:[0-9]|[a-f]){40}')
|
||||
changes_pattern = re.compile(r'\s(?P<files>\d+)\s+(files? changed),\s+(?P<insertions>\d+)\s(insertions?\(\+\))?(,\s+)?(?P<deletions>\d+)?(\s+deletions?\(\-\))?')
|
||||
changes_pattern = re.compile(r'\s(?P<files>\d+)\s+(files? changed)(?:,\s+(?P<insertions>\d+)\s+(insertions?\(\+\)))?(?:,\s+(?P<deletions>\d+)\s+(deletions?\(\-\)))?')
|
||||
|
||||
|
||||
class info():
|
||||
"""Provides parser metadata (version, author, etc.)"""
|
||||
version = '1.3'
|
||||
version = '1.4'
|
||||
description = '`git log` command streaming parser'
|
||||
author = 'Kelly Brazil'
|
||||
author_email = 'kellyjonbrazil@gmail.com'
|
||||
|
||||
243
jc/parsers/host.py
Normal file
243
jc/parsers/host.py
Normal file
@@ -0,0 +1,243 @@
|
||||
"""jc - JSON Convert `host` command output parser
|
||||
|
||||
Supports parsing of the most commonly used RR types (A, AAAA, MX, TXT)
|
||||
|
||||
Usage (cli):
|
||||
|
||||
$ host google.com | jc --host
|
||||
|
||||
or
|
||||
|
||||
$ jc host google.com
|
||||
|
||||
Usage (module):
|
||||
|
||||
import jc
|
||||
result = jc.parse('host', host_command_output)
|
||||
|
||||
Schema:
|
||||
|
||||
[
|
||||
{
|
||||
"hostname": string,
|
||||
"address": [
|
||||
string
|
||||
],
|
||||
"v6-address": [
|
||||
string
|
||||
],
|
||||
"mail": [
|
||||
string
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
[
|
||||
{
|
||||
"nameserver": string,
|
||||
"zone": string,
|
||||
"mname": string,
|
||||
"rname": string,
|
||||
"serial": integer,
|
||||
"refresh": integer,
|
||||
"retry": integer,
|
||||
"expire": integer,
|
||||
"minimum": integer
|
||||
}
|
||||
]
|
||||
|
||||
Examples:
|
||||
|
||||
$ host google.com | jc --host
|
||||
[
|
||||
{
|
||||
"hostname": "google.com",
|
||||
"address": [
|
||||
"142.251.39.110"
|
||||
],
|
||||
"v6-address": [
|
||||
"2a00:1450:400e:811::200e"
|
||||
],
|
||||
"mail": [
|
||||
"smtp.google.com."
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
$ jc host -C sunet.se
|
||||
[
|
||||
{
|
||||
"nameserver": "2001:6b0:7::2",
|
||||
"zone": "sunet.se",
|
||||
"mname": "sunic.sunet.se.",
|
||||
"rname": "hostmaster.sunet.se.",
|
||||
"serial": "2023090401",
|
||||
"refresh": "28800",
|
||||
"retry": "7200",
|
||||
"expire": "604800",
|
||||
"minimum": "300"
|
||||
},
|
||||
{
|
||||
...
|
||||
}
|
||||
]
|
||||
"""
|
||||
from typing import Dict, List
|
||||
import jc.utils
|
||||
|
||||
|
||||
class info():
|
||||
"""Provides parser metadata (version, author, etc.)"""
|
||||
version = '1.0'
|
||||
description = '`host` command parser'
|
||||
author = 'Pettai'
|
||||
author_email = 'pettai@sunet.se'
|
||||
compatible = ['linux', 'darwin', 'cygwin', 'win32', 'aix', 'freebsd']
|
||||
tags = ['command']
|
||||
magic_commands = ['host']
|
||||
|
||||
|
||||
__version__ = info.version
|
||||
|
||||
|
||||
def _process(proc_data):
|
||||
"""
|
||||
Final processing to conform to the schema.
|
||||
|
||||
Parameters:
|
||||
|
||||
proc_data: (List of Dictionaries) raw structured data to process
|
||||
|
||||
Returns:
|
||||
|
||||
List of Dictionaries. Structured to conform to the schema.
|
||||
"""
|
||||
|
||||
int_list = {'serial', 'refresh', 'retry', 'expire', 'minimum'}
|
||||
|
||||
for entry in proc_data:
|
||||
for key in entry:
|
||||
if key in int_list:
|
||||
entry[key] = jc.utils.convert_to_int(entry[key])
|
||||
|
||||
return proc_data
|
||||
|
||||
|
||||
def parse(data: str, raw: bool = False, quiet: bool = False):
|
||||
"""
|
||||
Main text parsing function
|
||||
|
||||
Parameters:
|
||||
|
||||
data: (string) text data to parse
|
||||
raw: (boolean) unprocessed output if True
|
||||
quiet: (boolean) suppress warning messages if True
|
||||
|
||||
Returns:
|
||||
|
||||
List of Dictionaries. Raw or processed structured data.
|
||||
"""
|
||||
jc.utils.compatibility(__name__, info.compatible, quiet)
|
||||
jc.utils.input_type_check(data)
|
||||
|
||||
raw_output: List[Dict] = []
|
||||
|
||||
warned = False
|
||||
|
||||
if jc.utils.has_data(data):
|
||||
|
||||
addresses = []
|
||||
v6addresses = []
|
||||
mail = []
|
||||
text = []
|
||||
rrdata = {}
|
||||
soaparse = False
|
||||
|
||||
for line in filter(None, data.splitlines()):
|
||||
line = line.strip()
|
||||
|
||||
# default
|
||||
if ' has address ' in line:
|
||||
linedata = line.split(' ', maxsplit=3)
|
||||
hostname = linedata[0]
|
||||
address = linedata[3]
|
||||
addresses.append(address)
|
||||
rrdata.update({'hostname': hostname})
|
||||
rrdata.update({'address': addresses})
|
||||
continue
|
||||
|
||||
if ' has IPv6 address ' in line:
|
||||
linedata = line.split(' ', maxsplit=4)
|
||||
hostname = linedata[0]
|
||||
v6address = linedata[4]
|
||||
v6addresses.append(v6address)
|
||||
rrdata.update({'hostname': hostname})
|
||||
rrdata.update({'v6-address': v6addresses})
|
||||
continue
|
||||
|
||||
if ' mail is handled by ' in line:
|
||||
linedata = line.split(' ', maxsplit=6)
|
||||
hostname = linedata[0]
|
||||
mx = linedata[6]
|
||||
mail.append(mx)
|
||||
rrdata.update({'hostname': hostname})
|
||||
rrdata.update({'mail': mail})
|
||||
continue
|
||||
|
||||
|
||||
# TXT parsing
|
||||
if ' descriptive text ' in line:
|
||||
linedata = line.split('descriptive text "', maxsplit=1)
|
||||
hostname = linedata[0]
|
||||
txt = linedata[1].strip('"')
|
||||
text.append(txt)
|
||||
rrdata.update({'hostname': hostname})
|
||||
rrdata.update({'text': text})
|
||||
continue
|
||||
|
||||
|
||||
# -C / SOA parsing
|
||||
if line.startswith('Nameserver '):
|
||||
soaparse = True
|
||||
rrdata = {}
|
||||
linedata = line.split(' ', maxsplit=1)
|
||||
nameserverip = linedata[1].rstrip(':')
|
||||
rrdata.update({'nameserver': nameserverip})
|
||||
continue
|
||||
|
||||
if ' has SOA record ' in line:
|
||||
linedata = line.split(' ', maxsplit=10)
|
||||
|
||||
zone = linedata[0]
|
||||
mname = linedata[4]
|
||||
rname = linedata[5]
|
||||
serial = linedata[6]
|
||||
refresh = linedata[7]
|
||||
retry = linedata[8]
|
||||
expire = linedata[9]
|
||||
minimum = linedata[10]
|
||||
|
||||
try:
|
||||
rrdata.update(
|
||||
{
|
||||
'zone': zone,
|
||||
'mname': mname,
|
||||
'rname': rname,
|
||||
'serial': serial,
|
||||
'refresh': refresh,
|
||||
'retry': retry,
|
||||
'expire': expire,
|
||||
'minimum': minimum
|
||||
},
|
||||
)
|
||||
raw_output.append(rrdata)
|
||||
|
||||
except IndexError:
|
||||
if not warned:
|
||||
jc.utils.warning_message(['Unknown format detected.'])
|
||||
warned = True
|
||||
|
||||
if not soaparse:
|
||||
raw_output.append(rrdata)
|
||||
|
||||
return raw_output if raw else _process(raw_output)
|
||||
@@ -219,7 +219,7 @@ import jc.utils
|
||||
|
||||
class info():
|
||||
"""Provides parser metadata (version, author, etc.)"""
|
||||
version = '2.2'
|
||||
version = '2.3'
|
||||
description = '`ifconfig` command parser'
|
||||
author = 'Kelly Brazil'
|
||||
author_email = 'kellyjonbrazil@gmail.com'
|
||||
@@ -425,18 +425,18 @@ def parse(
|
||||
# Linux syntax
|
||||
re_linux_interface = re.compile(r'''
|
||||
(?P<name>[a-zA-Z0-9:._-]+)\s+
|
||||
Link encap:(?P<type>\S+\s?\S+)
|
||||
Link\sencap:(?P<type>\S+\s?\S+)
|
||||
(\s+HWaddr\s+\b(?P<mac_addr>[0-9A-Fa-f:?]+))?
|
||||
''', re.IGNORECASE | re.VERBOSE
|
||||
)
|
||||
re_linux_ipv4 = re.compile(r'''
|
||||
inet addr:(?P<address>(?:[0-9]{1,3}\.){3}[0-9]{1,3})(\s+
|
||||
inet\saddr:(?P<address>(?:[0-9]{1,3}\.){3}[0-9]{1,3})(\s+
|
||||
Bcast:(?P<broadcast>(?:[0-9]{1,3}\.){3}[0-9]{1,3}))?\s+
|
||||
Mask:(?P<mask>(?:[0-9]{1,3}\.){3}[0-9]{1,3})
|
||||
''', re.IGNORECASE | re.VERBOSE
|
||||
)
|
||||
re_linux_ipv6 = re.compile(r'''
|
||||
inet6 addr:\s+(?P<address>\S+)/
|
||||
inet6\saddr:\s+(?P<address>\S+)/
|
||||
(?P<mask>[0-9]+)\s+
|
||||
Scope:(?P<scope>Link|Host)
|
||||
''', re.IGNORECASE | re.VERBOSE
|
||||
@@ -448,7 +448,7 @@ def parse(
|
||||
''', re.IGNORECASE | re.VERBOSE
|
||||
)
|
||||
re_linux_rx = re.compile(r'''
|
||||
RX packets:(?P<rx_packets>[0-9]+)\s+
|
||||
RX\spackets:(?P<rx_packets>[0-9]+)\s+
|
||||
errors:(?P<rx_errors>[0-9]+)\s+
|
||||
dropped:(?P<rx_dropped>[0-9]+)\s+
|
||||
overruns:(?P<rx_overruns>[0-9]+)\s+
|
||||
@@ -456,7 +456,7 @@ def parse(
|
||||
''', re.IGNORECASE | re.VERBOSE
|
||||
)
|
||||
re_linux_tx = re.compile(r'''
|
||||
TX packets:(?P<tx_packets>[0-9]+)\s+
|
||||
TX\spackets:(?P<tx_packets>[0-9]+)\s+
|
||||
errors:(?P<tx_errors>[0-9]+)\s+
|
||||
dropped:(?P<tx_dropped>[0-9]+)\s+
|
||||
overruns:(?P<tx_overruns>[0-9]+)\s+
|
||||
@@ -464,8 +464,8 @@ def parse(
|
||||
''', re.IGNORECASE | re.VERBOSE
|
||||
)
|
||||
re_linux_bytes = re.compile(r'''
|
||||
\W+RX bytes:(?P<rx_bytes>\d+)\s+\(.*\)\s+
|
||||
TX bytes:(?P<tx_bytes>\d+)\s+\(.*\)
|
||||
\W+RX\sbytes:(?P<rx_bytes>\d+)\s+\(.*\)\s+
|
||||
TX\sbytes:(?P<tx_bytes>\d+)\s+\(.*\)
|
||||
''', re.IGNORECASE | re.VERBOSE
|
||||
)
|
||||
re_linux_tx_stats = re.compile(r'''
|
||||
|
||||
@@ -17,12 +17,12 @@ contained in lists/arrays.
|
||||
|
||||
Usage (cli):
|
||||
|
||||
$ cat foo.ini | jc --ini
|
||||
$ cat foo.ini | jc --ini-dup
|
||||
|
||||
Usage (module):
|
||||
|
||||
import jc
|
||||
result = jc.parse('ini', ini_file_output)
|
||||
result = jc.parse('ini_dup', ini_file_output)
|
||||
|
||||
Schema:
|
||||
|
||||
@@ -62,7 +62,7 @@ Examples:
|
||||
fruit = peach
|
||||
color = green
|
||||
|
||||
$ cat example.ini | jc --ini -p
|
||||
$ cat example.ini | jc --ini-dup -p
|
||||
{
|
||||
"foo": [
|
||||
"fiz"
|
||||
@@ -97,7 +97,7 @@ import uuid
|
||||
|
||||
class info():
|
||||
"""Provides parser metadata (version, author, etc.)"""
|
||||
version = '1.0'
|
||||
version = '1.1'
|
||||
description = 'INI with duplicate key file parser'
|
||||
author = 'Kelly Brazil'
|
||||
author_email = 'kellyjonbrazil@gmail.com'
|
||||
|
||||
145
jc/parsers/ip_route.py
Normal file
145
jc/parsers/ip_route.py
Normal file
@@ -0,0 +1,145 @@
|
||||
"""jc - JSON Convert `ip route` command output parser
|
||||
|
||||
Usage (cli):
|
||||
|
||||
$ ip route | jc --ip-route
|
||||
|
||||
or
|
||||
|
||||
$ jc ip-route
|
||||
|
||||
Usage (module):
|
||||
|
||||
import jc
|
||||
result = jc.parse('ip_route', ip_route_command_output)
|
||||
|
||||
Schema:
|
||||
|
||||
[
|
||||
{
|
||||
"ip": string,
|
||||
"via": string,
|
||||
"dev": string,
|
||||
"metric": integer,
|
||||
"proto": string,
|
||||
"scope": string,
|
||||
"src": string,
|
||||
"via": string,
|
||||
"status": string
|
||||
}
|
||||
]
|
||||
|
||||
Examples:
|
||||
|
||||
$ ip route | jc --ip-route -p
|
||||
[
|
||||
{
|
||||
"ip": "10.0.2.0/24",
|
||||
"dev": "enp0s3",
|
||||
"proto": "kernel",
|
||||
"scope": "link",
|
||||
"src": "10.0.2.15",
|
||||
"metric": 100
|
||||
}
|
||||
]
|
||||
"""
|
||||
from typing import Dict
|
||||
|
||||
import jc.utils
|
||||
|
||||
|
||||
class info:
|
||||
"""Provides parser metadata (version, author, etc.)"""
|
||||
version = '1.0'
|
||||
description = '`ip route` command parser'
|
||||
author = 'Julian Jackson'
|
||||
author_email = 'jackson.julian55@yahoo.com'
|
||||
compatible = ['linux']
|
||||
magic_commands = ['ip route']
|
||||
tags = ['command']
|
||||
|
||||
|
||||
__version__ = info.version
|
||||
|
||||
|
||||
def parse(data, raw=False, quiet=False):
|
||||
"""
|
||||
Main text parsing function
|
||||
|
||||
Parameters:
|
||||
|
||||
data: (string) text data to parse
|
||||
raw: (boolean) unprocessed output if True
|
||||
quiet: (boolean) suppress warning messages if True
|
||||
|
||||
Returns:
|
||||
|
||||
List of Json objects if data is processed and Raw data if raw = true.
|
||||
"""
|
||||
structure = {}
|
||||
items = []
|
||||
lines = data.splitlines()
|
||||
index = 0
|
||||
place = 0
|
||||
inc = 0
|
||||
|
||||
for line in lines:
|
||||
temp = line.split()
|
||||
for word in temp:
|
||||
if word == 'via':
|
||||
y = {'via': temp[place + 1]}
|
||||
place += 1
|
||||
structure.update(y)
|
||||
elif word == 'dev':
|
||||
y = {'dev': temp[place + 1]}
|
||||
place += 1
|
||||
structure.update(y)
|
||||
elif word == 'metric':
|
||||
if raw:
|
||||
y = {'metric': temp[place + 1]}
|
||||
else:
|
||||
y = {'metric': jc.utils.convert_to_int(temp[place+1])}
|
||||
place += 1
|
||||
structure.update(y)
|
||||
elif word == 'proto':
|
||||
y = {'proto': temp[place + 1]}
|
||||
place += 1
|
||||
structure.update(y)
|
||||
elif word == 'scope':
|
||||
y = {'scope': temp[place + 1]}
|
||||
place += 1
|
||||
structure.update(y)
|
||||
elif word == 'src':
|
||||
y = {'src': temp[place + 1]}
|
||||
place += 1
|
||||
structure.update(y)
|
||||
elif word == 'status':
|
||||
y = {'status': temp[place + 1]}
|
||||
place += 1
|
||||
structure.update(y)
|
||||
elif word == 'default':
|
||||
y = {'ip': 'default'}
|
||||
place += 1
|
||||
structure.update(y)
|
||||
elif word == 'linkdown':
|
||||
y = {'status': 'linkdown'}
|
||||
place += 1
|
||||
structure.update(y)
|
||||
else:
|
||||
y = {'ip': temp[0]}
|
||||
place += 1
|
||||
structure.update(y)
|
||||
if y.get("ip") != "":
|
||||
items.append(structure)
|
||||
structure = {}
|
||||
place = 0
|
||||
index += 1
|
||||
inc += 1
|
||||
|
||||
jc.utils.compatibility(__name__, info.compatible, quiet)
|
||||
jc.utils.input_type_check(data)
|
||||
|
||||
if not jc.utils.has_data(data):
|
||||
return []
|
||||
|
||||
return items
|
||||
@@ -85,7 +85,7 @@ import jc.utils
|
||||
|
||||
class info():
|
||||
"""Provides parser metadata (version, author, etc.)"""
|
||||
version = '1.0'
|
||||
version = '1.1'
|
||||
description = '`iwconfig` command parser'
|
||||
author = 'Thomas Vincent'
|
||||
author_email = 'vrince@gmail.com'
|
||||
@@ -146,7 +146,7 @@ def parse(
|
||||
|
||||
raw_output: List[Dict] = []
|
||||
|
||||
re_interface = re.compile(r'^(?P<name>[a-zA-Z0-9:._-]+)\s+(?P<protocol>([a-zA-Z0-9]+\s)*[a-zA-Z0-9.]+)\s+ESSID:\"(?P<essid>[a-zA-Z0-9:._\s]+)\"')
|
||||
re_interface = re.compile(r'^(?P<name>[a-zA-Z0-9:._\-]+)\s+(?P<protocol>([a-zA-Z0-9]+\s)*[a-zA-Z0-9.]+)\s+ESSID:\"(?P<essid>[a-zA-Z0-9:._\s\-]+)\"')
|
||||
re_mode = re.compile(r'Mode:(?P<mode>\w+)')
|
||||
re_frequency = re.compile(r'Frequency:(?P<frequency>[0-9.]+)\s(?P<frequency_unit>\w+)')
|
||||
re_access_point = re.compile(r'Access Point:\s*(?P<access_point>[0-9A-F:]+)')
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"""jc - JSON Convert `last` and `lastb` command output parser
|
||||
|
||||
Supports `-w` and `-F` options.
|
||||
Supports `-w`, `-F`, and `-x` options.
|
||||
|
||||
Calculated epoch time fields are naive (i.e. based on the local time of the
|
||||
system the parser is run on) since there is no timezone information in the
|
||||
@@ -103,10 +103,15 @@ Examples:
|
||||
import re
|
||||
import jc.utils
|
||||
|
||||
DATE_RE = re.compile(r'[MTWFS][ouerha][nedritnu] [JFMASOND][aepuco][nbrynlgptvc]')
|
||||
LAST_F_DATE_RE = re.compile(r'\d\d:\d\d:\d\d \d\d\d\d')
|
||||
LOGIN_LOGOUT_EPOCH_RE = re.compile(r'.*\d\d:\d\d:\d\d \d\d\d\d.*')
|
||||
LOGOUT_IGNORED_EVENTS = ['down', 'crash']
|
||||
|
||||
|
||||
class info():
|
||||
"""Provides parser metadata (version, author, etc.)"""
|
||||
version = '1.8'
|
||||
version = '1.9'
|
||||
description = '`last` and `lastb` command parser'
|
||||
author = 'Kelly Brazil'
|
||||
author_email = 'kellyjonbrazil@gmail.com'
|
||||
@@ -138,9 +143,6 @@ def _process(proc_data):
|
||||
if 'tty' in entry and entry['tty'] == '~':
|
||||
entry['tty'] = None
|
||||
|
||||
if 'tty' in entry and entry['tty'] == 'system_boot':
|
||||
entry['tty'] = 'system boot'
|
||||
|
||||
if 'hostname' in entry and entry['hostname'] == '-':
|
||||
entry['hostname'] = None
|
||||
|
||||
@@ -153,11 +155,11 @@ def _process(proc_data):
|
||||
if 'logout' in entry and entry['logout'] == 'gone_-_no_logout':
|
||||
entry['logout'] = 'gone - no logout'
|
||||
|
||||
if 'login' in entry and re.match(r'.*\d\d:\d\d:\d\d \d\d\d\d.*', entry['login']):
|
||||
if 'login' in entry and LOGIN_LOGOUT_EPOCH_RE.match(entry['login']):
|
||||
timestamp = jc.utils.timestamp(entry['login'])
|
||||
entry['login_epoch'] = timestamp.naive
|
||||
|
||||
if 'logout' in entry and re.match(r'.*\d\d:\d\d:\d\d \d\d\d\d.*', entry['logout']):
|
||||
if 'logout' in entry and LOGIN_LOGOUT_EPOCH_RE.match(entry['logout']):
|
||||
timestamp = jc.utils.timestamp(entry['logout'])
|
||||
entry['logout_epoch'] = timestamp.naive
|
||||
|
||||
@@ -194,66 +196,71 @@ def parse(data, raw=False, quiet=False):
|
||||
# Clear any blank lines
|
||||
cleandata = list(filter(None, data.splitlines()))
|
||||
|
||||
if jc.utils.has_data(data):
|
||||
if not jc.utils.has_data(data):
|
||||
return []
|
||||
|
||||
for entry in cleandata:
|
||||
output_line = {}
|
||||
for entry in cleandata:
|
||||
output_line = {}
|
||||
|
||||
if (entry.startswith('wtmp begins ') or
|
||||
entry.startswith('btmp begins ') or
|
||||
entry.startswith('utx.log begins ')):
|
||||
if any(
|
||||
entry.startswith(f'{prefix} begins ')
|
||||
for prefix in ['wtmp', 'btmp', 'utx.log']
|
||||
):
|
||||
continue
|
||||
|
||||
continue
|
||||
entry = entry.replace('boot time', 'boot_time')
|
||||
entry = entry.replace(' still logged in', '- still_logged_in')
|
||||
entry = entry.replace(' gone - no logout', '- gone_-_no_logout')
|
||||
|
||||
entry = entry.replace('system boot', 'system_boot')
|
||||
entry = entry.replace('boot time', 'boot_time')
|
||||
entry = entry.replace(' still logged in', '- still_logged_in')
|
||||
entry = entry.replace(' gone - no logout', '- gone_-_no_logout')
|
||||
linedata = entry.split()
|
||||
|
||||
linedata = entry.split()
|
||||
if re.match(r'[MTWFS][ouerha][nedritnu] [JFMASOND][aepuco][nbrynlgptvc]', ' '.join(linedata[2:4])):
|
||||
linedata.insert(2, '-')
|
||||
# Adding "-" before the date part.
|
||||
if DATE_RE.match(' '.join(linedata[2:4])):
|
||||
linedata.insert(2, '-')
|
||||
|
||||
# freebsd fix
|
||||
if linedata[0] == 'boot_time':
|
||||
linedata.insert(1, '-')
|
||||
linedata.insert(1, '~')
|
||||
# freebsd fix
|
||||
if linedata[0] == 'boot_time':
|
||||
linedata.insert(1, '-')
|
||||
linedata.insert(1, '~')
|
||||
|
||||
output_line['user'] = linedata[0]
|
||||
output_line['tty'] = linedata[1]
|
||||
output_line['hostname'] = linedata[2]
|
||||
output_line['user'] = linedata[0]
|
||||
|
||||
# last -F support
|
||||
if re.match(r'\d\d:\d\d:\d\d \d\d\d\d', ' '.join(linedata[6:8])):
|
||||
output_line['login'] = ' '.join(linedata[3:8])
|
||||
# Fix for last -x (runlevel).
|
||||
if output_line['user'] == 'runlevel' and linedata[1] == '(to':
|
||||
linedata[1] += f' {linedata.pop(2)} {linedata.pop(2)}'
|
||||
elif output_line['user'] in ['reboot', 'shutdown'] and linedata[1] == 'system': # system down\system boot
|
||||
linedata[1] += f' {linedata.pop(2)}'
|
||||
|
||||
if len(linedata) > 9 and linedata[9] != 'crash' and linedata[9] != 'down':
|
||||
output_line['tty'] = linedata[1]
|
||||
output_line['hostname'] = linedata[2]
|
||||
|
||||
# last -F support
|
||||
if LAST_F_DATE_RE.match(' '.join(linedata[6:8])):
|
||||
output_line['login'] = ' '.join(linedata[3:8])
|
||||
|
||||
if len(linedata) > 9:
|
||||
if linedata[9] not in LOGOUT_IGNORED_EVENTS:
|
||||
output_line['logout'] = ' '.join(linedata[9:14])
|
||||
|
||||
if len(linedata) > 9 and (linedata[9] == 'crash' or linedata[9] == 'down'):
|
||||
else:
|
||||
output_line['logout'] = linedata[9]
|
||||
# add more items to the list to line up duration
|
||||
linedata.insert(10, '-')
|
||||
linedata.insert(10, '-')
|
||||
linedata.insert(10, '-')
|
||||
linedata.insert(10, '-')
|
||||
for _ in range(4):
|
||||
linedata.insert(10, '-')
|
||||
|
||||
if len(linedata) > 14:
|
||||
output_line['duration'] = linedata[14].replace('(', '').replace(')', '')
|
||||
if len(linedata) > 14:
|
||||
output_line['duration'] = linedata[14].replace('(', '').replace(')', '')
|
||||
else: # normal last support
|
||||
output_line['login'] = ' '.join(linedata[3:7])
|
||||
|
||||
# normal last support
|
||||
else:
|
||||
output_line['login'] = ' '.join(linedata[3:7])
|
||||
if len(linedata) > 8:
|
||||
output_line['logout'] = linedata[8]
|
||||
|
||||
if len(linedata) > 8:
|
||||
output_line['logout'] = linedata[8]
|
||||
if len(linedata) > 9:
|
||||
output_line['duration'] = linedata[9].replace('(', '').replace(')', '')
|
||||
|
||||
if len(linedata) > 9:
|
||||
output_line['duration'] = linedata[9].replace('(', '').replace(')', '')
|
||||
|
||||
raw_output.append(output_line)
|
||||
raw_output.append(output_line)
|
||||
|
||||
if raw:
|
||||
return raw_output
|
||||
else:
|
||||
return _process(raw_output)
|
||||
|
||||
return _process(raw_output)
|
||||
|
||||
162
jc/parsers/lsattr.py
Normal file
162
jc/parsers/lsattr.py
Normal file
@@ -0,0 +1,162 @@
|
||||
"""jc - JSON Convert `lsattr` command output parser
|
||||
|
||||
Usage (cli):
|
||||
|
||||
$ lsattr | jc --lsattr
|
||||
|
||||
or
|
||||
|
||||
$ jc lsattr
|
||||
|
||||
Usage (module):
|
||||
|
||||
import jc
|
||||
result = jc.parse('lsattr', lsattr_command_output)
|
||||
|
||||
Schema:
|
||||
|
||||
Information from https://github.com/mirror/busybox/blob/2d4a3d9e6c1493a9520b907e07a41aca90cdfd94/e2fsprogs/e2fs_lib.c#L40
|
||||
used to define field names
|
||||
|
||||
[
|
||||
{
|
||||
"file": string,
|
||||
"compressed_file": Optional[boolean],
|
||||
"compressed_dirty_file": Optional[boolean],
|
||||
"compression_raw_access": Optional[boolean],
|
||||
"secure_deletion": Optional[boolean],
|
||||
"undelete": Optional[boolean],
|
||||
"synchronous_updates": Optional[boolean],
|
||||
"synchronous_directory_updates": Optional[boolean],
|
||||
"immutable": Optional[boolean],
|
||||
"append_only": Optional[boolean],
|
||||
"no_dump": Optional[boolean],
|
||||
"no_atime": Optional[boolean],
|
||||
"compression_requested": Optional[boolean],
|
||||
"encrypted": Optional[boolean],
|
||||
"journaled_data": Optional[boolean],
|
||||
"indexed_directory": Optional[boolean],
|
||||
"no_tailmerging": Optional[boolean],
|
||||
"top_of_directory_hierarchies": Optional[boolean],
|
||||
"extents": Optional[boolean],
|
||||
"no_cow": Optional[boolean],
|
||||
"casefold": Optional[boolean],
|
||||
"inline_data": Optional[boolean],
|
||||
"project_hierarchy": Optional[boolean],
|
||||
"verity": Optional[boolean],
|
||||
}
|
||||
]
|
||||
|
||||
Examples:
|
||||
|
||||
$ sudo lsattr /etc/passwd | jc --lsattr
|
||||
[
|
||||
{
|
||||
"file": "/etc/passwd",
|
||||
"extents": true
|
||||
}
|
||||
]
|
||||
"""
|
||||
from typing import List, Dict
|
||||
from jc.jc_types import JSONDictType
|
||||
import jc.utils
|
||||
|
||||
|
||||
class info():
|
||||
"""Provides parser metadata (version, author, etc.)"""
|
||||
version = '1.0'
|
||||
description = '`lsattr` command parser'
|
||||
author = 'Mark Rotner'
|
||||
author_email = 'rotner.mr@gmail.com'
|
||||
compatible = ['linux']
|
||||
magic_commands = ['lsattr']
|
||||
tags = ['command']
|
||||
|
||||
|
||||
__version__ = info.version
|
||||
|
||||
|
||||
ERROR_PREFIX = "lsattr:"
|
||||
|
||||
# https://github.com/mirror/busybox/blob/2d4a3d9e6c1493a9520b907e07a41aca90cdfd94/e2fsprogs/e2fs_lib.c#L40
|
||||
# https://github.com/landley/toybox/blob/f1682dc79fd75f64042b5438918fe5a507977e1c/toys/other/lsattr.c#L97
|
||||
ATTRIBUTES = {
|
||||
"B": "compressed_file",
|
||||
"Z": "compressed_dirty_file",
|
||||
"X": "compression_raw_access",
|
||||
"s": "secure_deletion",
|
||||
"u": "undelete",
|
||||
"S": "synchronous_updates",
|
||||
"D": "synchronous_directory_updates",
|
||||
"i": "immutable",
|
||||
"a": "append_only",
|
||||
"d": "no_dump",
|
||||
"A": "no_atime",
|
||||
"c": "compression_requested",
|
||||
"E": "encrypted",
|
||||
"j": "journaled_data",
|
||||
"I": "indexed_directory",
|
||||
"t": "no_tailmerging",
|
||||
"T": "top_of_directory_hierarchies",
|
||||
"e": "extents",
|
||||
"C": "no_cow",
|
||||
"F": "casefold",
|
||||
"N": "inline_data",
|
||||
"P": "project_hierarchy",
|
||||
"V": "verity",
|
||||
}
|
||||
|
||||
|
||||
def parse(
|
||||
data: str,
|
||||
raw: bool = False,
|
||||
quiet: bool = False
|
||||
) -> List[JSONDictType]:
|
||||
"""
|
||||
Main text parsing function
|
||||
|
||||
Parameters:
|
||||
|
||||
data: (string) text data to parse
|
||||
quiet: (boolean) suppress warning messages if True
|
||||
|
||||
Returns:
|
||||
|
||||
List of Dictionaries. Raw or processed structured data.
|
||||
"""
|
||||
jc.utils.compatibility(__name__, info.compatible, quiet)
|
||||
jc.utils.input_type_check(data)
|
||||
|
||||
output: List = []
|
||||
|
||||
cleandata = list(filter(None, data.splitlines()))
|
||||
|
||||
if not jc.utils.has_data(data):
|
||||
return output
|
||||
|
||||
for line in cleandata:
|
||||
# -R flag returns the output in the format:
|
||||
# Folder:
|
||||
# attributes file_in_folder
|
||||
if line.endswith(':'):
|
||||
continue
|
||||
|
||||
# lsattr: Operation not supported ....
|
||||
if line.startswith(ERROR_PREFIX):
|
||||
continue
|
||||
|
||||
line_output: Dict = {}
|
||||
|
||||
# attributes file
|
||||
# --------------e----- /etc/passwd
|
||||
attributes, file = line.split()
|
||||
line_output['file'] = file
|
||||
for attribute in list(attributes):
|
||||
attribute_key = ATTRIBUTES.get(attribute)
|
||||
if attribute_key:
|
||||
line_output[attribute_key] = True
|
||||
|
||||
if line_output:
|
||||
output.append(line_output)
|
||||
|
||||
return output
|
||||
89
jc/parsers/lsb_release.py
Normal file
89
jc/parsers/lsb_release.py
Normal file
@@ -0,0 +1,89 @@
|
||||
"""jc - JSON Convert `lsb_release` command parser
|
||||
|
||||
This parser is an alias to the Key/Value parser (`--kv`).
|
||||
|
||||
Usage (cli):
|
||||
|
||||
$ lsb_release -a | jc --lsb-release
|
||||
|
||||
or
|
||||
$ jc lsb_release -a
|
||||
|
||||
Usage (module):
|
||||
|
||||
import jc
|
||||
result = jc.parse('lsb_release', lsb_release_command_output)
|
||||
|
||||
Schema:
|
||||
|
||||
{
|
||||
"<key>": string
|
||||
}
|
||||
|
||||
Examples:
|
||||
|
||||
$ lsb_release -a | jc --lsb-release -p
|
||||
{
|
||||
"Distributor ID": "Ubuntu",
|
||||
"Description": "Ubuntu 16.04.6 LTS",
|
||||
"Release": "16.04",
|
||||
"Codename": "xenial"
|
||||
}
|
||||
"""
|
||||
from jc.jc_types import JSONDictType
|
||||
import jc.parsers.kv
|
||||
import jc.utils
|
||||
|
||||
|
||||
class info():
|
||||
"""Provides parser metadata (version, author, etc.)"""
|
||||
version = '1.0'
|
||||
description = '`lsb_release` command parser'
|
||||
author = 'Kelly Brazil'
|
||||
author_email = 'kellyjonbrazil@gmail.com'
|
||||
details = 'Using the Key/Value parser'
|
||||
compatible = ['linux', 'darwin', 'cygwin', 'win32', 'aix', 'freebsd']
|
||||
magic_commands = ['lsb_release']
|
||||
tags = ['command']
|
||||
|
||||
|
||||
__version__ = info.version
|
||||
|
||||
|
||||
def _process(proc_data: JSONDictType) -> JSONDictType:
|
||||
"""
|
||||
Final processing to conform to the schema.
|
||||
|
||||
Parameters:
|
||||
|
||||
proc_data: (Dictionary) raw structured data to process
|
||||
|
||||
Returns:
|
||||
|
||||
Dictionary. Structured to conform to the schema.
|
||||
"""
|
||||
return jc.parsers.kv._process(proc_data)
|
||||
|
||||
|
||||
def parse(
|
||||
data: str,
|
||||
raw: bool = False,
|
||||
quiet: bool = False
|
||||
) -> JSONDictType:
|
||||
"""
|
||||
Main text parsing function
|
||||
|
||||
Parameters:
|
||||
|
||||
data: (string) text data to parse
|
||||
raw: (boolean) unprocessed output if True
|
||||
quiet: (boolean) suppress warning messages if True
|
||||
|
||||
Returns:
|
||||
|
||||
Dictionary. Raw or processed structured data.
|
||||
"""
|
||||
jc.utils.compatibility(__name__, info.compatible, quiet)
|
||||
raw_output = jc.parsers.kv.parse(data, raw, quiet)
|
||||
|
||||
return raw_output if raw else _process(raw_output)
|
||||
@@ -97,6 +97,24 @@ Schema:
|
||||
]
|
||||
}
|
||||
},
|
||||
"cdc_mbim": {
|
||||
"<item>": {
|
||||
"value": string,
|
||||
"description": string,
|
||||
"attributes": [
|
||||
string
|
||||
]
|
||||
}
|
||||
},
|
||||
"cdc_mbim_extended": {
|
||||
"<item>": {
|
||||
"value": string,
|
||||
"description": string,
|
||||
"attributes": [
|
||||
string
|
||||
]
|
||||
}
|
||||
},
|
||||
"videocontrol_descriptors": [
|
||||
{
|
||||
"<item>": {
|
||||
@@ -291,7 +309,7 @@ from jc.exceptions import ParseError
|
||||
|
||||
class info():
|
||||
"""Provides parser metadata (version, author, etc.)"""
|
||||
version = '1.3'
|
||||
version = '1.4'
|
||||
description = '`lsusb` command parser'
|
||||
author = 'Kelly Brazil'
|
||||
author_email = 'kellyjonbrazil@gmail.com'
|
||||
@@ -500,6 +518,8 @@ class _LsUsb():
|
||||
self.cdc_call_management = _descriptor_obj('cdc_call_management')
|
||||
self.cdc_acm = _descriptor_obj('cdc_acm')
|
||||
self.cdc_union = _descriptor_obj('cdc_union')
|
||||
self.cdc_mbim = _descriptor_obj('cdc_mbim')
|
||||
self.cdc_mbim_extended = _descriptor_obj('cdc_mbim_extended')
|
||||
self.endpoint_descriptors = _descriptor_list('endpoint_descriptor')
|
||||
self.videocontrol_interface_descriptors = _descriptor_list('videocontrol_interface_descriptor')
|
||||
self.videostreaming_interface_descriptors = _descriptor_list('videostreaming_interface_descriptor')
|
||||
@@ -538,7 +558,8 @@ class _LsUsb():
|
||||
section_header = self.normal_section_header
|
||||
|
||||
if self.section == 'videocontrol_interface_descriptor' \
|
||||
or self.section == 'videostreaming_interface_descriptor':
|
||||
or self.section == 'videostreaming_interface_descriptor' \
|
||||
or self.section == 'cdc_mbim_extended':
|
||||
|
||||
section_header = self.larger_section_header
|
||||
|
||||
@@ -689,6 +710,8 @@ class _LsUsb():
|
||||
' CDC Union:': 'cdc_union',
|
||||
' HID Device Descriptor:': 'hid_device_descriptor',
|
||||
' Report Descriptors:': 'report_descriptors',
|
||||
' CDC MBIM:': 'cdc_mbim',
|
||||
' CDC MBIM Extended:': 'cdc_mbim_extended',
|
||||
'Hub Descriptor:': 'hub_descriptor',
|
||||
' Hub Port Status:': 'hub_port_status',
|
||||
'Device Qualifier (for other device speed):': 'device_qualifier',
|
||||
@@ -713,6 +736,8 @@ class _LsUsb():
|
||||
'cdc_call_management': self.cdc_call_management.list,
|
||||
'cdc_acm': self.cdc_acm.list,
|
||||
'cdc_union': self.cdc_union.list,
|
||||
'cdc_mbim': self.cdc_mbim.list,
|
||||
'cdc_mbim_extended': self.cdc_mbim_extended.list,
|
||||
'hid_device_descriptor': self.hid_device_descriptor.list,
|
||||
# 'report_descriptors': self.report_descriptors_list, # not implemented
|
||||
'videocontrol_interface_descriptor': self.videocontrol_interface_descriptors.list,
|
||||
@@ -757,6 +782,8 @@ class _LsUsb():
|
||||
['device_descriptor']['configuration_descriptor']['interface_descriptors'][0]['cdc_call_management'] = {}
|
||||
['device_descriptor']['configuration_descriptor']['interface_descriptors'][0]['cdc_acm'] = {}
|
||||
['device_descriptor']['configuration_descriptor']['interface_descriptors'][0]['cdc_union'] = {}
|
||||
['device_descriptor']['configuration_descriptor']['interface_descriptors'][0]['cdc_mbim'] = {}
|
||||
['device_descriptor']['configuration_descriptor']['interface_descriptors'][0]['cdc_mbim_extended'] = {}
|
||||
['device_descriptor']['configuration_descriptor']['interface_descriptors'][0]['hid_device_descriptor'] = {}
|
||||
['device_descriptor']['configuration_descriptor']['interface_descriptors'][0]['endpoint_descriptors'] = []
|
||||
['device_descriptor']['configuration_descriptor']['interface_descriptors'][0]['endpoint_descriptors'][0] = {}
|
||||
@@ -847,6 +874,12 @@ class _LsUsb():
|
||||
if self.cdc_union._entries_for_this_bus_and_interface_idx_exist(idx, iface_idx):
|
||||
self.cdc_union._update_output(idx, iface_idx, i_desc_obj)
|
||||
|
||||
if self.cdc_mbim._entries_for_this_bus_and_interface_idx_exist(idx, iface_idx):
|
||||
self.cdc_mbim._update_output(idx, iface_idx, i_desc_obj)
|
||||
|
||||
if self.cdc_mbim_extended._entries_for_this_bus_and_interface_idx_exist(idx, iface_idx):
|
||||
self.cdc_mbim_extended._update_output(idx, iface_idx, i_desc_obj)
|
||||
|
||||
if self.hid_device_descriptor._entries_for_this_bus_and_interface_idx_exist(idx, iface_idx):
|
||||
self.hid_device_descriptor._update_output(idx, iface_idx, i_desc_obj)
|
||||
|
||||
@@ -923,6 +956,10 @@ def parse(data, raw=False, quiet=False):
|
||||
lsusb = _LsUsb()
|
||||
|
||||
if jc.utils.has_data(data):
|
||||
|
||||
# fix known too-long field names
|
||||
data = data.replace('bmNetworkCapabilities', 'bmNetworkCapabilit ')
|
||||
|
||||
for line in data.splitlines():
|
||||
# only -v option or no options are supported
|
||||
if line.startswith('/'):
|
||||
|
||||
@@ -160,7 +160,7 @@ def _aix_parse(data):
|
||||
|
||||
# AIX mount entries have the remote node as the zeroth element. If the
|
||||
# mount is local, the zeroth element is the filesystem instead. We can
|
||||
# detect this by the lenth of the list. For local mounts, length is 7,
|
||||
# detect this by the length of the list. For local mounts, length is 7,
|
||||
# and for remote mounts, the length is 8. In the remote case, pop off
|
||||
# the zeroth element. Then parsed_line has a consistent format.
|
||||
if len(parsed_line) == 8:
|
||||
|
||||
@@ -355,17 +355,18 @@ import jc.utils
|
||||
|
||||
class info():
|
||||
"""Provides parser metadata (version, author, etc.)"""
|
||||
version = '1.13'
|
||||
version = '1.15'
|
||||
description = '`netstat` command parser'
|
||||
author = 'Kelly Brazil'
|
||||
author_email = 'kellyjonbrazil@gmail.com'
|
||||
compatible = ['linux', 'darwin', 'freebsd']
|
||||
compatible = ['linux', 'darwin', 'freebsd', 'win32']
|
||||
magic_commands = ['netstat']
|
||||
tags = ['command']
|
||||
|
||||
|
||||
__version__ = info.version
|
||||
|
||||
WINDOWS_NETSTAT_HEADER = "Active Connections"
|
||||
|
||||
def _process(proc_data):
|
||||
"""
|
||||
@@ -450,9 +451,10 @@ def parse(data, raw=False, quiet=False):
|
||||
|
||||
import jc.parsers.netstat_freebsd_osx
|
||||
raw_output = jc.parsers.netstat_freebsd_osx.parse(cleandata)
|
||||
|
||||
# use linux parser
|
||||
else:
|
||||
elif cleandata[0] == WINDOWS_NETSTAT_HEADER: # use windows parser.
|
||||
import jc.parsers.netstat_windows
|
||||
raw_output = jc.parsers.netstat_windows.parse(cleandata)
|
||||
else: # use linux parser.
|
||||
import jc.parsers.netstat_linux
|
||||
raw_output = jc.parsers.netstat_linux.parse(cleandata)
|
||||
|
||||
|
||||
@@ -31,10 +31,19 @@ def normalize_interface_headers(header):
|
||||
|
||||
|
||||
def parse_network(headers, entry):
|
||||
LIST_OF_STATES = [
|
||||
"ESTABLISHED", "SYN_SENT", "SYN_RECV", "FIN_WAIT1", "FIN_WAIT2",
|
||||
"TIME_WAIT", "CLOSED", "CLOSE_WAIT", "LAST_ACK", "LISTEN", "CLOSING",
|
||||
"UNKNOWN", "7"
|
||||
]
|
||||
|
||||
# split entry based on presence of value in "State" column
|
||||
contains_state = any(state in entry for state in LIST_OF_STATES)
|
||||
split_modifier = 1 if contains_state else 2
|
||||
entry = entry.split(maxsplit=len(headers) - split_modifier)
|
||||
|
||||
# Count words in header
|
||||
# if len of line is one less than len of header, then insert None in field 5
|
||||
entry = entry.split(maxsplit=len(headers) - 1)
|
||||
|
||||
if len(entry) == len(headers) - 1:
|
||||
entry.insert(5, None)
|
||||
|
||||
|
||||
75
jc/parsers/netstat_windows.py
Normal file
75
jc/parsers/netstat_windows.py
Normal file
@@ -0,0 +1,75 @@
|
||||
"""
|
||||
jc - JSON Convert Windows `netstat` command output parser
|
||||
"""
|
||||
|
||||
|
||||
from typing import Dict, List
|
||||
|
||||
POSSIBLE_PROTOCOLS = ("TCP", "UDP", "TCPv6", "UDPv6")
|
||||
|
||||
def normalize_headers(headers: str):
|
||||
"""
|
||||
Normalizes the headers to match the jc netstat parser style
|
||||
(local_address -> local_address, local_port...).
|
||||
"""
|
||||
|
||||
headers = headers.lower().strip()
|
||||
headers = headers.replace("local address", "local_address")
|
||||
headers = headers.replace("foreign address", "foreign_address")
|
||||
return headers.split()
|
||||
|
||||
|
||||
def parse(cleandata: List[str]):
|
||||
"""
|
||||
Main text parsing function for Windows netstat
|
||||
|
||||
Parameters:
|
||||
|
||||
cleandata: (string) text data to parse
|
||||
|
||||
Returns:
|
||||
|
||||
List of Dictionaries. Raw structured data.
|
||||
"""
|
||||
|
||||
raw_output = []
|
||||
cleandata.pop(0) # Removing the "Active Connections" header.
|
||||
headers = normalize_headers(cleandata.pop(0))
|
||||
|
||||
for line in cleandata:
|
||||
line = line.strip()
|
||||
|
||||
if not line.startswith(POSSIBLE_PROTOCOLS): # -b.
|
||||
line_data = raw_output.pop(len(raw_output) - 1)
|
||||
line_data['program_name'] = line
|
||||
raw_output.append(line_data)
|
||||
continue
|
||||
|
||||
line_data = line.split()
|
||||
line_data: Dict[str, str] = dict(zip(headers, line_data))
|
||||
for key in list(line_data.keys()):
|
||||
if key == "local_address":
|
||||
local_address, local_port = line_data[key].rsplit(
|
||||
":", maxsplit=1)
|
||||
line_data["local_address"] = local_address
|
||||
line_data["local_port"] = local_port
|
||||
continue
|
||||
|
||||
if key == "foreign_address":
|
||||
foreign_address, foreign_port = line_data[key].rsplit(
|
||||
":", maxsplit=1)
|
||||
line_data["foreign_address"] = foreign_address
|
||||
line_data["foreign_port"] = foreign_port
|
||||
continue
|
||||
|
||||
# There is no state in UDP, so the data after the "state" header will leak.
|
||||
if key == "proto" and "state" in headers and line_data["proto"] == "UDP":
|
||||
next_header = headers.index("state") + 1
|
||||
if len(headers) > next_header:
|
||||
next_header = headers[next_header]
|
||||
line_data[next_header] = line_data["state"]
|
||||
line_data["state"] = ''
|
||||
|
||||
raw_output.append(line_data)
|
||||
|
||||
return raw_output
|
||||
236
jc/parsers/nsd_control.py
Normal file
236
jc/parsers/nsd_control.py
Normal file
@@ -0,0 +1,236 @@
|
||||
"""jc - JSON Convert `nsd-control` command output parser
|
||||
|
||||
Usage (cli):
|
||||
|
||||
$ nsd-control | jc --nsd-control
|
||||
|
||||
or
|
||||
|
||||
$ jc nsd-control
|
||||
|
||||
Usage (module):
|
||||
|
||||
import jc
|
||||
result = jc.parse('nsd_control', nsd_control_command_output)
|
||||
|
||||
Schema:
|
||||
|
||||
[
|
||||
{
|
||||
"version": string,
|
||||
"verbosity": integer,
|
||||
"ratelimit": integer
|
||||
}
|
||||
]
|
||||
|
||||
[
|
||||
{
|
||||
"zone": string
|
||||
"status": {
|
||||
"state": string,
|
||||
"served-serial": string,
|
||||
"commit-serial": string,
|
||||
"wait": string
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
Examples:
|
||||
|
||||
$ nsd-control | jc --nsd-control status
|
||||
[
|
||||
{
|
||||
"version": "4.6.2",
|
||||
"verbosity": "2",
|
||||
"ratelimit": "0"
|
||||
}
|
||||
]
|
||||
|
||||
$ nsd-control | jc --nsd-control zonestatus sunet.se
|
||||
[
|
||||
{
|
||||
"zone": "sunet.se",
|
||||
"status": {
|
||||
"state": "ok",
|
||||
"served-serial": "2023090704 since 2023-09-07T16:34:27",
|
||||
"commit-serial": "2023090704 since 2023-09-07T16:34:27",
|
||||
"wait": "28684 sec between attempts"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
"""
|
||||
from typing import List, Dict
|
||||
import jc.utils
|
||||
|
||||
|
||||
class info():
|
||||
"""Provides parser metadata (version, author, etc.)"""
|
||||
version = '1.0'
|
||||
description = '`nsd-control` command parser'
|
||||
author = 'Pettai'
|
||||
author_email = 'pettai@sunet.se'
|
||||
compatible = ['linux', 'darwin', 'cygwin', 'win32', 'aix', 'freebsd']
|
||||
tags = ['command']
|
||||
magic_commands = ['nsd-control']
|
||||
|
||||
|
||||
__version__ = info.version
|
||||
|
||||
|
||||
def _process(proc_data):
|
||||
"""
|
||||
Final processing to conform to the schema.
|
||||
|
||||
Parameters:
|
||||
|
||||
proc_data: (List of Dictionaries) raw structured data to process
|
||||
|
||||
Returns:
|
||||
|
||||
List of Dictionaries. Structured to conform to the schema.
|
||||
"""
|
||||
int_list = {'verbosity', 'ratelimit', 'wait'}
|
||||
|
||||
for entry in proc_data:
|
||||
for key in entry:
|
||||
if key in int_list:
|
||||
entry[key] = jc.utils.convert_to_int(entry[key])
|
||||
|
||||
return proc_data
|
||||
|
||||
|
||||
def parse(data: str, raw: bool = False, quiet: bool = False):
|
||||
"""
|
||||
Main text parsing function
|
||||
|
||||
Parameters:
|
||||
|
||||
data: (string) text data to parse
|
||||
raw: (boolean) unprocessed output if True
|
||||
quiet: (boolean) suppress warning messages if True
|
||||
|
||||
Returns:
|
||||
|
||||
List of Dictionaries. Raw or processed structured data.
|
||||
"""
|
||||
jc.utils.compatibility(__name__, info.compatible, quiet)
|
||||
jc.utils.input_type_check(data)
|
||||
|
||||
raw_output: List[Dict] = []
|
||||
|
||||
if jc.utils.has_data(data):
|
||||
|
||||
itrparse = False
|
||||
itr: Dict = {}
|
||||
|
||||
for line in filter(None, data.splitlines()):
|
||||
line = line.strip()
|
||||
|
||||
# default 'ok'
|
||||
if line.startswith('ok'):
|
||||
raw_output.append({'command': 'ok'})
|
||||
continue
|
||||
|
||||
# status
|
||||
if line.startswith('version:'):
|
||||
status = {}
|
||||
linedata = line.split(':', maxsplit=1)
|
||||
version = linedata[1].strip()
|
||||
status.update({'version': version})
|
||||
continue
|
||||
|
||||
if line.startswith('verbosity:'):
|
||||
linedata = line.split(':', maxsplit=1)
|
||||
verbosity = linedata[1]
|
||||
status.update({'verbosity': verbosity})
|
||||
continue
|
||||
|
||||
if line.startswith('ratelimit:'):
|
||||
linedata = line.split(':', maxsplit=1)
|
||||
ratelimit = linedata[1]
|
||||
status.update({'ratelimit': ratelimit})
|
||||
raw_output.append(status)
|
||||
continue
|
||||
|
||||
# print_cookie_secrets
|
||||
if line.startswith('active'):
|
||||
itrparse = True
|
||||
itr = {}
|
||||
linedata = line.split(':', maxsplit=1)
|
||||
active = linedata[1].strip()
|
||||
itr.update({'active': active})
|
||||
continue
|
||||
|
||||
if line.startswith('staging'):
|
||||
linedata = line.split(':', maxsplit=1)
|
||||
staging = linedata[1].strip()
|
||||
itr.update({'staging': staging})
|
||||
continue
|
||||
|
||||
# print_tsig
|
||||
if line.startswith('key:'):
|
||||
tsigs = {}
|
||||
tsigdata = dict()
|
||||
linedata = line.split(' ', maxsplit=6)
|
||||
name = linedata[2].strip('"').rstrip('"')
|
||||
tsigdata.update({'name': name})
|
||||
secret = linedata[4].strip('"').rstrip('"')
|
||||
tsigdata.update({'secret': secret})
|
||||
algorithm = linedata[6].strip('"').rstrip('"')
|
||||
tsigdata.update({'algorithm': algorithm})
|
||||
tsigs.update({'key': tsigdata})
|
||||
raw_output.append(tsigs)
|
||||
continue
|
||||
|
||||
# zonestatus
|
||||
if line.startswith('zone:'):
|
||||
zonename: Dict = dict()
|
||||
zstatus: Dict = dict()
|
||||
linedata = line.split(':\t', maxsplit=1)
|
||||
zone = linedata[1]
|
||||
zonename.update({'zone': zone})
|
||||
continue
|
||||
|
||||
if line.startswith('state:'):
|
||||
linedata = line.split(': ', maxsplit=1)
|
||||
state = linedata[1]
|
||||
zstatus.update({'state': state})
|
||||
continue
|
||||
|
||||
if line.startswith('served-serial:'):
|
||||
linedata = line.split(': ', maxsplit=1)
|
||||
served = linedata[1].strip('"').rstrip('"')
|
||||
zstatus.update({'served-serial': served})
|
||||
continue
|
||||
|
||||
if line.startswith('commit-serial:'):
|
||||
linedata = line.split(': ', maxsplit=1)
|
||||
commit = linedata[1].strip('"').rstrip('"')
|
||||
zstatus.update({'commit-serial': commit})
|
||||
continue
|
||||
|
||||
if line.startswith('wait:'):
|
||||
linedata = line.split(': ', maxsplit=1)
|
||||
wait = linedata[1].strip('"').rstrip('"')
|
||||
zstatus.update({'wait': wait})
|
||||
zonename.update({'status': zstatus})
|
||||
raw_output.append(zonename)
|
||||
continue
|
||||
|
||||
# stats
|
||||
if line.startswith('server') or line.startswith('num.') or line.startswith('size.') or line.startswith('time.') or line.startswith('zone.'):
|
||||
itrparse = True
|
||||
linedata = line.split('=', maxsplit=1)
|
||||
key = linedata[0]
|
||||
if key.startswith('time.'):
|
||||
value = float(linedata[1])
|
||||
else:
|
||||
value = int(linedata[1])
|
||||
itr.update({key: value})
|
||||
continue
|
||||
|
||||
if itrparse:
|
||||
raw_output.append(itr)
|
||||
|
||||
return raw_output if raw else _process(raw_output)
|
||||
113
jc/parsers/os_release.py
Normal file
113
jc/parsers/os_release.py
Normal file
@@ -0,0 +1,113 @@
|
||||
"""jc - JSON Convert `/etc/os-release` file parser
|
||||
|
||||
This parser is an alias to the Key/Value parser (`--kv`).
|
||||
|
||||
Usage (cli):
|
||||
|
||||
$ cat /etc/os-release | jc --os-release
|
||||
|
||||
Usage (module):
|
||||
|
||||
import jc
|
||||
result = jc.parse('os_release', os_release_output)
|
||||
|
||||
Schema:
|
||||
|
||||
{
|
||||
"<key>": string
|
||||
}
|
||||
|
||||
Examples:
|
||||
|
||||
$ cat /etc/os-release | jc --os-release -p
|
||||
{
|
||||
"NAME": "CentOS Linux",
|
||||
"VERSION": "7 (Core)",
|
||||
"ID": "centos",
|
||||
"ID_LIKE": "rhel fedora",
|
||||
"VERSION_ID": "7",
|
||||
"PRETTY_NAME": "CentOS Linux 7 (Core)",
|
||||
"ANSI_COLOR": "0;31",
|
||||
"CPE_NAME": "cpe:/o:centos:centos:7",
|
||||
"HOME_URL": "https://www.centos.org/",
|
||||
"BUG_REPORT_URL": "https://bugs.centos.org/",
|
||||
"CENTOS_MANTISBT_PROJECT": "CentOS-7",
|
||||
"CENTOS_MANTISBT_PROJECT_VERSION": "7",
|
||||
"REDHAT_SUPPORT_PRODUCT": "centos",
|
||||
"REDHAT_SUPPORT_PRODUCT_VERSION": "7"
|
||||
}
|
||||
|
||||
$ cat /etc/os-release | jc --os-release -p -r
|
||||
{
|
||||
"NAME": "\\"CentOS Linux\\"",
|
||||
"VERSION": "\\"7 (Core)\\"",
|
||||
"ID": "\\"centos\\"",
|
||||
"ID_LIKE": "\\"rhel fedora\\"",
|
||||
"VERSION_ID": "\\"7\\"",
|
||||
"PRETTY_NAME": "\\"CentOS Linux 7 (Core)\\"",
|
||||
"ANSI_COLOR": "\\"0;31\\"",
|
||||
"CPE_NAME": "\\"cpe:/o:centos:centos:7\\"",
|
||||
"HOME_URL": "\\"https://www.centos.org/\\"",
|
||||
"BUG_REPORT_URL": "\\"https://bugs.centos.org/\\"",
|
||||
"CENTOS_MANTISBT_PROJECT": "\\"CentOS-7\\"",
|
||||
"CENTOS_MANTISBT_PROJECT_VERSION": "\\"7\\"",
|
||||
"REDHAT_SUPPORT_PRODUCT": "\\"centos\\"",
|
||||
"REDHAT_SUPPORT_PRODUCT_VERSION": "\\"7\\""
|
||||
}
|
||||
"""
|
||||
from jc.jc_types import JSONDictType
|
||||
import jc.parsers.kv
|
||||
import jc.utils
|
||||
|
||||
|
||||
class info():
|
||||
"""Provides parser metadata (version, author, etc.)"""
|
||||
version = '1.0'
|
||||
description = '`/etc/os-release` file parser'
|
||||
author = 'Kelly Brazil'
|
||||
author_email = 'kellyjonbrazil@gmail.com'
|
||||
details = 'Using the Key/Value parser'
|
||||
compatible = ['linux', 'darwin', 'cygwin', 'win32', 'aix', 'freebsd']
|
||||
tags = ['file', 'standard', 'string']
|
||||
|
||||
|
||||
__version__ = info.version
|
||||
|
||||
|
||||
def _process(proc_data: JSONDictType) -> JSONDictType:
|
||||
"""
|
||||
Final processing to conform to the schema.
|
||||
|
||||
Parameters:
|
||||
|
||||
proc_data: (Dictionary) raw structured data to process
|
||||
|
||||
Returns:
|
||||
|
||||
Dictionary. Structured to conform to the schema.
|
||||
"""
|
||||
return jc.parsers.kv._process(proc_data)
|
||||
|
||||
|
||||
def parse(
|
||||
data: str,
|
||||
raw: bool = False,
|
||||
quiet: bool = False
|
||||
) -> JSONDictType:
|
||||
"""
|
||||
Main text parsing function
|
||||
|
||||
Parameters:
|
||||
|
||||
data: (string) text data to parse
|
||||
raw: (boolean) unprocessed output if True
|
||||
quiet: (boolean) suppress warning messages if True
|
||||
|
||||
Returns:
|
||||
|
||||
Dictionary. Raw or processed structured data.
|
||||
"""
|
||||
jc.utils.compatibility(__name__, info.compatible, quiet)
|
||||
raw_output = jc.parsers.kv.parse(data, raw, quiet)
|
||||
|
||||
return raw_output if raw else _process(raw_output)
|
||||
@@ -96,14 +96,14 @@ class PBParser(object):
|
||||
prefix = self.data[0:6]
|
||||
for case in Switch(prefix):
|
||||
if case('bplist'):
|
||||
self.file_type = 'binary'
|
||||
import biplist
|
||||
parsed_plist = biplist.readPlist(self.file_path)
|
||||
# self.file_type = 'binary'
|
||||
# import biplist
|
||||
# parsed_plist = biplist.readPlist(self.file_path)
|
||||
break
|
||||
if case('<?xml '):
|
||||
self.file_type = 'xml'
|
||||
import plistlib
|
||||
parsed_plist = plistlib.readPlist(self.file_path)
|
||||
# self.file_type = 'xml'
|
||||
# import plistlib
|
||||
# parsed_plist = plistlib.readPlist(self.file_path)
|
||||
break
|
||||
if case():
|
||||
self.file_type = 'ascii'
|
||||
@@ -111,7 +111,7 @@ class PBParser(object):
|
||||
if self.data[0:2] == '//':
|
||||
# this is to try to see if we can locate the desired string encoding of the file
|
||||
import re
|
||||
result = re.search('^// !\$\*(.+?)\*\$!', self.data) # pylint: disable=anomalous-backslash-in-string
|
||||
result = re.search(r'^// !\$\*(.+?)\*\$!', self.data) # pylint: disable=anomalous-backslash-in-string
|
||||
if result:
|
||||
self.string_encoding = result.group(1)
|
||||
#now return the parse
|
||||
|
||||
@@ -57,7 +57,7 @@ def KeySorter(obj1, obj2):
|
||||
result = StringCmp(str(obj1), str(obj2))
|
||||
return result
|
||||
|
||||
class pbRoot(collections.MutableMapping):
|
||||
class pbRoot(collections.abc.MutableMapping):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.store = dict()
|
||||
|
||||
@@ -40,6 +40,9 @@ Schema:
|
||||
"kb_ccwr_s": float,
|
||||
"cswch_s": float,
|
||||
"nvcswch_s": float,
|
||||
"usr_ms": integer,
|
||||
"system_ms": integer,
|
||||
"guest_ms": integer,
|
||||
"command": string
|
||||
}
|
||||
]
|
||||
@@ -128,7 +131,7 @@ from jc.exceptions import ParseError
|
||||
|
||||
class info():
|
||||
"""Provides parser metadata (version, author, etc.)"""
|
||||
version = '1.1'
|
||||
version = '1.3'
|
||||
description = '`pidstat -H` command parser'
|
||||
author = 'Kelly Brazil'
|
||||
author_email = 'kellyjonbrazil@gmail.com'
|
||||
@@ -152,11 +155,16 @@ def _process(proc_data: List[Dict]) -> List[Dict]:
|
||||
|
||||
List of Dictionaries. Structured to conform to the schema.
|
||||
"""
|
||||
int_list = {'time', 'uid', 'pid', 'cpu', 'vsz', 'rss', 'stksize', 'stkref'}
|
||||
int_list = {
|
||||
'time', 'uid', 'pid', 'cpu', 'vsz', 'rss', 'stksize', 'stkref',
|
||||
'usr_ms', 'system_ms', 'guest_ms'
|
||||
}
|
||||
|
||||
float_list = {'percent_usr', 'percent_system', 'percent_guest', 'percent_cpu',
|
||||
'minflt_s', 'majflt_s', 'percent_mem', 'kb_rd_s', 'kb_wr_s',
|
||||
'kb_ccwr_s', 'cswch_s', 'nvcswch_s'}
|
||||
float_list = {
|
||||
'percent_usr', 'percent_system', 'percent_guest', 'percent_cpu',
|
||||
'minflt_s', 'majflt_s', 'percent_mem', 'kb_rd_s', 'kb_wr_s',
|
||||
'kb_ccwr_s', 'cswch_s', 'nvcswch_s', 'percent_wait'
|
||||
}
|
||||
|
||||
for entry in proc_data:
|
||||
for key in entry:
|
||||
@@ -169,6 +177,14 @@ def _process(proc_data: List[Dict]) -> List[Dict]:
|
||||
return proc_data
|
||||
|
||||
|
||||
def normalize_header(header: str) -> str:
|
||||
return header.replace('#', ' ')\
|
||||
.replace('-', '_')\
|
||||
.replace('/', '_')\
|
||||
.replace('%', 'percent_')\
|
||||
.lower()
|
||||
|
||||
|
||||
def parse(
|
||||
data: str,
|
||||
raw: bool = False,
|
||||
@@ -191,29 +207,28 @@ def parse(
|
||||
jc.utils.input_type_check(data)
|
||||
|
||||
raw_output: List = []
|
||||
table_list: List = []
|
||||
header_found = False
|
||||
|
||||
if jc.utils.has_data(data):
|
||||
|
||||
# check for line starting with # as the start of the table
|
||||
data_list = list(filter(None, data.splitlines()))
|
||||
for line in data_list.copy():
|
||||
if line.startswith('#'):
|
||||
break
|
||||
else:
|
||||
data_list.pop(0)
|
||||
|
||||
if not data_list:
|
||||
for line in data_list:
|
||||
if line.startswith('#'):
|
||||
header_found = True
|
||||
if len(table_list) > 1:
|
||||
raw_output.extend(simple_table_parse(table_list))
|
||||
table_list = [normalize_header(line)]
|
||||
continue
|
||||
|
||||
if header_found:
|
||||
table_list.append(line)
|
||||
|
||||
if len(table_list) > 1:
|
||||
raw_output.extend(simple_table_parse(table_list))
|
||||
|
||||
if not header_found:
|
||||
raise ParseError('Could not parse pidstat output. Make sure to use "pidstat -h".')
|
||||
|
||||
# normalize header
|
||||
data_list[0] = data_list[0].replace('#', ' ')\
|
||||
.replace('/', '_')\
|
||||
.replace('%', 'percent_')\
|
||||
.lower()
|
||||
|
||||
# remove remaining header lines (e.g. pidstat -H 2 5)
|
||||
data_list = [i for i in data_list if not i.startswith('#')]
|
||||
|
||||
raw_output = simple_table_parse(data_list)
|
||||
|
||||
return raw_output if raw else _process(raw_output)
|
||||
|
||||
@@ -34,6 +34,7 @@ Schema:
|
||||
"percent_usr": float,
|
||||
"percent_system": float,
|
||||
"percent_guest": float,
|
||||
"percent_wait": float,
|
||||
"percent_cpu": float,
|
||||
"cpu": integer,
|
||||
"minflt_s": float,
|
||||
@@ -48,6 +49,9 @@ Schema:
|
||||
"kb_ccwr_s": float,
|
||||
"cswch_s": float,
|
||||
"nvcswch_s": float,
|
||||
"usr_ms": integer,
|
||||
"system_ms": integer,
|
||||
"guest_ms": integer,
|
||||
"command": string,
|
||||
|
||||
# below object only exists if using -qq or ignore_exceptions=True
|
||||
@@ -72,7 +76,7 @@ Examples:
|
||||
{"time":"1646859134","uid":"0","pid":"9","percent_usr":"0.00","perc...}
|
||||
...
|
||||
"""
|
||||
from typing import Dict, Iterable, Union
|
||||
from typing import List, Dict, Iterable, Union
|
||||
import jc.utils
|
||||
from jc.streaming import (
|
||||
add_jc_meta, streaming_input_type_check, streaming_line_input_type_check, raise_or_yield
|
||||
@@ -83,7 +87,7 @@ from jc.exceptions import ParseError
|
||||
|
||||
class info():
|
||||
"""Provides parser metadata (version, author, etc.)"""
|
||||
version = '1.1'
|
||||
version = '1.2'
|
||||
description = '`pidstat -H` command streaming parser'
|
||||
author = 'Kelly Brazil'
|
||||
author_email = 'kellyjonbrazil@gmail.com'
|
||||
@@ -107,11 +111,16 @@ def _process(proc_data: Dict) -> Dict:
|
||||
|
||||
Dictionary. Structured data to conform to the schema.
|
||||
"""
|
||||
int_list = {'time', 'uid', 'pid', 'cpu', 'vsz', 'rss', 'stksize', 'stkref'}
|
||||
int_list = {
|
||||
'time', 'uid', 'pid', 'cpu', 'vsz', 'rss', 'stksize', 'stkref',
|
||||
'usr_ms', 'system_ms', 'guest_ms'
|
||||
}
|
||||
|
||||
float_list = {'percent_usr', 'percent_system', 'percent_guest', 'percent_cpu',
|
||||
'minflt_s', 'majflt_s', 'percent_mem', 'kb_rd_s', 'kb_wr_s',
|
||||
'kb_ccwr_s', 'cswch_s', 'nvcswch_s'}
|
||||
float_list = {
|
||||
'percent_usr', 'percent_system', 'percent_guest', 'percent_wait',
|
||||
'percent_cpu', 'minflt_s', 'majflt_s', 'percent_mem', 'kb_rd_s',
|
||||
'kb_wr_s', 'kb_ccwr_s', 'cswch_s', 'nvcswch_s'
|
||||
}
|
||||
|
||||
for key in proc_data:
|
||||
if key in int_list:
|
||||
@@ -123,6 +132,14 @@ def _process(proc_data: Dict) -> Dict:
|
||||
return proc_data
|
||||
|
||||
|
||||
def normalize_header(header: str) -> str:
|
||||
return header.replace('#', ' ')\
|
||||
.replace('-', '_')\
|
||||
.replace('/', '_')\
|
||||
.replace('%', 'percent_')\
|
||||
.lower()
|
||||
|
||||
|
||||
@add_jc_meta
|
||||
def parse(
|
||||
data: Iterable[str],
|
||||
@@ -149,8 +166,8 @@ def parse(
|
||||
jc.utils.compatibility(__name__, info.compatible, quiet)
|
||||
streaming_input_type_check(data)
|
||||
|
||||
found_first_hash = False
|
||||
header = ''
|
||||
table_list: List = []
|
||||
header: str = ''
|
||||
|
||||
for line in data:
|
||||
try:
|
||||
@@ -161,29 +178,30 @@ def parse(
|
||||
# skip blank lines
|
||||
continue
|
||||
|
||||
if not line.startswith('#') and not found_first_hash:
|
||||
# skip preamble lines before header row
|
||||
if line.startswith('#'):
|
||||
if len(table_list) > 1:
|
||||
output_line = simple_table_parse(table_list)[0]
|
||||
yield output_line if raw else _process(output_line)
|
||||
header = ''
|
||||
|
||||
header = normalize_header(line)
|
||||
table_list = [header]
|
||||
continue
|
||||
|
||||
if line.startswith('#') and not found_first_hash:
|
||||
# normalize header
|
||||
header = line.replace('#', ' ')\
|
||||
.replace('/', '_')\
|
||||
.replace('%', 'percent_')\
|
||||
.lower()
|
||||
found_first_hash = True
|
||||
continue
|
||||
|
||||
if line.startswith('#') and found_first_hash:
|
||||
# skip header lines after first one is found
|
||||
continue
|
||||
|
||||
output_line = simple_table_parse([header, line])[0]
|
||||
|
||||
if output_line:
|
||||
if header:
|
||||
table_list.append(line)
|
||||
output_line = simple_table_parse(table_list)[0]
|
||||
yield output_line if raw else _process(output_line)
|
||||
else:
|
||||
raise ParseError('Not pidstat data')
|
||||
table_list = [header]
|
||||
continue
|
||||
|
||||
except Exception as e:
|
||||
yield raise_or_yield(ignore_exceptions, e, line)
|
||||
|
||||
try:
|
||||
if len(table_list) > 1:
|
||||
output_line = simple_table_parse(table_list)[0]
|
||||
yield output_line if raw else _process(output_line)
|
||||
|
||||
except Exception as e:
|
||||
yield raise_or_yield(ignore_exceptions, e, str(table_list))
|
||||
|
||||
@@ -30,6 +30,8 @@ Schema:
|
||||
"packets_received": integer,
|
||||
"packet_loss_percent": float,
|
||||
"duplicates": integer,
|
||||
"errors": integer,
|
||||
"corrupted": integer,
|
||||
"round_trip_ms_min": float,
|
||||
"round_trip_ms_avg": float,
|
||||
"round_trip_ms_max": float,
|
||||
@@ -157,6 +159,7 @@ Examples:
|
||||
]
|
||||
}
|
||||
"""
|
||||
import re
|
||||
import string
|
||||
import ipaddress
|
||||
import jc.utils
|
||||
@@ -164,7 +167,7 @@ import jc.utils
|
||||
|
||||
class info():
|
||||
"""Provides parser metadata (version, author, etc.)"""
|
||||
version = '1.8'
|
||||
version = '1.10'
|
||||
description = '`ping` and `ping6` command parser'
|
||||
author = 'Kelly Brazil'
|
||||
author_email = 'kellyjonbrazil@gmail.com'
|
||||
@@ -190,7 +193,7 @@ def _process(proc_data):
|
||||
"""
|
||||
int_list = {
|
||||
'data_bytes', 'packets_transmitted', 'packets_received', 'bytes', 'icmp_seq', 'ttl',
|
||||
'duplicates', 'vr', 'hl', 'tos', 'len', 'id', 'flg', 'off', 'pro', 'cks'
|
||||
'duplicates', 'corrupted', 'errors', 'vr', 'hl', 'tos', 'len', 'id', 'flg', 'off', 'pro', 'cks'
|
||||
}
|
||||
|
||||
float_list = {
|
||||
@@ -284,6 +287,8 @@ def _linux_parse(data):
|
||||
|
||||
if ipv4 and linedata[0][5] not in string.digits:
|
||||
hostname = True
|
||||
# fixup for missing hostname
|
||||
linedata[0] = linedata[0][:5] + 'nohost' + linedata[0][5:]
|
||||
elif ipv4 and linedata[0][5] in string.digits:
|
||||
hostname = False
|
||||
elif not ipv4 and ' (' in linedata[0]:
|
||||
@@ -314,45 +319,52 @@ def _linux_parse(data):
|
||||
|
||||
if line.startswith('---'):
|
||||
footer = True
|
||||
raw_output['destination'] = line.split()[1]
|
||||
if line[4] != ' ': # fixup for missing hostname
|
||||
raw_output['destination'] = line.split()[1]
|
||||
continue
|
||||
|
||||
if footer:
|
||||
if 'packets transmitted' in line:
|
||||
if ' duplicates,' in line:
|
||||
raw_output.update(
|
||||
{
|
||||
'packets_transmitted': line.split()[0],
|
||||
'packets_received': line.split()[3],
|
||||
'packet_loss_percent': line.split()[7].rstrip('%'),
|
||||
'duplicates': line.split()[5].lstrip('+'),
|
||||
'time_ms': line.split()[11].replace('ms', '')
|
||||
}
|
||||
)
|
||||
continue
|
||||
else:
|
||||
raw_output.update(
|
||||
{
|
||||
'packets_transmitted': line.split()[0],
|
||||
'packets_received': line.split()[3],
|
||||
'packet_loss_percent': line.split()[5].rstrip('%'),
|
||||
'duplicates': '0',
|
||||
'time_ms': line.split()[9].replace('ms', '')
|
||||
}
|
||||
)
|
||||
continue
|
||||
# Init in zero, to keep compatibility with previous behaviour
|
||||
if 'duplicates' not in raw_output:
|
||||
raw_output['duplicates'] = '0'
|
||||
|
||||
else:
|
||||
split_line = line.split(' = ')[1]
|
||||
split_line = split_line.split('/')
|
||||
raw_output.update(
|
||||
{
|
||||
'round_trip_ms_min': split_line[0],
|
||||
'round_trip_ms_avg': split_line[1],
|
||||
'round_trip_ms_max': split_line[2],
|
||||
'round_trip_ms_stddev': split_line[3].split()[0]
|
||||
}
|
||||
)
|
||||
#
|
||||
# See: https://github.com/dgibson/iputils/blob/master/ping_common.c#L995
|
||||
#
|
||||
m = re.search(r'(\d+) packets transmitted', line)
|
||||
if m:
|
||||
raw_output['packets_transmitted'] = m.group(1)
|
||||
|
||||
m = re.search(r'(\d+) received,', line)
|
||||
if m:
|
||||
raw_output['packets_received'] = m.group(1)
|
||||
|
||||
m = re.search(r'[+](\d+) duplicates', line)
|
||||
if m:
|
||||
raw_output['duplicates'] = m.group(1)
|
||||
|
||||
m = re.search(r'[+](\d+) corrupted', line)
|
||||
if m:
|
||||
raw_output['corrupted'] = m.group(1)
|
||||
|
||||
m = re.search(r'[+](\d+) errors', line)
|
||||
if m:
|
||||
raw_output['errors'] = m.group(1)
|
||||
|
||||
m = re.search(r'([\d\.]+)% packet loss', line)
|
||||
if m:
|
||||
raw_output['packet_loss_percent'] = m.group(1)
|
||||
|
||||
m = re.search(r'time (\d+)ms', line)
|
||||
if m:
|
||||
raw_output['time_ms'] = m.group(1)
|
||||
|
||||
m = re.search(r'rtt min\/avg\/max\/mdev += +([\d\.]+)\/([\d\.]+)\/([\d\.]+)\/([\d\.]+) ms', line)
|
||||
if m:
|
||||
raw_output['round_trip_ms_min'] = m.group(1)
|
||||
raw_output['round_trip_ms_avg'] = m.group(2)
|
||||
raw_output['round_trip_ms_max'] = m.group(3)
|
||||
raw_output['round_trip_ms_stddev'] = m.group(4)
|
||||
|
||||
# ping response lines
|
||||
else:
|
||||
|
||||
@@ -31,7 +31,7 @@ Schema:
|
||||
"source_ip": string,
|
||||
"destination_ip": string,
|
||||
"sent_bytes": integer,
|
||||
"pattern": string, # (null if not set)
|
||||
"pattern": string, # null if not set
|
||||
"destination": string,
|
||||
"timestamp": float,
|
||||
"response_bytes": integer,
|
||||
@@ -44,10 +44,12 @@ Schema:
|
||||
"packets_received": integer,
|
||||
"packet_loss_percent": float,
|
||||
"duplicates": integer,
|
||||
"round_trip_ms_min": float,
|
||||
"round_trip_ms_avg": float,
|
||||
"round_trip_ms_max": float,
|
||||
"round_trip_ms_stddev": float,
|
||||
"errors": integer, # null if not set
|
||||
"corrupted": integer, # null if not set
|
||||
"round_trip_ms_min": float, # null if not set
|
||||
"round_trip_ms_avg": float, # null if not set
|
||||
"round_trip_ms_max": float, # null if not set
|
||||
"round_trip_ms_stddev": float, # null if not set
|
||||
|
||||
# below object only exists if using -qq or ignore_exceptions=True
|
||||
"_jc_meta": {
|
||||
@@ -74,6 +76,7 @@ Examples:
|
||||
{"type":"reply","destination_ip":"1.1.1.1","sent_bytes":"56","patte...}
|
||||
...
|
||||
"""
|
||||
import re
|
||||
import string
|
||||
import ipaddress
|
||||
import jc.utils
|
||||
@@ -85,7 +88,7 @@ from jc.exceptions import ParseError
|
||||
|
||||
class info():
|
||||
"""Provides parser metadata (version, author, etc.)"""
|
||||
version = '1.2'
|
||||
version = '1.4'
|
||||
description = '`ping` and `ping6` command streaming parser'
|
||||
author = 'Kelly Brazil'
|
||||
author_email = 'kellyjonbrazil@gmail.com'
|
||||
@@ -110,13 +113,14 @@ def _process(proc_data):
|
||||
Dictionary. Structured data to conform to the schema.
|
||||
"""
|
||||
int_list = {
|
||||
'sent_bytes', 'packets_transmitted', 'packets_received', 'response_bytes', 'icmp_seq',
|
||||
'ttl', 'duplicates', 'vr', 'hl', 'tos', 'len', 'id', 'flg', 'off', 'pro', 'cks'
|
||||
'sent_bytes', 'packets_transmitted', 'packets_received',
|
||||
'response_bytes', 'icmp_seq', 'ttl', 'duplicates', 'vr', 'hl', 'tos',
|
||||
'len', 'id', 'flg', 'off', 'pro', 'cks', 'errors', 'corrupted'
|
||||
}
|
||||
|
||||
float_list = {
|
||||
'packet_loss_percent', 'round_trip_ms_min', 'round_trip_ms_avg', 'round_trip_ms_max',
|
||||
'round_trip_ms_stddev', 'timestamp', 'time_ms'
|
||||
'packet_loss_percent', 'round_trip_ms_min', 'round_trip_ms_avg',
|
||||
'round_trip_ms_max', 'round_trip_ms_stddev', 'timestamp', 'time_ms'
|
||||
}
|
||||
|
||||
for key in proc_data:
|
||||
@@ -144,6 +148,12 @@ class _state:
|
||||
packet_loss_percent = None
|
||||
time_ms = None
|
||||
duplicates = None
|
||||
corrupted = None
|
||||
errors = None
|
||||
round_trip_ms_min = None
|
||||
round_trip_ms_avg = None
|
||||
round_trip_ms_max = None
|
||||
round_trip_ms_stddev = None
|
||||
|
||||
|
||||
def _ipv6_in(line):
|
||||
@@ -340,6 +350,8 @@ def _linux_parse(line, s):
|
||||
|
||||
if s.ipv4 and line[5] not in string.digits:
|
||||
s.hostname = True
|
||||
# fixup for missing hostname
|
||||
line = line[:5] + 'nohost' + line[5:]
|
||||
elif s.ipv4 and line[5] in string.digits:
|
||||
s.hostname = False
|
||||
elif not s.ipv4 and ' (' in line:
|
||||
@@ -367,24 +379,44 @@ def _linux_parse(line, s):
|
||||
return None
|
||||
|
||||
if s.footer:
|
||||
if 'packets transmitted' in line:
|
||||
if ' duplicates,' in line:
|
||||
s.packets_transmitted = line.split()[0]
|
||||
s.packets_received = line.split()[3]
|
||||
s.packet_loss_percent = line.split()[7].rstrip('%')
|
||||
s.duplicates = line.split()[5].lstrip('+')
|
||||
s.time_ms = line.split()[11].replace('ms', '')
|
||||
return None
|
||||
#
|
||||
# See: https://github.com/dgibson/iputils/blob/master/ping_common.c#L995
|
||||
#
|
||||
m = re.search(r'(\d+) packets transmitted', line)
|
||||
if m:
|
||||
s.packets_transmitted = m.group(1)
|
||||
|
||||
s.packets_transmitted = line.split()[0]
|
||||
s.packets_received = line.split()[3]
|
||||
s.packet_loss_percent = line.split()[5].rstrip('%')
|
||||
s.duplicates = '0'
|
||||
s.time_ms = line.split()[9].replace('ms', '')
|
||||
return None
|
||||
m = re.search(r'(\d+) received,', line)
|
||||
if m:
|
||||
s.packets_received = m.group(1)
|
||||
|
||||
m = re.search(r'[+](\d+) duplicates', line)
|
||||
if m:
|
||||
s.duplicates = m.group(1)
|
||||
|
||||
m = re.search(r'[+](\d+) corrupted', line)
|
||||
if m:
|
||||
s.corrupted = m.group(1)
|
||||
|
||||
m = re.search(r'[+](\d+) errors', line)
|
||||
if m:
|
||||
s.errors = m.group(1)
|
||||
|
||||
m = re.search(r'([\d\.]+)% packet loss', line)
|
||||
if m:
|
||||
s.packet_loss_percent = m.group(1)
|
||||
|
||||
m = re.search(r'time (\d+)ms', line)
|
||||
if m:
|
||||
s.time_ms = m.group(1)
|
||||
|
||||
m = re.search(r'rtt min\/avg\/max\/mdev += +([\d\.]+)\/([\d\.]+)\/([\d\.]+)\/([\d\.]+) ms', line)
|
||||
if m:
|
||||
s.round_trip_ms_min = m.group(1)
|
||||
s.round_trip_ms_avg = m.group(2)
|
||||
s.round_trip_ms_max = m.group(3)
|
||||
s.round_trip_ms_stddev = m.group(4)
|
||||
|
||||
split_line = line.split(' = ')[1]
|
||||
split_line = split_line.split('/')
|
||||
output_line = {
|
||||
'type': 'summary',
|
||||
'destination_ip': s.destination_ip or None,
|
||||
@@ -392,15 +424,16 @@ def _linux_parse(line, s):
|
||||
'pattern': s.pattern or None,
|
||||
'packets_transmitted': s.packets_transmitted or None,
|
||||
'packets_received': s.packets_received or None,
|
||||
'packet_loss_percent': s.packet_loss_percent or None,
|
||||
'duplicates': s.duplicates or None,
|
||||
'time_ms': s.time_ms or None,
|
||||
'round_trip_ms_min': split_line[0],
|
||||
'round_trip_ms_avg': split_line[1],
|
||||
'round_trip_ms_max': split_line[2],
|
||||
'round_trip_ms_stddev': split_line[3].split()[0]
|
||||
'packet_loss_percent': s.packet_loss_percent,
|
||||
'duplicates': s.duplicates or '0',
|
||||
'errors': s.errors,
|
||||
'corrupted': s.corrupted,
|
||||
'time_ms': s.time_ms,
|
||||
'round_trip_ms_min': s.round_trip_ms_min,
|
||||
'round_trip_ms_avg': s.round_trip_ms_avg,
|
||||
'round_trip_ms_max': s.round_trip_ms_max,
|
||||
'round_trip_ms_stddev': s.round_trip_ms_stddev
|
||||
}
|
||||
|
||||
return output_line
|
||||
|
||||
# ping response lines
|
||||
@@ -486,6 +519,7 @@ def parse(data, raw=False, quiet=False, ignore_exceptions=False):
|
||||
streaming_input_type_check(data)
|
||||
|
||||
s = _state()
|
||||
summary_obj = {}
|
||||
|
||||
for line in data:
|
||||
try:
|
||||
@@ -526,6 +560,12 @@ def parse(data, raw=False, quiet=False, ignore_exceptions=False):
|
||||
if s.os_detected and s.linux:
|
||||
output_line = _linux_parse(line, s)
|
||||
|
||||
# summary can be multiple lines so don't output until the end
|
||||
if output_line:
|
||||
if output_line.get('type', None) == 'summary':
|
||||
summary_obj = output_line
|
||||
continue
|
||||
|
||||
elif s.os_detected and s.bsd:
|
||||
output_line = _bsd_parse(line, s)
|
||||
|
||||
@@ -540,3 +580,10 @@ def parse(data, raw=False, quiet=False, ignore_exceptions=False):
|
||||
|
||||
except Exception as e:
|
||||
yield raise_or_yield(ignore_exceptions, e, line)
|
||||
|
||||
# yield summary, if it exists
|
||||
try:
|
||||
if summary_obj:
|
||||
yield summary_obj if raw else _process(summary_obj)
|
||||
except Exception as e:
|
||||
yield raise_or_yield(ignore_exceptions, e, str(summary_obj))
|
||||
|
||||
@@ -194,6 +194,7 @@ def parse(
|
||||
net_packet_p = re.compile(r'^sk RefCnt Type Proto Iface R Rmem User Inode\n')
|
||||
net_protocols_p = re.compile(r'^protocol size sockets memory press maxhdr slab module cl co di ac io in de sh ss gs se re sp bi br ha uh gp em\n')
|
||||
net_route_p = re.compile(r'^Iface\tDestination\tGateway \tFlags\tRefCnt\tUse\tMetric\tMask\t\tMTU\tWindow\tIRTT\s+\n')
|
||||
net_tcp_p = re.compile(r'^\s+sl\s+local_address\s+(?:rem_address|remote_address)\s+st\s+tx_queue\s+rx_queue\s+tr\s+tm->when\s+retrnsmt\s+uid\s+timeout\s+inode')
|
||||
net_unix_p = re.compile(r'^Num RefCount Protocol Flags Type St Inode Path\n')
|
||||
|
||||
pid_fdinfo_p = re.compile(r'^pos:\t\d+\nflags:\t\d+\nmnt_id:\t\d+\n')
|
||||
@@ -204,7 +205,7 @@ def parse(
|
||||
pid_smaps_p = re.compile(r'^[0-9a-f]{12}-[0-9a-f]{12} [rwxsp\-]{4} [0-9a-f]{8} [0-9a-f]{2}:[0-9a-f]{2} \d+ [^\n]+\nSize:\s+\d+ \S\S\n')
|
||||
pid_stat_p = re.compile(r'^\d+ \(.+\) \S \d+ \d+ \d+ \d+ -?\d+ (?:\d+ ){43}\d+$', re.DOTALL)
|
||||
pid_statm_p = re.compile(r'^\d+ \d+ \d+\s\d+\s\d+\s\d+\s\d+$')
|
||||
pid_status_p = re.compile(r'^Name:\t.+\nUmask:\t\d+\nState:\t.+\nTgid:\t\d+\n')
|
||||
pid_status_p = re.compile(r'^Name:\t.+\n(?:Umask:\t\d+\n)?State:\t.+\nTgid:\t\d+\n')
|
||||
|
||||
# scsi_device_info = re.compile(r"^'\w+' '.+' 0x\d+")
|
||||
# scsi_scsi_p = re.compile(r'^Attached devices:\nHost: \w+ ')
|
||||
@@ -249,6 +250,7 @@ def parse(
|
||||
net_packet_p: 'proc_net_packet',
|
||||
net_protocols_p: 'proc_net_protocols',
|
||||
net_route_p: 'proc_net_route',
|
||||
net_tcp_p: 'proc_net_tcp',
|
||||
net_unix_p: 'proc_net_unix',
|
||||
net_ipv6_route_p: 'proc_net_ipv6_route', # before net_dev_mcast
|
||||
net_dev_mcast_p: 'proc_net_dev_mcast', # after net_ipv6_route
|
||||
|
||||
293
jc/parsers/proc_net_tcp.py
Normal file
293
jc/parsers/proc_net_tcp.py
Normal file
@@ -0,0 +1,293 @@
|
||||
"""jc - JSON Convert `/proc/net/tcp` and `proc/net/tcp6` file parser
|
||||
|
||||
IPv4 and IPv6 addresses are converted to standard notation unless the raw
|
||||
(--raw) option is used.
|
||||
|
||||
Usage (cli):
|
||||
|
||||
$ cat /proc/net/tcp | jc --proc
|
||||
|
||||
or
|
||||
|
||||
$ jc /proc/net/tcp
|
||||
|
||||
or
|
||||
|
||||
$ cat /proc/net/tcp | jc --proc-net-tcp
|
||||
|
||||
Usage (module):
|
||||
|
||||
import jc
|
||||
result = jc.parse('proc', proc_net_tcp_file)
|
||||
|
||||
or
|
||||
|
||||
import jc
|
||||
result = jc.parse('proc_net_tcp', proc_net_tcp_file)
|
||||
|
||||
Schema:
|
||||
|
||||
Field names and types gathered from the following:
|
||||
|
||||
https://www.kernel.org/doc/Documentation/networking/proc_net_tcp.txt
|
||||
|
||||
https://github.com/torvalds/linux/blob/master/net/ipv4/tcp_ipv4.c
|
||||
|
||||
https://github.com/torvalds/linux/blob/master/net/ipv6/tcp_ipv6.c
|
||||
|
||||
[
|
||||
{
|
||||
"entry": integer,
|
||||
"local_address": string,
|
||||
"local_port": integer,
|
||||
"remote_address": string,
|
||||
"remote_port": integer,
|
||||
"state": string,
|
||||
"tx_queue": string,
|
||||
"rx_queue": string,
|
||||
"timer_active": integer,
|
||||
"jiffies_until_timer_expires": string,
|
||||
"unrecovered_rto_timeouts": string,
|
||||
"uid": integer,
|
||||
"unanswered_0_window_probes": integer,
|
||||
"inode": integer,
|
||||
"sock_ref_count": integer,
|
||||
"sock_mem_loc": string,
|
||||
"retransmit_timeout": integer,
|
||||
"soft_clock_tick": integer,
|
||||
"ack_quick_pingpong": integer,
|
||||
"sending_congestion_window": integer,
|
||||
"slow_start_size_threshold": integer
|
||||
}
|
||||
]
|
||||
|
||||
Examples:
|
||||
|
||||
$ cat /proc/net/tcp | jc --proc -p
|
||||
[
|
||||
{
|
||||
"entry": "0",
|
||||
"local_address": "10.0.0.28",
|
||||
"local_port": 42082,
|
||||
"remote_address": "64.12.0.108",
|
||||
"remote_port": 80,
|
||||
"state": "04",
|
||||
"tx_queue": "00000001",
|
||||
"rx_queue": "00000000",
|
||||
"timer_active": 1,
|
||||
"jiffies_until_timer_expires": "00000015",
|
||||
"unrecovered_rto_timeouts": "00000000",
|
||||
"uid": 0,
|
||||
"unanswered_0_window_probes": 0,
|
||||
"inode": 0,
|
||||
"sock_ref_count": 3,
|
||||
"sock_mem_loc": "ffff8c7a0de930c0",
|
||||
"retransmit_timeout": 21,
|
||||
"soft_clock_tick": 4,
|
||||
"ack_quick_pingpong": 30,
|
||||
"sending_congestion_window": 10,
|
||||
"slow_start_size_threshold": -1
|
||||
},
|
||||
{
|
||||
"entry": "1",
|
||||
"local_address": "10.0.0.28",
|
||||
"local_port": 38864,
|
||||
"remote_address": "104.244.42.65",
|
||||
"remote_port": 80,
|
||||
"state": "06",
|
||||
"tx_queue": "00000000",
|
||||
"rx_queue": "00000000",
|
||||
"timer_active": 3,
|
||||
"jiffies_until_timer_expires": "000007C5",
|
||||
"unrecovered_rto_timeouts": "00000000",
|
||||
"uid": 0,
|
||||
"unanswered_0_window_probes": 0,
|
||||
"inode": 0,
|
||||
"sock_ref_count": 3,
|
||||
"sock_mem_loc": "ffff8c7a12d31aa0"
|
||||
},
|
||||
...
|
||||
]
|
||||
|
||||
$ cat /proc/net/tcp | jc --proc -p -r
|
||||
[
|
||||
{
|
||||
"entry": "1",
|
||||
"local_address": "1C00000A",
|
||||
"local_port": "A462",
|
||||
"remote_address": "6C000C40",
|
||||
"remote_port": "0050",
|
||||
"state": "04",
|
||||
"tx_queue": "00000001",
|
||||
"rx_queue": "00000000",
|
||||
"timer_active": "01",
|
||||
"jiffies_until_timer_expires": "00000015",
|
||||
"unrecovered_rto_timeouts": "00000000",
|
||||
"uid": "0",
|
||||
"unanswered_0_window_probes": "0",
|
||||
"inode": "0",
|
||||
"sock_ref_count": "3",
|
||||
"sock_mem_loc": "ffff8c7a0de930c0",
|
||||
"retransmit_timeout": "21",
|
||||
"soft_clock_tick": "4",
|
||||
"ack_quick_pingpong": "30",
|
||||
"sending_congestion_window": "10",
|
||||
"slow_start_size_threshold": "-1"
|
||||
},
|
||||
{
|
||||
"entry": "2",
|
||||
"local_address": "1C00000A",
|
||||
"local_port": "97D0",
|
||||
"remote_address": "412AF468",
|
||||
"remote_port": "0050",
|
||||
"state": "06",
|
||||
"tx_queue": "00000000",
|
||||
"rx_queue": "00000000",
|
||||
"timer_active": "03",
|
||||
"jiffies_until_timer_expires": "000007C5",
|
||||
"unrecovered_rto_timeouts": "00000000",
|
||||
"uid": "0",
|
||||
"unanswered_0_window_probes": "0",
|
||||
"inode": "0",
|
||||
"sock_ref_count": "3",
|
||||
"sock_mem_loc": "ffff8c7a12d31aa0"
|
||||
},
|
||||
...
|
||||
]
|
||||
"""
|
||||
import binascii
|
||||
import socket
|
||||
import struct
|
||||
from typing import List, Dict
|
||||
import jc.utils
|
||||
|
||||
|
||||
class info():
|
||||
"""Provides parser metadata (version, author, etc.)"""
|
||||
version = '1.0'
|
||||
description = '`/proc/net/tcp` and `/proc/net/tcp6` file parser'
|
||||
author = 'Alvin Solomon'
|
||||
author_email = 'alvinms01@gmail.com'
|
||||
compatible = ['linux']
|
||||
tags = ['file']
|
||||
hidden = True
|
||||
|
||||
|
||||
__version__ = info.version
|
||||
|
||||
|
||||
def hex_to_ip(hexaddr: str) -> str:
|
||||
if len(hexaddr) == 8:
|
||||
addr_long = int(hexaddr, 16)
|
||||
return socket.inet_ntop(socket.AF_INET, struct.pack("<L", addr_long))
|
||||
elif len(hexaddr) == 32:
|
||||
addr = binascii.a2b_hex(hexaddr)
|
||||
addr_tup = struct.unpack('>IIII', addr)
|
||||
addr_bytes = struct.pack('@IIII', *addr_tup)
|
||||
return socket.inet_ntop(socket.AF_INET6, addr_bytes)
|
||||
|
||||
return ''
|
||||
|
||||
|
||||
def _process(proc_data: List[Dict]) -> List[Dict]:
|
||||
"""
|
||||
Final processing to conform to the schema.
|
||||
|
||||
Parameters:
|
||||
|
||||
proc_data: (List of Dictionaries) raw structured data to process
|
||||
|
||||
Returns:
|
||||
|
||||
List of Dictionaries. Structured to conform to the schema.
|
||||
"""
|
||||
int_list = {
|
||||
'timer_active', 'uid', 'unanswered_0_window_probes', 'inode',
|
||||
'sock_ref_count', 'retransmit_timeout', 'soft_clock_tick',
|
||||
'ack_quick_pingpong', 'sending_congestion_window',
|
||||
'slow_start_size_threshold'
|
||||
}
|
||||
|
||||
for entry in proc_data:
|
||||
if 'local_address' in entry:
|
||||
entry['local_address'] = hex_to_ip(entry['local_address'])
|
||||
entry['local_port'] = int(entry['local_port'], 16)
|
||||
entry['remote_address'] = hex_to_ip(entry['remote_address'])
|
||||
entry['remote_port'] = int(entry['remote_port'], 16)
|
||||
|
||||
for item in int_list:
|
||||
if item in entry:
|
||||
entry[item] = jc.utils.convert_to_int(entry[item])
|
||||
|
||||
return proc_data
|
||||
|
||||
|
||||
def parse(
|
||||
data: str,
|
||||
raw: bool = False,
|
||||
quiet: bool = False
|
||||
) -> List[Dict]:
|
||||
"""
|
||||
Main text parsing function
|
||||
|
||||
Parameters:
|
||||
|
||||
data: (string) text data to parse
|
||||
raw: (boolean) unprocessed output if True
|
||||
quiet: (boolean) suppress warning messages if True
|
||||
|
||||
Returns:
|
||||
|
||||
List of Dictionaries. Raw or processed structured data.
|
||||
"""
|
||||
jc.utils.compatibility(__name__, info.compatible, quiet)
|
||||
jc.utils.input_type_check(data)
|
||||
|
||||
raw_output: List = []
|
||||
|
||||
if jc.utils.has_data(data):
|
||||
|
||||
line_data = data.splitlines()[1:]
|
||||
|
||||
for entry in line_data:
|
||||
line = entry.split()
|
||||
output_line = {}
|
||||
output_line['entry'] = line[0][:-1]
|
||||
|
||||
local_ip_port = line[1]
|
||||
local_ip = local_ip_port.split(':')[0]
|
||||
local_port = local_ip_port.split(':')[1]
|
||||
|
||||
output_line['local_address'] = local_ip
|
||||
output_line['local_port'] = local_port
|
||||
|
||||
remote_ip_port = line[2]
|
||||
remote_ip = remote_ip_port.split(':')[0]
|
||||
remote_port = remote_ip_port.split(':')[1]
|
||||
|
||||
output_line['remote_address'] = remote_ip
|
||||
output_line['remote_port'] = remote_port
|
||||
|
||||
output_line['state'] = line[3]
|
||||
output_line['tx_queue'] = line[4][:8]
|
||||
output_line['rx_queue'] = line[4][9:]
|
||||
output_line['timer_active'] = line[5][:2]
|
||||
output_line['jiffies_until_timer_expires'] = line[5][3:]
|
||||
output_line['unrecovered_rto_timeouts'] = line[6]
|
||||
output_line['uid'] = line[7]
|
||||
output_line['unanswered_0_window_probes'] = line[8]
|
||||
output_line['inode'] = line[9]
|
||||
output_line['sock_ref_count'] = line[10]
|
||||
output_line['sock_mem_loc'] = line[11]
|
||||
|
||||
# fields not always included
|
||||
if len(line) > 12:
|
||||
output_line['retransmit_timeout'] = line[12]
|
||||
output_line['soft_clock_tick'] = line[13]
|
||||
output_line['ack_quick_pingpong'] = line[14]
|
||||
output_line['sending_congestion_window'] = line[15]
|
||||
output_line['slow_start_size_threshold'] = line[16]
|
||||
|
||||
raw_output.append(output_line)
|
||||
|
||||
return raw_output if raw else _process(raw_output)
|
||||
@@ -109,7 +109,8 @@ Examples:
|
||||
"mw",
|
||||
"me",
|
||||
"dw",
|
||||
"sd"
|
||||
"sd",
|
||||
"mp"
|
||||
],
|
||||
"VmFlags_pretty": [
|
||||
"readable",
|
||||
@@ -211,6 +212,7 @@ def _process(proc_data: List[Dict]) -> List[Dict]:
|
||||
'mw': 'may write',
|
||||
'me': 'may execute',
|
||||
'ms': 'may share',
|
||||
'mp': 'MPX-specific VMA',
|
||||
'gd': 'stack segment growns down',
|
||||
'pf': 'pure PFN range',
|
||||
'dw': 'disabled write to the mapped file',
|
||||
@@ -274,10 +276,10 @@ def parse(
|
||||
|
||||
if jc.utils.has_data(data):
|
||||
map_line = re.compile(r'''
|
||||
^(?P<start>[0-9a-f]{12,16})-
|
||||
(?P<end>[0-9a-f]{12,16})\s
|
||||
^(?P<start>[0-9a-f]{8,16})-
|
||||
(?P<end>[0-9a-f]{8,16})\s
|
||||
(?P<perms>[rwxsp\-]{4})\s
|
||||
(?P<offset>[0-9a-f]{8})\s
|
||||
(?P<offset>[0-9a-f]{8,9})\s
|
||||
(?P<maj>[0-9a-f]{2}):
|
||||
(?P<min>[0-9a-f]{2})\s
|
||||
(?P<inode>\d+)\s+
|
||||
|
||||
18
jc/parsers/pyedid/LICENSE
Normal file
18
jc/parsers/pyedid/LICENSE
Normal file
@@ -0,0 +1,18 @@
|
||||
Copyright 2019-2020 Jonas Lieb, Davydov Denis
|
||||
|
||||
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.
|
||||
0
jc/parsers/pyedid/__init__.py
Normal file
0
jc/parsers/pyedid/__init__.py
Normal file
171
jc/parsers/pyedid/edid.py
Executable file
171
jc/parsers/pyedid/edid.py
Executable file
@@ -0,0 +1,171 @@
|
||||
"""
|
||||
Edid module
|
||||
"""
|
||||
|
||||
import struct
|
||||
from collections import namedtuple
|
||||
from typing import ByteString
|
||||
|
||||
__all__ = ["Edid"]
|
||||
|
||||
|
||||
class Edid:
|
||||
"""Edid class
|
||||
|
||||
Raises:
|
||||
`ValueError`: if invalid edid data
|
||||
"""
|
||||
|
||||
_STRUCT_FORMAT = (
|
||||
"<" # little-endian
|
||||
"8s" # constant header (8 bytes)
|
||||
"H" # manufacturer id (2 bytes)
|
||||
"H" # product id (2 bytes)
|
||||
"I" # serial number (4 bytes)
|
||||
"B" # manufactoring week (1 byte)
|
||||
"B" # manufactoring year (1 byte)
|
||||
"B" # edid version (1 byte)
|
||||
"B" # edid revision (1 byte)
|
||||
"B" # video input type (1 byte)
|
||||
"B" # horizontal size in cm (1 byte)
|
||||
"B" # vertical size in cm (1 byte)
|
||||
"B" # display gamma (1 byte)
|
||||
"B" # supported features (1 byte)
|
||||
"10s" # color characteristics (10 bytes)
|
||||
"H" # supported timings (2 bytes)
|
||||
"B" # reserved timing (1 byte)
|
||||
"16s" # EDID supported timings (16 bytes)
|
||||
"18s" # detailed timing block 1 (18 bytes)
|
||||
"18s" # detailed timing block 2 (18 bytes)
|
||||
"18s" # detailed timing block 3 (18 bytes)
|
||||
"18s" # detailed timing block 4 (18 bytes)
|
||||
"B" # extension flag (1 byte)
|
||||
"B"
|
||||
) # checksum (1 byte)
|
||||
|
||||
_TIMINGS = {
|
||||
0: (1280, 1024, 75.0),
|
||||
1: (1024, 768, 75.0),
|
||||
2: (1024, 768, 70.0),
|
||||
3: (1024, 768, 60.0),
|
||||
4: (1024, 768, 87.0),
|
||||
5: (832, 624, 75.0),
|
||||
6: (800, 600, 75.0),
|
||||
7: (800, 600, 72.0),
|
||||
8: (800, 600, 60.0),
|
||||
9: (800, 600, 56.0),
|
||||
10: (640, 480, 75.0),
|
||||
11: (640, 480, 72.0),
|
||||
12: (640, 480, 67.0),
|
||||
13: (640, 480, 60.0),
|
||||
14: (720, 400, 88.0),
|
||||
15: (720, 400, 70.0),
|
||||
}
|
||||
|
||||
_ASPECT_RATIOS = {
|
||||
0b00: (16, 10),
|
||||
0b01: ( 4, 3),
|
||||
0b10: ( 5, 4),
|
||||
0b11: (16, 9),
|
||||
}
|
||||
|
||||
_RawEdid = namedtuple("RawEdid",
|
||||
("header",
|
||||
"manu_id",
|
||||
"prod_id",
|
||||
"serial_no",
|
||||
"manu_week",
|
||||
"manu_year",
|
||||
"edid_version",
|
||||
"edid_revision",
|
||||
"input_type",
|
||||
"width",
|
||||
"height",
|
||||
"gamma",
|
||||
"features",
|
||||
"color",
|
||||
"timings_supported",
|
||||
"timings_reserved",
|
||||
"timings_edid",
|
||||
"timing_1",
|
||||
"timing_2",
|
||||
"timing_3",
|
||||
"timing_4",
|
||||
"extension",
|
||||
"checksum")
|
||||
)
|
||||
|
||||
def __init__(self, edid: ByteString):
|
||||
self._parse_edid(edid)
|
||||
|
||||
def _parse_edid(self, edid: ByteString):
|
||||
"""Convert edid byte string to edid object"""
|
||||
if struct.calcsize(self._STRUCT_FORMAT) != 128:
|
||||
raise ValueError("Wrong edid size.")
|
||||
|
||||
if sum(map(int, edid)) % 256 != 0:
|
||||
raise ValueError("Checksum mismatch.")
|
||||
|
||||
unpacked = struct.unpack(self._STRUCT_FORMAT, edid)
|
||||
raw_edid = self._RawEdid(*unpacked)
|
||||
|
||||
if raw_edid.header != b'\x00\xff\xff\xff\xff\xff\xff\x00':
|
||||
raise ValueError("Invalid header.")
|
||||
|
||||
self.raw = edid
|
||||
self.manufacturer_id = raw_edid.manu_id
|
||||
self.product = raw_edid.prod_id
|
||||
self.year = raw_edid.manu_year + 1990
|
||||
self.edid_version = "{:d}.{:d}".format(raw_edid.edid_version, raw_edid.edid_revision)
|
||||
self.type = "digital" if (raw_edid.input_type & 0xFF) else "analog"
|
||||
self.width = float(raw_edid.width)
|
||||
self.height = float(raw_edid.height)
|
||||
self.gamma = (raw_edid.gamma+100)/100
|
||||
self.dpms_standby = bool(raw_edid.features & 0xFF)
|
||||
self.dpms_suspend = bool(raw_edid.features & 0x7F)
|
||||
self.dpms_activeoff = bool(raw_edid.features & 0x3F)
|
||||
|
||||
self.resolutions = []
|
||||
for i in range(16):
|
||||
bit = raw_edid.timings_supported & (1 << i)
|
||||
if bit:
|
||||
self.resolutions.append(self._TIMINGS[i])
|
||||
|
||||
for i in range(8):
|
||||
bytes_data = raw_edid.timings_edid[2*i:2*i+2]
|
||||
if bytes_data == b'\x01\x01':
|
||||
continue
|
||||
byte1, byte2 = bytes_data
|
||||
x_res = 8*(int(byte1)+31)
|
||||
aspect_ratio = self._ASPECT_RATIOS[(byte2>>6) & 0b11]
|
||||
y_res = int(x_res * aspect_ratio[1]/aspect_ratio[0])
|
||||
rate = (int(byte2) & 0b00111111) + 60.0
|
||||
self.resolutions.append((x_res, y_res, rate))
|
||||
|
||||
self.name = None
|
||||
self.serial = None
|
||||
|
||||
for timing_bytes in (raw_edid.timing_1, raw_edid.timing_2, raw_edid.timing_3, raw_edid.timing_4):
|
||||
# "other" descriptor
|
||||
if timing_bytes[0:2] == b'\x00\x00':
|
||||
timing_type = timing_bytes[3]
|
||||
if timing_type in (0xFF, 0xFE, 0xFC):
|
||||
buffer = timing_bytes[5:]
|
||||
buffer = buffer.partition(b"\x0a")[0]
|
||||
text = buffer.decode("cp437")
|
||||
if timing_type == 0xFF:
|
||||
self.serial = text
|
||||
elif timing_type == 0xFC:
|
||||
self.name = text
|
||||
|
||||
if not self.serial:
|
||||
self.serial = raw_edid.serial_no
|
||||
|
||||
def __repr__(self):
|
||||
clsname = self.__class__.__name__
|
||||
attributes = []
|
||||
for name in dir(self):
|
||||
if not name.startswith("_"):
|
||||
value = getattr(self, name)
|
||||
attributes.append("\t{}={}".format(name, value))
|
||||
return "{}(\n{}\n)".format(clsname, ", \n".join(attributes))
|
||||
0
jc/parsers/pyedid/helpers/__init__.py
Normal file
0
jc/parsers/pyedid/helpers/__init__.py
Normal file
61
jc/parsers/pyedid/helpers/edid_helper.py
Normal file
61
jc/parsers/pyedid/helpers/edid_helper.py
Normal file
@@ -0,0 +1,61 @@
|
||||
"""
|
||||
EDID helper
|
||||
"""
|
||||
|
||||
from subprocess import CalledProcessError, check_output
|
||||
from typing import ByteString, List
|
||||
|
||||
__all__ = ["EdidHelper"]
|
||||
|
||||
|
||||
class EdidHelper:
|
||||
"""Class for working with EDID data"""
|
||||
|
||||
@staticmethod
|
||||
def hex2bytes(hex_data: str) -> ByteString:
|
||||
"""Convert hex EDID string to bytes
|
||||
|
||||
Args:
|
||||
hex_data (str): hex edid string
|
||||
|
||||
Returns:
|
||||
ByteString: edid byte string
|
||||
"""
|
||||
# delete edid 1.3 additional block
|
||||
if len(hex_data) > 256:
|
||||
hex_data = hex_data[:256]
|
||||
|
||||
numbers = []
|
||||
for i in range(0, len(hex_data), 2):
|
||||
pair = hex_data[i : i + 2]
|
||||
numbers.append(int(pair, 16))
|
||||
return bytes(numbers)
|
||||
|
||||
@classmethod
|
||||
def get_edids(cls) -> List[ByteString]:
|
||||
"""Get edids from xrandr
|
||||
|
||||
Raises:
|
||||
`RuntimeError`: if error with retrieving xrandr util data
|
||||
|
||||
Returns:
|
||||
List[ByteString]: list with edids
|
||||
"""
|
||||
try:
|
||||
output = check_output(["xrandr", "--verbose"])
|
||||
except (CalledProcessError, FileNotFoundError) as err:
|
||||
raise RuntimeError(
|
||||
"Error retrieving xrandr util data: {}".format(err)
|
||||
) from None
|
||||
|
||||
edids = []
|
||||
lines = output.splitlines()
|
||||
for i, line in enumerate(lines):
|
||||
line = line.decode().strip()
|
||||
if line.startswith("EDID:"):
|
||||
selection = lines[i + 1 : i + 9]
|
||||
selection = list(s.decode().strip() for s in selection)
|
||||
selection = "".join(selection)
|
||||
bytes_section = cls.hex2bytes(selection)
|
||||
edids.append(bytes_section)
|
||||
return edids
|
||||
136
jc/parsers/pyedid/helpers/registry.py
Normal file
136
jc/parsers/pyedid/helpers/registry.py
Normal file
@@ -0,0 +1,136 @@
|
||||
"""
|
||||
Module for working with PNP ID REGISTRY
|
||||
"""
|
||||
|
||||
import csv
|
||||
import string
|
||||
from html.parser import HTMLParser
|
||||
from urllib import request
|
||||
|
||||
__all__ = ["Registry"]
|
||||
|
||||
|
||||
class WebPnpIdParser(HTMLParser):
|
||||
"""Parser pnp id from https://uefi.org/PNP_ID_List
|
||||
|
||||
Examples:
|
||||
p = WebPnpIdParser()
|
||||
p.feed(html_data)
|
||||
p.result
|
||||
"""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self._find_table = False
|
||||
self._find_row = False
|
||||
# first -- company name, second -- pnp id, third -- approved date
|
||||
self._last_field = []
|
||||
# key -- pnp id, value -- tuple (company_name, approved_date)
|
||||
self.result = {}
|
||||
|
||||
def handle_starttag(self, tag, attrs):
|
||||
if tag == "tbody":
|
||||
self._find_table = True
|
||||
elif self._find_table and tag == "tr":
|
||||
self._find_row = True
|
||||
|
||||
def handle_endtag(self, tag):
|
||||
if tag == "tbody":
|
||||
self._find_table = False
|
||||
elif self._find_table and tag == "tr":
|
||||
self._find_row = False
|
||||
# add table row to result
|
||||
self.result[self._last_field[1]] = (
|
||||
self._last_field[0],
|
||||
self._last_field[-1],
|
||||
)
|
||||
self._last_field.clear()
|
||||
|
||||
def handle_data(self, data):
|
||||
# skip processing until table is found
|
||||
if not self._find_table:
|
||||
return
|
||||
|
||||
if self._find_row:
|
||||
data = data.strip()
|
||||
if data:
|
||||
self._last_field.append(data)
|
||||
|
||||
def error(self, message):
|
||||
super().close()
|
||||
|
||||
|
||||
class Registry(dict):
|
||||
"""Registry pnp id data dictionary
|
||||
|
||||
key -- pnp_id
|
||||
value -- company name
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def from_web(cls, filter_by_id: str = None):
|
||||
"""Get registry from https://uefi.org/PNP_ID_List
|
||||
|
||||
Args:
|
||||
filter_by_id (str), optional: filter registry by id
|
||||
|
||||
Raises:
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
url = "https://uefi.org/PNP_ID_List"
|
||||
if filter_by_id:
|
||||
url += "?search={}".format(filter_by_id)
|
||||
|
||||
with request.urlopen(url) as req:
|
||||
parse = WebPnpIdParser()
|
||||
parse.feed(req.read().decode())
|
||||
|
||||
registry = cls()
|
||||
for key, value in parse.result.items():
|
||||
# skip invalid search value
|
||||
if filter_by_id and key != filter_by_id:
|
||||
continue
|
||||
registry[key] = value[0]
|
||||
return registry
|
||||
|
||||
@classmethod
|
||||
def from_csv(cls, csv_path: str, filter_by_id: str = None):
|
||||
"""Get registry by csv local file
|
||||
|
||||
Args:
|
||||
csv_path (str): path to csv file
|
||||
filter_by_id (str), optional: filter registry by id
|
||||
|
||||
Raises:
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
registry = cls()
|
||||
with open(csv_path, "r") as file:
|
||||
reader = csv.reader(file)
|
||||
for line in reader:
|
||||
# filter
|
||||
if filter_by_id and filter_by_id != line[0]:
|
||||
continue
|
||||
registry[line[0]] = line[1]
|
||||
return registry
|
||||
|
||||
def to_csv(self, csv_path: str):
|
||||
"""Dump registry to csv file"""
|
||||
with open(csv_path, "w") as csv_file:
|
||||
writer = csv.writer(csv_file)
|
||||
writer.writerows(self.items())
|
||||
return self
|
||||
|
||||
def get_company_from_id(self, pnp_id: str) -> str:
|
||||
"""Convert PNP id to company name"""
|
||||
return self.get(pnp_id, "Unknown")
|
||||
|
||||
def get_company_from_raw(self, raw: int) -> str:
|
||||
"""Convert raw edid value to company name"""
|
||||
tmp = [(raw >> 10) & 31, (raw >> 5) & 31, raw & 31]
|
||||
pnp_id = "".join(string.ascii_uppercase[n - 1] for n in tmp)
|
||||
return self.get_company_from_id(pnp_id)
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user