mirror of
https://github.com/kellyjonbrazil/jc.git
synced 2026-04-03 17:44:07 +02:00
Compare commits
148 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 | ||
|
|
b0d6a7307c | ||
|
|
53ad793ff8 | ||
|
|
ea4332d8e4 | ||
|
|
5c5ff9324f | ||
|
|
2ab6352fdb | ||
|
|
56dcbaf40c | ||
|
|
029b5abcac | ||
|
|
32521ac91a | ||
|
|
bf88407902 | ||
|
|
63cf47db63 | ||
|
|
9970866b3b | ||
|
|
d61941b276 | ||
|
|
081fdb8026 | ||
|
|
f64dfbf79d | ||
|
|
5fb73f4ad5 | ||
|
|
1c16d32420 | ||
|
|
e367e0d714 | ||
|
|
4046649e32 | ||
|
|
77fcfe439c | ||
|
|
71d1355419 | ||
|
|
d1b270f336 | ||
|
|
9190a08332 | ||
|
|
3fae50e305 | ||
|
|
3582497ed4 | ||
|
|
19a67daabf | ||
|
|
02a7e5fd8a | ||
|
|
df72b16022 | ||
|
|
61bdc13810 | ||
|
|
1350a34316 | ||
|
|
7cfe68b96a | ||
|
|
7a93a61f54 | ||
|
|
362977e598 | ||
|
|
5cac897beb | ||
|
|
cad94cc6b3 | ||
|
|
fe4e429e85 | ||
|
|
d83b10e2d5 | ||
|
|
bd2a757fcd | ||
|
|
358b69a4cb | ||
|
|
08821a1454 | ||
|
|
90277c1d87 | ||
|
|
a51e702f77 | ||
|
|
466e37128b | ||
|
|
f1ea61388f | ||
|
|
72c11fda3a | ||
|
|
d6645983ef | ||
|
|
01e911ecdb | ||
|
|
12c0b3d889 | ||
|
|
c1075e40b7 | ||
|
|
fabe3f01b7 | ||
|
|
3a2ff61899 | ||
|
|
aeff94d272 | ||
|
|
c94f5d83fa | ||
|
|
b0756fa0f9 | ||
|
|
6c85abd57b | ||
|
|
7659ae94bd | ||
|
|
e306e81e43 | ||
|
|
6d5768b26b | ||
|
|
cee9f8bf32 | ||
|
|
b8e2678c01 | ||
|
|
b0fe96ed03 | ||
|
|
bb65ec380b | ||
|
|
ae6248227b |
29
CHANGELOG
29
CHANGELOG
@@ -1,5 +1,34 @@
|
||||
jc changelog
|
||||
|
||||
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
|
||||
- Add AIX support for the `arp` command parser
|
||||
- Add AIX support for the `mount` command parser
|
||||
- Fix `lsusb` command parser when extra hub port status information is output
|
||||
- Refactor `lsusb` command parser for more code reuse
|
||||
- Fix INI file parser to include top-level values with no section header
|
||||
- Fix INI file parser to not specially handle the [DEFAULT] section
|
||||
- Fix INI file and Key/Value parsers to only remove one quotation mark from the
|
||||
beginning and end of values.
|
||||
- Update copyright dates
|
||||
|
||||
20221230 v1.22.4
|
||||
- Add `iwconfig` command parser
|
||||
- Add NeXTSTEP format support to the PLIST file parser
|
||||
|
||||
16
EXAMPLES.md
16
EXAMPLES.md
@@ -1636,21 +1636,21 @@ cat example.ini | jc --ini -p
|
||||
```
|
||||
```json
|
||||
{
|
||||
"bitbucket.org": {
|
||||
"ServeraLiveInterval": "45",
|
||||
"DEFAULT": {
|
||||
"ServerAliveInterval": "45",
|
||||
"Compression": "yes",
|
||||
"CompressionLevel": "9",
|
||||
"ForwardX11": "yes",
|
||||
"ForwardX11": "yes"
|
||||
},
|
||||
"bitbucket.org": {
|
||||
"User": "hg"
|
||||
},
|
||||
"topsecret.server.com": {
|
||||
"ServeraLiveInterval": "45",
|
||||
"Compression": "yes",
|
||||
"CompressionLevel": "9",
|
||||
"ForwardX11": "no",
|
||||
"Port": "50022"
|
||||
"Port": "50022",
|
||||
"ForwardX11": "no"
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
### iostat
|
||||
```bash
|
||||
|
||||
372
README.md
372
README.md
@@ -3,7 +3,7 @@
|
||||
|
||||
> 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
|
||||
Ansible filter plugin in the `community.general` collection. See this
|
||||
@@ -44,8 +44,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 +133,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 +143,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,141 +154,147 @@ 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) |
|
||||
| `--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) |
|
||||
| `--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) |
|
||||
| `--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) |
|
||||
| ` --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) |
|
||||
| `--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) |
|
||||
| `--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) |
|
||||
| ` --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) |
|
||||
| `--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) |
|
||||
| `--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
|
||||
|
||||
@@ -309,6 +315,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`.
|
||||
@@ -756,37 +810,31 @@ ifconfig | jc -p --ifconfig # or: jc -p ifconfig
|
||||
cat example.ini
|
||||
```
|
||||
```
|
||||
[DEFAULT]
|
||||
ServerAliveInterval = 45
|
||||
Compression = yes
|
||||
CompressionLevel = 9
|
||||
ForwardX11 = yes
|
||||
foo = fiz
|
||||
bar = buz
|
||||
|
||||
[bitbucket.org]
|
||||
User = hg
|
||||
[section1]
|
||||
fruit = apple
|
||||
color = blue
|
||||
|
||||
[topsecret.server.com]
|
||||
Port = 50022
|
||||
ForwardX11 = no
|
||||
[section2]
|
||||
fruit = pear
|
||||
color = green
|
||||
```
|
||||
```bash
|
||||
cat example.ini | jc -p --ini
|
||||
```
|
||||
```json
|
||||
{
|
||||
"bitbucket.org": {
|
||||
"ServeraLiveInterval": "45",
|
||||
"Compression": "yes",
|
||||
"CompressionLevel": "9",
|
||||
"ForwardX11": "yes",
|
||||
"User": "hg"
|
||||
"foo": "fiz",
|
||||
"bar": "buz",
|
||||
"section1": {
|
||||
"fruit": "apple",
|
||||
"color": "blue"
|
||||
},
|
||||
"topsecret.server.com": {
|
||||
"ServeraLiveInterval": "45",
|
||||
"Compression": "yes",
|
||||
"CompressionLevel": "9",
|
||||
"ForwardX11": "no",
|
||||
"Port": "50022"
|
||||
"section2": {
|
||||
"fruit": "pear",
|
||||
"color": "green"
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -1242,4 +1290,4 @@ cat istio.yaml | jc -p --yaml
|
||||
]
|
||||
```
|
||||
|
||||
© 2019-2022 Kelly Brazil
|
||||
© 2019-2023 Kelly Brazil
|
||||
@@ -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 --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 --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 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 ssh 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 zpool)
|
||||
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 --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 --vmstat --vmstat-s --w --wc --who --x509-cert --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,7 +9,7 @@ _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 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 ssh 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 zpool)
|
||||
jc_commands_describe=(
|
||||
'acpi:run "acpi" command with magic syntax.'
|
||||
'airport:run "airport" command with magic syntax.'
|
||||
@@ -76,6 +76,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.'
|
||||
@@ -101,8 +102,9 @@ _jc() {
|
||||
'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 --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 --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 --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 --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 --vmstat --vmstat-s --w --wc --who --x509-cert --xml --xrandr --yaml --zipinfo --zpool-iostat --zpool-status)
|
||||
jc_parsers_describe=(
|
||||
'--acpi:`acpi` command parser'
|
||||
'--airport:`airport -I` command parser'
|
||||
@@ -151,6 +153,7 @@ _jc() {
|
||||
'--id:`id` command parser'
|
||||
'--ifconfig:`ifconfig` command parser'
|
||||
'--ini:INI file parser'
|
||||
'--ini-dup:INI with duplicate key file parser'
|
||||
'--iostat:`iostat` command parser'
|
||||
'--iostat-s:`iostat` command streaming parser'
|
||||
'--ip-address:IPv4 and IPv6 Address string parser'
|
||||
@@ -249,7 +252,8 @@ _jc() {
|
||||
'--sfdisk:`sfdisk` command parser'
|
||||
'--shadow:`/etc/shadow` 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'
|
||||
@@ -265,6 +269,7 @@ _jc() {
|
||||
'--time:`/usr/bin/time` command parser'
|
||||
'--timedatectl:`timedatectl status` command parser'
|
||||
'--timestamp:Unix Epoch Timestamp string parser'
|
||||
'--toml:TOML file parser'
|
||||
'--top:`top -b` command parser'
|
||||
'--top-s:`top -b` command streaming parser'
|
||||
'--tracepath:`tracepath` and `tracepath6` command parser'
|
||||
@@ -278,6 +283,7 @@ _jc() {
|
||||
'--upower:`upower` command parser'
|
||||
'--uptime:`uptime` command parser'
|
||||
'--url:URL string parser'
|
||||
'--ver:Version string parser'
|
||||
'--vmstat:`vmstat` command parser'
|
||||
'--vmstat-s:`vmstat` command streaming parser'
|
||||
'--w:`w` command parser'
|
||||
@@ -288,6 +294,8 @@ _jc() {
|
||||
'--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.5 by Kelly Brazil (kellyjonbrazil@gmail.com)
|
||||
|
||||
@@ -140,4 +140,4 @@ Returns:
|
||||
### Parser Information
|
||||
Compatibility: linux, aix, freebsd, darwin
|
||||
|
||||
Version 1.11 by Kelly Brazil (kellyjonbrazil@gmail.com)
|
||||
Version 1.12 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.7 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.8 by Kelly Brazil (kellyjonbrazil@gmail.com)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -3,13 +3,15 @@
|
||||
|
||||
# jc.parsers.ini
|
||||
|
||||
jc - JSON Convert `INI` file parser
|
||||
jc - JSON Convert INI file parser
|
||||
|
||||
Parses standard `INI` files and files containing simple key/value pairs.
|
||||
Parses standard INI files.
|
||||
|
||||
- Delimiter can be `=` or `:`. Missing values are supported.
|
||||
- Comment prefix can be `#` or `;`. Comments must be on their own line.
|
||||
- If duplicate keys are found, only the last value will be used.
|
||||
- If any section names have the same name as a top-level key, the top-level
|
||||
key will be overwritten by the section data.
|
||||
|
||||
> Note: Values starting and ending with double or single quotation marks
|
||||
> will have the marks removed. If you would like to keep the quotation
|
||||
@@ -27,45 +29,47 @@ Usage (module):
|
||||
|
||||
Schema:
|
||||
|
||||
ini or key/value document converted to a dictionary - see the configparser
|
||||
INI document converted to a dictionary - see the python configparser
|
||||
standard library documentation for more details.
|
||||
|
||||
{
|
||||
"key1": string,
|
||||
"key2": string
|
||||
"<key1>": string,
|
||||
"<key2>": string,
|
||||
"<section1>": {
|
||||
"<key1>": string,
|
||||
"<key2>": string
|
||||
},
|
||||
"<section2>": {
|
||||
"<key1>": string,
|
||||
"<key2>": string
|
||||
}
|
||||
}
|
||||
|
||||
Examples:
|
||||
|
||||
$ cat example.ini
|
||||
[DEFAULT]
|
||||
ServerAliveInterval = 45
|
||||
Compression = yes
|
||||
CompressionLevel = 9
|
||||
ForwardX11 = yes
|
||||
foo = fiz
|
||||
bar = buz
|
||||
|
||||
[bitbucket.org]
|
||||
User = hg
|
||||
[section1]
|
||||
fruit = apple
|
||||
color = blue
|
||||
|
||||
[topsecret.server.com]
|
||||
Port = 50022
|
||||
ForwardX11 = no
|
||||
[section2]
|
||||
fruit = pear
|
||||
color = green
|
||||
|
||||
$ cat example.ini | jc --ini -p
|
||||
{
|
||||
"bitbucket.org": {
|
||||
"ServerAliveInterval": "45",
|
||||
"Compression": "yes",
|
||||
"CompressionLevel": "9",
|
||||
"ForwardX11": "yes",
|
||||
"User": "hg"
|
||||
"foo": "fiz",
|
||||
"bar": "buz",
|
||||
"section1": {
|
||||
"fruit": "apple",
|
||||
"color": "blue"
|
||||
},
|
||||
"topsecret.server.com": {
|
||||
"ServerAliveInterval": "45",
|
||||
"Compression": "yes",
|
||||
"CompressionLevel": "9",
|
||||
"ForwardX11": "no",
|
||||
"Port": "50022"
|
||||
"section2": {
|
||||
"fruit": "pear",
|
||||
"color": "green"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,9 +91,9 @@ Parameters:
|
||||
|
||||
Returns:
|
||||
|
||||
Dictionary representing the ini file
|
||||
Dictionary representing the INI file.
|
||||
|
||||
### Parser Information
|
||||
Compatibility: linux, darwin, cygwin, win32, aix, freebsd
|
||||
|
||||
Version 1.8 by Kelly Brazil (kellyjonbrazil@gmail.com)
|
||||
Version 2.0 by Kelly Brazil (kellyjonbrazil@gmail.com)
|
||||
|
||||
121
docs/parsers/ini_dup.md
Normal file
121
docs/parsers/ini_dup.md
Normal file
@@ -0,0 +1,121 @@
|
||||
[Home](https://kellyjonbrazil.github.io/jc/)
|
||||
<a id="jc.parsers.ini_dup"></a>
|
||||
|
||||
# jc.parsers.ini\_dup
|
||||
|
||||
jc - JSON Convert INI with duplicate key file parser
|
||||
|
||||
Parses standard INI files and preserves duplicate values. All values are
|
||||
contained in lists/arrays.
|
||||
|
||||
- Delimiter can be `=` or `:`. Missing values are supported.
|
||||
- Comment prefix can be `#` or `;`. Comments must be on their own line.
|
||||
- If any section names have the same name as a top-level key, the top-level
|
||||
key will be overwritten by the section data.
|
||||
- If multi-line values are used, each line will be a separate item in the
|
||||
value list. Blank lines in multi-line values are not supported.
|
||||
|
||||
> Note: Values starting and ending with double or single quotation marks
|
||||
> will have the marks removed. If you would like to keep the quotation
|
||||
> marks, use the `-r` command-line argument or the `raw=True` argument in
|
||||
> `parse()`.
|
||||
|
||||
Usage (cli):
|
||||
|
||||
$ cat foo.ini | jc --ini
|
||||
|
||||
Usage (module):
|
||||
|
||||
import jc
|
||||
result = jc.parse('ini', ini_file_output)
|
||||
|
||||
Schema:
|
||||
|
||||
INI document converted to a dictionary - see the python configparser
|
||||
standard library documentation for more details.
|
||||
|
||||
{
|
||||
"<key1>": [
|
||||
string
|
||||
],
|
||||
"<key2>": [
|
||||
string
|
||||
],
|
||||
"<section1>": {
|
||||
"<key1>": [
|
||||
string
|
||||
],
|
||||
"<key2>": [
|
||||
string
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
Examples:
|
||||
|
||||
$ cat example.ini
|
||||
foo = fiz
|
||||
bar = buz
|
||||
|
||||
[section1]
|
||||
fruit = apple
|
||||
color = blue
|
||||
color = red
|
||||
|
||||
[section2]
|
||||
fruit = pear
|
||||
fruit = peach
|
||||
color = green
|
||||
|
||||
$ cat example.ini | jc --ini -p
|
||||
{
|
||||
"foo": [
|
||||
"fiz"
|
||||
],
|
||||
"bar": [
|
||||
"buz"
|
||||
],
|
||||
"section1": {
|
||||
"fruit": [
|
||||
"apple"
|
||||
],
|
||||
"color": [
|
||||
"blue",
|
||||
"red"
|
||||
]
|
||||
},
|
||||
"section2": {
|
||||
"fruit": [
|
||||
"pear",
|
||||
"peach"
|
||||
],
|
||||
"color": [
|
||||
"green"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
<a id="jc.parsers.ini_dup.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:
|
||||
|
||||
Dictionary representing the INI file.
|
||||
|
||||
### Parser Information
|
||||
Compatibility: linux, darwin, cygwin, win32, aix, freebsd
|
||||
|
||||
Version 1.0 by Kelly Brazil (kellyjonbrazil@gmail.com)
|
||||
@@ -51,7 +51,6 @@ Schema:
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
Examples:
|
||||
|
||||
$ iwconfig | jc --iwconfig -p
|
||||
|
||||
@@ -26,8 +26,8 @@ Usage (module):
|
||||
|
||||
Schema:
|
||||
|
||||
key/value document converted to a dictionary - see the configparser standard
|
||||
library documentation for more details.
|
||||
Key/Value document converted to a dictionary - see the python configparser
|
||||
standard library documentation for more details.
|
||||
|
||||
{
|
||||
"key1": string,
|
||||
@@ -41,6 +41,7 @@ Examples:
|
||||
name = John Doe
|
||||
address=555 California Drive
|
||||
age: 34
|
||||
|
||||
; comments can include # or ;
|
||||
# delimiter can be = or :
|
||||
# quoted values have quotation marks stripped by default
|
||||
@@ -65,8 +66,6 @@ def parse(data, raw=False, quiet=False)
|
||||
|
||||
Main text parsing function
|
||||
|
||||
Note: this is just a wrapper for jc.parsers.ini
|
||||
|
||||
Parameters:
|
||||
|
||||
data: (string) text data to parse
|
||||
@@ -75,9 +74,9 @@ Parameters:
|
||||
|
||||
Returns:
|
||||
|
||||
Dictionary representing the key/value file
|
||||
Dictionary representing a Key/Value pair document.
|
||||
|
||||
### Parser Information
|
||||
Compatibility: linux, darwin, cygwin, win32, aix, freebsd
|
||||
|
||||
Version 1.2 by Kelly Brazil (kellyjonbrazil@gmail.com)
|
||||
Version 2.0 by Kelly Brazil (kellyjonbrazil@gmail.com)
|
||||
|
||||
@@ -102,6 +102,28 @@ Schema:
|
||||
]
|
||||
}
|
||||
},
|
||||
"videocontrol_descriptors": [
|
||||
{
|
||||
"<item>": {
|
||||
"value": string,
|
||||
"description": string,
|
||||
"attributes": [
|
||||
string
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"videostreaming_descriptors": [
|
||||
{
|
||||
"<item>": {
|
||||
"value": string,
|
||||
"description": string,
|
||||
"attributes": [
|
||||
string
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"endpoint_descriptors": [
|
||||
{
|
||||
"<item>": {
|
||||
@@ -290,4 +312,4 @@ Returns:
|
||||
### Parser Information
|
||||
Compatibility: linux
|
||||
|
||||
Version 1.2 by Kelly Brazil (kellyjonbrazil@gmail.com)
|
||||
Version 1.3 by Kelly Brazil (kellyjonbrazil@gmail.com)
|
||||
|
||||
@@ -25,7 +25,7 @@ Schema:
|
||||
"filesystem": string,
|
||||
"mount_point": string,
|
||||
"type": string,
|
||||
"access": [
|
||||
"options": [
|
||||
string
|
||||
]
|
||||
}
|
||||
@@ -39,7 +39,7 @@ Example:
|
||||
"filesystem": "sysfs",
|
||||
"mount_point": "/sys",
|
||||
"type": "sysfs",
|
||||
"access": [
|
||||
"options": [
|
||||
"rw",
|
||||
"nosuid",
|
||||
"nodev",
|
||||
@@ -51,7 +51,7 @@ Example:
|
||||
"filesystem": "proc",
|
||||
"mount_point": "/proc",
|
||||
"type": "proc",
|
||||
"access": [
|
||||
"options": [
|
||||
"rw",
|
||||
"nosuid",
|
||||
"nodev",
|
||||
@@ -63,7 +63,7 @@ Example:
|
||||
"filesystem": "udev",
|
||||
"mount_point": "/dev",
|
||||
"type": "devtmpfs",
|
||||
"access": [
|
||||
"options": [
|
||||
"rw",
|
||||
"nosuid",
|
||||
"relatime",
|
||||
@@ -96,6 +96,6 @@ Returns:
|
||||
List of Dictionaries. Raw or processed structured data.
|
||||
|
||||
### Parser Information
|
||||
Compatibility: linux, darwin, freebsd
|
||||
Compatibility: linux, darwin, freebsd, aix
|
||||
|
||||
Version 1.7 by Kelly Brazil (kellyjonbrazil@gmail.com)
|
||||
Version 1.8 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)
|
||||
|
||||
81
docs/parsers/toml.md
Normal file
81
docs/parsers/toml.md
Normal file
@@ -0,0 +1,81 @@
|
||||
[Home](https://kellyjonbrazil.github.io/jc/)
|
||||
<a id="jc.parsers.toml"></a>
|
||||
|
||||
# jc.parsers.toml
|
||||
|
||||
jc - JSON Convert TOML file parser
|
||||
|
||||
Usage (cli):
|
||||
|
||||
$ cat file.toml | jc --toml
|
||||
|
||||
Usage (module):
|
||||
|
||||
import jc
|
||||
result = jc.parse('toml', toml_file_output)
|
||||
|
||||
Schema:
|
||||
|
||||
TOML Document converted to a Dictionary.
|
||||
See https://toml.io/en/ for details.
|
||||
|
||||
{
|
||||
"key1": string/int/float/boolean/null/array/object,
|
||||
"key2": string/int/float/boolean/null/array/object
|
||||
}
|
||||
|
||||
Examples:
|
||||
|
||||
$ cat file.toml
|
||||
title = "TOML Example"
|
||||
|
||||
[owner]
|
||||
name = "Tom Preston-Werner"
|
||||
dob = 1979-05-27T07:32:00-08:00
|
||||
|
||||
[database]
|
||||
enabled = true
|
||||
ports = [ 8000, 8001, 8002 ]
|
||||
|
||||
$ cat file.toml | jc --toml -p
|
||||
{
|
||||
"title": "TOML Example",
|
||||
"owner": {
|
||||
"name": "Tom Preston-Werner",
|
||||
"dob": 296667120,
|
||||
"dob_iso": "1979-05-27T07:32:00-08:00"
|
||||
},
|
||||
"database": {
|
||||
"enabled": true,
|
||||
"ports": [
|
||||
8000,
|
||||
8001,
|
||||
8002
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
<a id="jc.parsers.toml.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)
|
||||
@@ -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:
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ jc - JSON Convert `xrandr` command output parser
|
||||
Usage (cli):
|
||||
|
||||
$ xrandr | jc --xrandr
|
||||
$ xrandr --properties | jc --xrandr
|
||||
|
||||
or
|
||||
|
||||
@@ -49,13 +50,17 @@ 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": [
|
||||
@@ -132,7 +137,71 @@ 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,
|
||||
"associated_device": {
|
||||
"associated_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"
|
||||
}
|
||||
}
|
||||
],
|
||||
@@ -162,4 +231,4 @@ Returns:
|
||||
### Parser Information
|
||||
Compatibility: linux, darwin, cygwin, aix, freebsd
|
||||
|
||||
Version 1.1 by Kevin Lyter (lyter_git at sent.com)
|
||||
Version 1.2 by Kevin Lyter (lyter_git at sent.com)
|
||||
|
||||
@@ -18,8 +18,8 @@ Usage (module):
|
||||
|
||||
Schema:
|
||||
|
||||
YAML Document converted to a Dictionary
|
||||
See https://pypi.org/project/ruamel.yaml for details
|
||||
YAML Document converted to a Dictionary.
|
||||
See https://pypi.org/project/ruamel.yaml for details.
|
||||
|
||||
[
|
||||
{
|
||||
@@ -30,7 +30,7 @@ Schema:
|
||||
|
||||
Examples:
|
||||
|
||||
$ cat istio-mtls-permissive.yaml
|
||||
$ cat file.yaml
|
||||
apiVersion: "authentication.istio.io/v1alpha1"
|
||||
kind: "Policy"
|
||||
metadata:
|
||||
@@ -51,7 +51,7 @@ Examples:
|
||||
tls:
|
||||
mode: ISTIO_MUTUAL
|
||||
|
||||
$ cat istio-mtls-permissive.yaml | jc --yaml -p
|
||||
$ cat file.yaml | jc --yaml -p
|
||||
[
|
||||
{
|
||||
"apiVersion": "authentication.istio.io/v1alpha1",
|
||||
|
||||
@@ -99,4 +99,4 @@ Returns:
|
||||
### Parser Information
|
||||
Compatibility: linux, darwin
|
||||
|
||||
Version 1.1 by Matt J (https://github.com/listuser)
|
||||
Version 1.2 by Matt J (https://github.com/listuser)
|
||||
|
||||
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.0 by Kelly Brazil (kellyjonbrazil@gmail.com)
|
||||
200
jc/cli.py
200
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():
|
||||
@@ -48,7 +54,7 @@ class info():
|
||||
author: str = 'Kelly Brazil'
|
||||
author_email: str = 'kellyjonbrazil@gmail.com'
|
||||
website: str = 'https://github.com/kellyjonbrazil/jc'
|
||||
copyright: str = '© 2019-2022 Kelly Brazil'
|
||||
copyright: str = '© 2019-2023 Kelly Brazil'
|
||||
license: str = 'MIT License'
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -432,6 +443,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 +596,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 +610,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 +640,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 +765,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
|
||||
|
||||
|
||||
14
jc/lib.py
14
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.4'
|
||||
__version__ = '1.23.0'
|
||||
|
||||
parsers: List[str] = [
|
||||
'acpi',
|
||||
@@ -59,6 +59,7 @@ parsers: List[str] = [
|
||||
'id',
|
||||
'ifconfig',
|
||||
'ini',
|
||||
'ini-dup',
|
||||
'iostat',
|
||||
'iostat-s',
|
||||
'ip-address',
|
||||
@@ -158,6 +159,7 @@ parsers: List[str] = [
|
||||
'sfdisk',
|
||||
'shadow',
|
||||
'ss',
|
||||
'ssh-conf',
|
||||
'sshd-conf',
|
||||
'stat',
|
||||
'stat-s',
|
||||
@@ -174,6 +176,7 @@ parsers: List[str] = [
|
||||
'time',
|
||||
'timedatectl',
|
||||
'timestamp',
|
||||
'toml',
|
||||
'top',
|
||||
'top-s',
|
||||
'tracepath',
|
||||
@@ -187,6 +190,7 @@ parsers: List[str] = [
|
||||
'upower',
|
||||
'uptime',
|
||||
'url',
|
||||
'ver',
|
||||
'vmstat',
|
||||
'vmstat-s',
|
||||
'w',
|
||||
@@ -196,7 +200,9 @@ parsers: List[str] = [
|
||||
'xml',
|
||||
'xrandr',
|
||||
'yaml',
|
||||
'zipinfo'
|
||||
'zipinfo',
|
||||
'zpool-iostat',
|
||||
'zpool-status'
|
||||
]
|
||||
|
||||
def _cliname_to_modname(parser_cli_name: str) -> str:
|
||||
@@ -277,7 +283,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.5'
|
||||
description = '`acpi` command parser'
|
||||
author = 'Kelly Brazil'
|
||||
author_email = 'kellyjonbrazil@gmail.com'
|
||||
@@ -336,7 +336,9 @@ def parse(data, raw=False, quiet=False):
|
||||
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:
|
||||
|
||||
@@ -119,7 +119,7 @@ import jc.parsers.universal
|
||||
|
||||
class info():
|
||||
"""Provides parser metadata (version, author, etc.)"""
|
||||
version = '1.11'
|
||||
version = '1.12'
|
||||
description = '`arp` command parser'
|
||||
author = 'Kelly Brazil'
|
||||
author_email = 'kellyjonbrazil@gmail.com'
|
||||
@@ -222,14 +222,26 @@ def parse(
|
||||
else:
|
||||
for line in cleandata:
|
||||
splitline = line.split()
|
||||
if '<incomplete>' not in splitline:
|
||||
|
||||
# Ignore AIX bucket information
|
||||
if 'bucket:' in splitline[0]:
|
||||
continue
|
||||
elif 'There' in splitline[0] and 'are' in splitline[1]:
|
||||
continue
|
||||
|
||||
# AIX uses (incomplete)
|
||||
elif '<incomplete>' not in splitline and '(incomplete)' not in splitline:
|
||||
output_line = {
|
||||
'name': splitline[0],
|
||||
'address': splitline[1].lstrip('(').rstrip(')'),
|
||||
'hwtype': splitline[4].lstrip('[').rstrip(']'),
|
||||
'hwaddress': splitline[3],
|
||||
'iface': splitline[6],
|
||||
}
|
||||
# Handle permanence and ignore interface in AIX
|
||||
if 'permanent' in splitline:
|
||||
output_line['permanent'] = True
|
||||
elif 'in' not in splitline[6]: # AIX doesn't show interface
|
||||
output_line['iface'] = splitline[6]
|
||||
|
||||
else:
|
||||
output_line = {
|
||||
@@ -237,8 +249,10 @@ def parse(
|
||||
'address': splitline[1].lstrip('(').rstrip(')'),
|
||||
'hwtype': None,
|
||||
'hwaddress': None,
|
||||
'iface': splitline[5],
|
||||
}
|
||||
# AIX doesn't show interface
|
||||
if len(splitline) >= 5:
|
||||
output_line['iface'] = splitline[5]
|
||||
|
||||
raw_output.append(output_line)
|
||||
|
||||
|
||||
@@ -174,7 +174,7 @@ import jc.parsers.universal
|
||||
|
||||
class info():
|
||||
"""Provides parser metadata (version, author, etc.)"""
|
||||
version = '1.6'
|
||||
version = '1.7'
|
||||
description = '`crontab` command and file parser'
|
||||
author = 'Kelly Brazil'
|
||||
author_email = 'kellyjonbrazil@gmail.com'
|
||||
@@ -273,6 +273,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.8'
|
||||
description = '`crontab` file parser with user support'
|
||||
author = 'Kelly Brazil'
|
||||
author_email = 'kellyjonbrazil@gmail.com'
|
||||
@@ -271,6 +271,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)
|
||||
|
||||
|
||||
@@ -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'''
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
"""jc - JSON Convert `INI` file parser
|
||||
"""jc - JSON Convert INI file parser
|
||||
|
||||
Parses standard `INI` files and files containing simple key/value pairs.
|
||||
Parses standard INI files.
|
||||
|
||||
- Delimiter can be `=` or `:`. Missing values are supported.
|
||||
- Comment prefix can be `#` or `;`. Comments must be on their own line.
|
||||
- If duplicate keys are found, only the last value will be used.
|
||||
- If any section names have the same name as a top-level key, the top-level
|
||||
key will be overwritten by the section data.
|
||||
|
||||
> Note: Values starting and ending with double or single quotation marks
|
||||
> will have the marks removed. If you would like to keep the quotation
|
||||
@@ -22,59 +24,62 @@ Usage (module):
|
||||
|
||||
Schema:
|
||||
|
||||
ini or key/value document converted to a dictionary - see the configparser
|
||||
INI document converted to a dictionary - see the python configparser
|
||||
standard library documentation for more details.
|
||||
|
||||
{
|
||||
"key1": string,
|
||||
"key2": string
|
||||
"<key1>": string,
|
||||
"<key2>": string,
|
||||
"<section1>": {
|
||||
"<key1>": string,
|
||||
"<key2>": string
|
||||
},
|
||||
"<section2>": {
|
||||
"<key1>": string,
|
||||
"<key2>": string
|
||||
}
|
||||
}
|
||||
|
||||
Examples:
|
||||
|
||||
$ cat example.ini
|
||||
[DEFAULT]
|
||||
ServerAliveInterval = 45
|
||||
Compression = yes
|
||||
CompressionLevel = 9
|
||||
ForwardX11 = yes
|
||||
foo = fiz
|
||||
bar = buz
|
||||
|
||||
[bitbucket.org]
|
||||
User = hg
|
||||
[section1]
|
||||
fruit = apple
|
||||
color = blue
|
||||
|
||||
[topsecret.server.com]
|
||||
Port = 50022
|
||||
ForwardX11 = no
|
||||
[section2]
|
||||
fruit = pear
|
||||
color = green
|
||||
|
||||
$ cat example.ini | jc --ini -p
|
||||
{
|
||||
"bitbucket.org": {
|
||||
"ServerAliveInterval": "45",
|
||||
"Compression": "yes",
|
||||
"CompressionLevel": "9",
|
||||
"ForwardX11": "yes",
|
||||
"User": "hg"
|
||||
"foo": "fiz",
|
||||
"bar": "buz",
|
||||
"section1": {
|
||||
"fruit": "apple",
|
||||
"color": "blue"
|
||||
},
|
||||
"topsecret.server.com": {
|
||||
"ServerAliveInterval": "45",
|
||||
"Compression": "yes",
|
||||
"CompressionLevel": "9",
|
||||
"ForwardX11": "no",
|
||||
"Port": "50022"
|
||||
"section2": {
|
||||
"fruit": "pear",
|
||||
"color": "green"
|
||||
}
|
||||
}
|
||||
"""
|
||||
import jc.utils
|
||||
import configparser
|
||||
import uuid
|
||||
|
||||
|
||||
class info():
|
||||
"""Provides parser metadata (version, author, etc.)"""
|
||||
version = '1.8'
|
||||
version = '2.0'
|
||||
description = 'INI file parser'
|
||||
author = 'Kelly Brazil'
|
||||
author_email = 'kellyjonbrazil@gmail.com'
|
||||
details = 'Using configparser from the standard library'
|
||||
details = 'Using configparser from the python standard library'
|
||||
compatible = ['linux', 'darwin', 'cygwin', 'win32', 'aix', 'freebsd']
|
||||
tags = ['standard', 'file', 'string']
|
||||
|
||||
@@ -82,6 +87,19 @@ class info():
|
||||
__version__ = info.version
|
||||
|
||||
|
||||
def _remove_quotes(value):
|
||||
if value is None:
|
||||
value = ''
|
||||
|
||||
elif value.startswith('"') and value.endswith('"'):
|
||||
value = value[1:-1]
|
||||
|
||||
elif value.startswith("'") and value.endswith("'"):
|
||||
value = value[1:-1]
|
||||
|
||||
return value
|
||||
|
||||
|
||||
def _process(proc_data):
|
||||
"""
|
||||
Final processing to conform to the schema.
|
||||
@@ -92,32 +110,16 @@ def _process(proc_data):
|
||||
|
||||
Returns:
|
||||
|
||||
Dictionary representing an ini or simple key/value pair document.
|
||||
Dictionary representing the INI file.
|
||||
"""
|
||||
# remove quotation marks from beginning and end of values
|
||||
for heading in proc_data:
|
||||
# standard ini files with headers
|
||||
if isinstance(proc_data[heading], dict):
|
||||
for key, value in proc_data[heading].items():
|
||||
if value is not None and value.startswith('"') and value.endswith('"'):
|
||||
proc_data[heading][key] = value.lstrip('"').rstrip('"')
|
||||
for k, v in proc_data.items():
|
||||
if isinstance(v, dict):
|
||||
for key, value in v.items():
|
||||
v[key] = _remove_quotes(value)
|
||||
continue
|
||||
|
||||
elif value is not None and value.startswith("'") and value.endswith("'"):
|
||||
proc_data[heading][key] = value.lstrip("'").rstrip("'")
|
||||
|
||||
elif value is None:
|
||||
proc_data[heading][key] = ''
|
||||
|
||||
# simple key/value files with no headers
|
||||
else:
|
||||
if proc_data[heading] is not None and proc_data[heading].startswith('"') and proc_data[heading].endswith('"'):
|
||||
proc_data[heading] = proc_data[heading].lstrip('"').rstrip('"')
|
||||
|
||||
elif proc_data[heading] is not None and proc_data[heading].startswith("'") and proc_data[heading].endswith("'"):
|
||||
proc_data[heading] = proc_data[heading].lstrip("'").rstrip("'")
|
||||
|
||||
elif proc_data[heading] is None:
|
||||
proc_data[heading] = ''
|
||||
proc_data[k] = _remove_quotes(v)
|
||||
|
||||
return proc_data
|
||||
|
||||
@@ -134,7 +136,7 @@ def parse(data, raw=False, quiet=False):
|
||||
|
||||
Returns:
|
||||
|
||||
Dictionary representing the ini file
|
||||
Dictionary representing the INI file.
|
||||
"""
|
||||
jc.utils.compatibility(__name__, info.compatible, quiet)
|
||||
jc.utils.input_type_check(data)
|
||||
@@ -143,25 +145,36 @@ def parse(data, raw=False, quiet=False):
|
||||
|
||||
if jc.utils.has_data(data):
|
||||
|
||||
ini = configparser.ConfigParser(allow_no_value=True,
|
||||
interpolation=None,
|
||||
strict=False)
|
||||
ini_parser = configparser.ConfigParser(
|
||||
allow_no_value=True,
|
||||
interpolation=None,
|
||||
default_section=None,
|
||||
strict=False
|
||||
)
|
||||
|
||||
# don't convert keys to lower-case:
|
||||
ini.optionxform = lambda option: option
|
||||
ini_parser.optionxform = lambda option: option
|
||||
|
||||
try:
|
||||
ini.read_string(data)
|
||||
raw_output = {s: dict(ini.items(s)) for s in ini.sections()}
|
||||
ini_parser.read_string(data)
|
||||
raw_output = {s: dict(ini_parser.items(s)) for s in ini_parser.sections()}
|
||||
|
||||
except configparser.MissingSectionHeaderError:
|
||||
data = '[data]\n' + data
|
||||
ini.read_string(data)
|
||||
output_dict = {s: dict(ini.items(s)) for s in ini.sections()}
|
||||
for key, value in output_dict['data'].items():
|
||||
raw_output[key] = value
|
||||
# find a top-level section name that will not collide with any existing ones
|
||||
while True:
|
||||
my_uuid = str(uuid.uuid4())
|
||||
if my_uuid not in data:
|
||||
break
|
||||
|
||||
data = f'[{my_uuid}]\n' + data
|
||||
ini_parser.read_string(data)
|
||||
temp_dict = {s: dict(ini_parser.items(s)) for s in ini_parser.sections()}
|
||||
|
||||
# move items under fake top-level sections to the root
|
||||
raw_output = temp_dict.pop(my_uuid)
|
||||
|
||||
# get the rest of the sections
|
||||
raw_output.update(temp_dict)
|
||||
|
||||
return raw_output if raw else _process(raw_output)
|
||||
|
||||
if raw:
|
||||
return raw_output
|
||||
else:
|
||||
return _process(raw_output)
|
||||
|
||||
225
jc/parsers/ini_dup.py
Normal file
225
jc/parsers/ini_dup.py
Normal file
@@ -0,0 +1,225 @@
|
||||
"""jc - JSON Convert INI with duplicate key file parser
|
||||
|
||||
Parses standard INI files and preserves duplicate values. All values are
|
||||
contained in lists/arrays.
|
||||
|
||||
- Delimiter can be `=` or `:`. Missing values are supported.
|
||||
- Comment prefix can be `#` or `;`. Comments must be on their own line.
|
||||
- If any section names have the same name as a top-level key, the top-level
|
||||
key will be overwritten by the section data.
|
||||
- If multi-line values are used, each line will be a separate item in the
|
||||
value list. Blank lines in multi-line values are not supported.
|
||||
|
||||
> Note: Values starting and ending with double or single quotation marks
|
||||
> will have the marks removed. If you would like to keep the quotation
|
||||
> marks, use the `-r` command-line argument or the `raw=True` argument in
|
||||
> `parse()`.
|
||||
|
||||
Usage (cli):
|
||||
|
||||
$ cat foo.ini | jc --ini
|
||||
|
||||
Usage (module):
|
||||
|
||||
import jc
|
||||
result = jc.parse('ini', ini_file_output)
|
||||
|
||||
Schema:
|
||||
|
||||
INI document converted to a dictionary - see the python configparser
|
||||
standard library documentation for more details.
|
||||
|
||||
{
|
||||
"<key1>": [
|
||||
string
|
||||
],
|
||||
"<key2>": [
|
||||
string
|
||||
],
|
||||
"<section1>": {
|
||||
"<key1>": [
|
||||
string
|
||||
],
|
||||
"<key2>": [
|
||||
string
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
Examples:
|
||||
|
||||
$ cat example.ini
|
||||
foo = fiz
|
||||
bar = buz
|
||||
|
||||
[section1]
|
||||
fruit = apple
|
||||
color = blue
|
||||
color = red
|
||||
|
||||
[section2]
|
||||
fruit = pear
|
||||
fruit = peach
|
||||
color = green
|
||||
|
||||
$ cat example.ini | jc --ini -p
|
||||
{
|
||||
"foo": [
|
||||
"fiz"
|
||||
],
|
||||
"bar": [
|
||||
"buz"
|
||||
],
|
||||
"section1": {
|
||||
"fruit": [
|
||||
"apple"
|
||||
],
|
||||
"color": [
|
||||
"blue",
|
||||
"red"
|
||||
]
|
||||
},
|
||||
"section2": {
|
||||
"fruit": [
|
||||
"pear",
|
||||
"peach"
|
||||
],
|
||||
"color": [
|
||||
"green"
|
||||
]
|
||||
}
|
||||
}
|
||||
"""
|
||||
import jc.utils
|
||||
import configparser
|
||||
import uuid
|
||||
|
||||
|
||||
class info():
|
||||
"""Provides parser metadata (version, author, etc.)"""
|
||||
version = '1.0'
|
||||
description = 'INI with duplicate key file parser'
|
||||
author = 'Kelly Brazil'
|
||||
author_email = 'kellyjonbrazil@gmail.com'
|
||||
details = 'Using configparser from the python standard library'
|
||||
compatible = ['linux', 'darwin', 'cygwin', 'win32', 'aix', 'freebsd']
|
||||
tags = ['standard', 'file', 'string']
|
||||
|
||||
|
||||
__version__ = info.version
|
||||
|
||||
|
||||
class MultiDict(dict):
|
||||
# https://stackoverflow.com/a/38286559/12303989
|
||||
def __setitem__(self, key, value):
|
||||
if key in self:
|
||||
if isinstance(value, list):
|
||||
self[key].extend(value)
|
||||
|
||||
elif isinstance(value, str):
|
||||
if len(self[key])>1:
|
||||
return
|
||||
|
||||
else:
|
||||
super().__setitem__(key, value)
|
||||
|
||||
|
||||
def _remove_quotes(value):
|
||||
if value is None:
|
||||
value = ''
|
||||
|
||||
elif value.startswith('"') and value.endswith('"'):
|
||||
value = value[1:-1]
|
||||
|
||||
elif value.startswith("'") and value.endswith("'"):
|
||||
value = value[1:-1]
|
||||
|
||||
return value
|
||||
|
||||
|
||||
def _process(proc_data):
|
||||
"""
|
||||
Final processing to conform to the schema.
|
||||
|
||||
Parameters:
|
||||
|
||||
proc_data: (Dictionary) raw structured data to process
|
||||
|
||||
Returns:
|
||||
|
||||
Dictionary representing the INI file.
|
||||
"""
|
||||
# remove quotation marks from beginning and end of values
|
||||
for k, v in proc_data.items():
|
||||
if isinstance(v, dict):
|
||||
for key, value in v.items():
|
||||
if isinstance(value, list):
|
||||
v[key] = [_remove_quotes(x) for x in value]
|
||||
else:
|
||||
v[key] = _remove_quotes(value)
|
||||
continue
|
||||
|
||||
elif isinstance(v, list):
|
||||
proc_data[k] = [_remove_quotes(x) for x in v]
|
||||
|
||||
else:
|
||||
proc_data[k] = _remove_quotes(v)
|
||||
|
||||
return proc_data
|
||||
|
||||
|
||||
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:
|
||||
|
||||
Dictionary representing the INI file.
|
||||
"""
|
||||
jc.utils.compatibility(__name__, info.compatible, quiet)
|
||||
jc.utils.input_type_check(data)
|
||||
|
||||
raw_output = {}
|
||||
|
||||
if jc.utils.has_data(data):
|
||||
|
||||
ini_parser = configparser.ConfigParser(
|
||||
dict_type = MultiDict,
|
||||
allow_no_value=True,
|
||||
interpolation=None,
|
||||
default_section=None,
|
||||
empty_lines_in_values=False,
|
||||
strict=False
|
||||
)
|
||||
|
||||
# don't convert keys to lower-case:
|
||||
ini_parser.optionxform = lambda option: option
|
||||
|
||||
try:
|
||||
ini_parser.read_string(data)
|
||||
raw_output = {s: dict(ini_parser.items(s)) for s in ini_parser.sections()}
|
||||
|
||||
except configparser.MissingSectionHeaderError:
|
||||
# find a top-level section name that will not collide with any existing ones
|
||||
while True:
|
||||
my_uuid = str(uuid.uuid4())
|
||||
if my_uuid not in data:
|
||||
break
|
||||
|
||||
data = f'[{my_uuid}]\n' + data
|
||||
ini_parser.read_string(data)
|
||||
temp_dict = {s: dict(ini_parser.items(s)) for s in ini_parser.sections()}
|
||||
|
||||
# move items under fake top-level sections to the root
|
||||
raw_output = temp_dict.pop(my_uuid)
|
||||
|
||||
# get the rest of the sections
|
||||
raw_output.update(temp_dict)
|
||||
|
||||
return raw_output if raw else _process(raw_output)
|
||||
@@ -46,7 +46,6 @@ Schema:
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
Examples:
|
||||
|
||||
$ iwconfig | jc --iwconfig -p
|
||||
@@ -78,7 +77,6 @@ Examples:
|
||||
"missed_beacon": 0
|
||||
}
|
||||
]
|
||||
|
||||
"""
|
||||
import re
|
||||
from typing import List, Dict
|
||||
|
||||
@@ -21,8 +21,8 @@ Usage (module):
|
||||
|
||||
Schema:
|
||||
|
||||
key/value document converted to a dictionary - see the configparser standard
|
||||
library documentation for more details.
|
||||
Key/Value document converted to a dictionary - see the python configparser
|
||||
standard library documentation for more details.
|
||||
|
||||
{
|
||||
"key1": string,
|
||||
@@ -36,6 +36,7 @@ Examples:
|
||||
name = John Doe
|
||||
address=555 California Drive
|
||||
age: 34
|
||||
|
||||
; comments can include # or ;
|
||||
# delimiter can be = or :
|
||||
# quoted values have quotation marks stripped by default
|
||||
@@ -50,15 +51,17 @@ Examples:
|
||||
"occupation": "Engineer"
|
||||
}
|
||||
"""
|
||||
import jc.utils
|
||||
import configparser
|
||||
|
||||
|
||||
class info():
|
||||
"""Provides parser metadata (version, author, etc.)"""
|
||||
version = '1.2'
|
||||
version = '2.0'
|
||||
description = 'Key/Value file and string parser'
|
||||
author = 'Kelly Brazil'
|
||||
author_email = 'kellyjonbrazil@gmail.com'
|
||||
details = 'This is a wrapper for the INI parser'
|
||||
details = 'Using configparser from the python standard library'
|
||||
compatible = ['linux', 'darwin', 'cygwin', 'win32', 'aix', 'freebsd']
|
||||
tags = ['generic', 'file', 'string']
|
||||
|
||||
@@ -66,12 +69,36 @@ class info():
|
||||
__version__ = info.version
|
||||
|
||||
|
||||
def _process(proc_data):
|
||||
"""
|
||||
Final processing to conform to the schema.
|
||||
|
||||
Parameters:
|
||||
|
||||
proc_data: (Dictionary) raw structured data to process
|
||||
|
||||
Returns:
|
||||
|
||||
Dictionary representing a Key/Value pair document.
|
||||
"""
|
||||
# remove quotation marks from beginning and end of values
|
||||
for key in proc_data:
|
||||
if proc_data[key] is None:
|
||||
proc_data[key] = ''
|
||||
|
||||
elif proc_data[key].startswith('"') and proc_data[key].endswith('"'):
|
||||
proc_data[key] = proc_data[key][1:-1]
|
||||
|
||||
elif proc_data[key].startswith("'") and proc_data[key].endswith("'"):
|
||||
proc_data[key] = proc_data[key][1:-1]
|
||||
|
||||
return proc_data
|
||||
|
||||
|
||||
def parse(data, raw=False, quiet=False):
|
||||
"""
|
||||
Main text parsing function
|
||||
|
||||
Note: this is just a wrapper for jc.parsers.ini
|
||||
|
||||
Parameters:
|
||||
|
||||
data: (string) text data to parse
|
||||
@@ -80,7 +107,30 @@ def parse(data, raw=False, quiet=False):
|
||||
|
||||
Returns:
|
||||
|
||||
Dictionary representing the key/value file
|
||||
Dictionary representing a Key/Value pair document.
|
||||
"""
|
||||
import jc.parsers.ini
|
||||
return jc.parsers.ini.parse(data, raw=raw, quiet=quiet)
|
||||
jc.utils.compatibility(__name__, info.compatible, quiet)
|
||||
jc.utils.input_type_check(data)
|
||||
|
||||
raw_output = {}
|
||||
|
||||
if jc.utils.has_data(data):
|
||||
|
||||
kv_parser = configparser.ConfigParser(
|
||||
allow_no_value=True,
|
||||
interpolation=None,
|
||||
default_section=None,
|
||||
strict=False
|
||||
)
|
||||
|
||||
# don't convert keys to lower-case:
|
||||
kv_parser.optionxform = lambda option: option
|
||||
|
||||
data = '[data]\n' + data
|
||||
kv_parser.read_string(data)
|
||||
output_dict = {s: dict(kv_parser.items(s)) for s in kv_parser.sections()}
|
||||
for key, value in output_dict['data'].items():
|
||||
raw_output[key] = value
|
||||
|
||||
return raw_output if raw else _process(raw_output)
|
||||
|
||||
|
||||
@@ -97,6 +97,28 @@ Schema:
|
||||
]
|
||||
}
|
||||
},
|
||||
"videocontrol_descriptors": [
|
||||
{
|
||||
"<item>": {
|
||||
"value": string,
|
||||
"description": string,
|
||||
"attributes": [
|
||||
string
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"videostreaming_descriptors": [
|
||||
{
|
||||
"<item>": {
|
||||
"value": string,
|
||||
"description": string,
|
||||
"attributes": [
|
||||
string
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"endpoint_descriptors": [
|
||||
{
|
||||
"<item>": {
|
||||
@@ -269,7 +291,7 @@ from jc.exceptions import ParseError
|
||||
|
||||
class info():
|
||||
"""Provides parser metadata (version, author, etc.)"""
|
||||
version = '1.2'
|
||||
version = '1.3'
|
||||
description = '`lsusb` command parser'
|
||||
author = 'Kelly Brazil'
|
||||
author_email = 'kellyjonbrazil@gmail.com'
|
||||
@@ -307,6 +329,145 @@ class _NestedDict(dict):
|
||||
return self.setdefault(key, _NestedDict())
|
||||
|
||||
|
||||
class _root_obj:
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
self.list = []
|
||||
|
||||
def _entries_for_this_bus_exist(self, bus_idx):
|
||||
"""Returns true if there are object entries for the corresponding bus index"""
|
||||
for item in self.list:
|
||||
keyname = tuple(item.keys())[0]
|
||||
|
||||
if '_state' in item[keyname] and item[keyname]['_state']['bus_idx'] == bus_idx:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def _update_output(self, bus_idx, output_line):
|
||||
"""modifies output_line dictionary for the corresponding bus index.
|
||||
output_line is the self.output_line attribute from the _lsusb object."""
|
||||
for item in self.list:
|
||||
keyname = tuple(item.keys())[0]
|
||||
|
||||
if '_state' in item[keyname] and item[keyname]['_state']['bus_idx'] == bus_idx:
|
||||
# is this a top level value or an attribute?
|
||||
if item[keyname]['_state']['attribute_value']:
|
||||
last_item = item[keyname]['_state']['last_item']
|
||||
if 'attributes' not in output_line[f'{self.name}'][last_item]:
|
||||
output_line[f'{self.name}'][last_item]['attributes'] = []
|
||||
|
||||
this_attribute = f'{keyname} {item[keyname].get("value", "")} {item[keyname].get("description", "")}'.strip()
|
||||
output_line[f'{self.name}'][last_item]['attributes'].append(this_attribute)
|
||||
continue
|
||||
|
||||
output_line[f'{self.name}'].update(item)
|
||||
del output_line[f'{self.name}'][keyname]['_state']
|
||||
|
||||
|
||||
class _descriptor_obj:
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
self.list = []
|
||||
|
||||
def _entries_for_this_bus_and_interface_idx_exist(self, bus_idx, iface_idx):
|
||||
"""Returns true if there are object entries for the corresponding bus index
|
||||
and interface index"""
|
||||
for item in self.list:
|
||||
keyname = tuple(item.keys())[0]
|
||||
|
||||
if '_state' in item[keyname] and item[keyname]['_state']['bus_idx'] == bus_idx \
|
||||
and item[keyname]['_state']['interface_descriptor_idx'] == iface_idx:
|
||||
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def _update_output(self, bus_idx, iface_idx, output_line):
|
||||
"""modifies output_line dictionary for the corresponding bus index and
|
||||
interface index. output_line is the i_desc_obj object."""
|
||||
for item in self.list:
|
||||
keyname = tuple(item.keys())[0]
|
||||
|
||||
if '_state' in item[keyname] and item[keyname]['_state']['bus_idx'] == bus_idx \
|
||||
and item[keyname]['_state']['interface_descriptor_idx'] == iface_idx:
|
||||
|
||||
# is this a top level value or an attribute?
|
||||
if item[keyname]['_state']['attribute_value']:
|
||||
last_item = item[keyname]['_state']['last_item']
|
||||
if 'attributes' not in output_line[f'{self.name}'][last_item]:
|
||||
output_line[f'{self.name}'][last_item]['attributes'] = []
|
||||
|
||||
this_attribute = f'{keyname} {item[keyname].get("value", "")} {item[keyname].get("description", "")}'.strip()
|
||||
output_line[f'{self.name}'][last_item]['attributes'].append(this_attribute)
|
||||
continue
|
||||
|
||||
output_line[f'{self.name}'].update(item)
|
||||
del output_line[f'{self.name}'][keyname]['_state']
|
||||
|
||||
|
||||
class _descriptor_list:
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
self.list = []
|
||||
|
||||
def _entries_for_this_bus_and_interface_idx_exist(self, bus_idx, iface_idx):
|
||||
"""Returns true if there are object entries for the corresponding bus index
|
||||
and interface index"""
|
||||
for item in self.list:
|
||||
keyname = tuple(item.keys())[0]
|
||||
|
||||
if '_state' in item[keyname] and item[keyname]['_state']['bus_idx'] == bus_idx \
|
||||
and item[keyname]['_state']['interface_descriptor_idx'] == iface_idx:
|
||||
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def _get_objects_list(self, bus_idx, iface_idx):
|
||||
"""Returns a list of descriptor object dictionaries for the corresponding
|
||||
bus index and interface index"""
|
||||
object_collection = []
|
||||
|
||||
# find max number of items in this object that match the bus_idx and iface_idx
|
||||
num_of_items = -1
|
||||
for item in self.list:
|
||||
keyname = tuple(item.keys())[0]
|
||||
|
||||
if '_state' in item[keyname] and item[keyname]['_state']['bus_idx'] == bus_idx \
|
||||
and item[keyname]['_state']['interface_descriptor_idx'] == iface_idx:
|
||||
|
||||
num_of_items = item[keyname]['_state'][f'{self.name}_idx']
|
||||
|
||||
# create and return the collection of objects that match the bus_idx and iface_idx
|
||||
if num_of_items > -1:
|
||||
for obj_idx in range(num_of_items + 1):
|
||||
this_object = {}
|
||||
for item in self.list:
|
||||
keyname = tuple(item.keys())[0]
|
||||
|
||||
if '_state' in item[keyname] and item[keyname]['_state']['bus_idx'] == bus_idx \
|
||||
and item[keyname]['_state']['interface_descriptor_idx'] == iface_idx \
|
||||
and item[keyname]['_state'][f'{self.name}_idx'] == obj_idx:
|
||||
|
||||
# is this a top level value or an attribute?
|
||||
if item[keyname]['_state']['attribute_value']:
|
||||
last_item = item[keyname]['_state']['last_item']
|
||||
if 'attributes' not in this_object[last_item]:
|
||||
this_object[last_item]['attributes'] = []
|
||||
|
||||
this_attribute = f'{keyname} {item[keyname].get("value", "")} {item[keyname].get("description", "")}'.strip()
|
||||
this_object[last_item]['attributes'].append(this_attribute)
|
||||
continue
|
||||
|
||||
this_object.update(item)
|
||||
del item[keyname]['_state']
|
||||
|
||||
object_collection.append(this_object)
|
||||
|
||||
return object_collection
|
||||
|
||||
|
||||
class _LsUsb():
|
||||
def __init__(self):
|
||||
self.raw_output = []
|
||||
@@ -314,27 +475,37 @@ class _LsUsb():
|
||||
|
||||
self.section = ''
|
||||
self.old_section = ''
|
||||
|
||||
# section_header is formatted with the correct spacing to be used with
|
||||
# jc.parsers.universal.sparse_table_parse(). Pad end of string to be at least len of 25
|
||||
# this value changes for different sections (e.g. videocontrol & videostreaming)
|
||||
self.normal_section_header = 'key val description'
|
||||
self.larger_section_header = 'key val description'
|
||||
|
||||
self.bus_idx = -1
|
||||
self.interface_descriptor_idx = -1
|
||||
self.endpoint_descriptor_idx = -1
|
||||
self.videocontrol_interface_descriptor_idx = -1
|
||||
self.videostreaming_interface_descriptor_idx = -1
|
||||
self.last_item = ''
|
||||
self.last_indent = 0
|
||||
self.attribute_value = False
|
||||
|
||||
self.bus_list = []
|
||||
self.device_descriptor_list = []
|
||||
self.configuration_descriptor_list = []
|
||||
self.interface_association_list = []
|
||||
self.device_descriptor = _root_obj('device_descriptor')
|
||||
self.configuration_descriptor = _root_obj('configuration_descriptor')
|
||||
self.interface_association = _root_obj('interface_association')
|
||||
self.interface_descriptor_list = []
|
||||
self.interface_descriptor_attribute_list = []
|
||||
self.cdc_header_list = []
|
||||
self.cdc_call_management_list = []
|
||||
self.cdc_acm_list = []
|
||||
self.cdc_union_list = []
|
||||
self.endpoint_descriptor_list = []
|
||||
self.hid_device_descriptor_list = []
|
||||
self.report_descriptors_list = []
|
||||
self.hub_descriptor_list = []
|
||||
self.cdc_header = _descriptor_obj('cdc_header')
|
||||
self.cdc_call_management = _descriptor_obj('cdc_call_management')
|
||||
self.cdc_acm = _descriptor_obj('cdc_acm')
|
||||
self.cdc_union = _descriptor_obj('cdc_union')
|
||||
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')
|
||||
self.hid_device_descriptor = _descriptor_obj('hid_device_descriptor')
|
||||
# self.report_descriptors_list = [] # not implemented
|
||||
self.hub_descriptor = _root_obj('hub_descriptor')
|
||||
self.hub_port_status_list = []
|
||||
self.device_qualifier_list = []
|
||||
self.device_status_list = []
|
||||
@@ -355,14 +526,21 @@ class _LsUsb():
|
||||
# determine whether this is a top-level value item or lower-level attribute
|
||||
if indent > self.last_indent and self.old_section == self.section:
|
||||
self.attribute_value = True
|
||||
elif indent == self.last_indent and self.attribute_value and self.old_section == self.section:
|
||||
|
||||
elif indent == self.last_indent and self.attribute_value \
|
||||
and self.old_section == self.section:
|
||||
|
||||
self.attribute_value = True
|
||||
|
||||
else:
|
||||
self.attribute_value = False
|
||||
|
||||
# section_header is formatted with the correct spacing to be used with
|
||||
# jc.parsers.universal.sparse_table_parse(). Pad end of string to be at least len of 25
|
||||
section_header = 'key val description'
|
||||
section_header = self.normal_section_header
|
||||
|
||||
if self.section == 'videocontrol_interface_descriptor' \
|
||||
or self.section == 'videostreaming_interface_descriptor':
|
||||
|
||||
section_header = self.larger_section_header
|
||||
|
||||
temp_obj = [section_header, line.strip() + (' ' * 25)]
|
||||
temp_obj = sparse_table_parse(temp_obj)
|
||||
@@ -377,7 +555,9 @@ class _LsUsb():
|
||||
'last_item': self.last_item,
|
||||
'bus_idx': self.bus_idx,
|
||||
'interface_descriptor_idx': self.interface_descriptor_idx,
|
||||
'endpoint_descriptor_idx': self.endpoint_descriptor_idx
|
||||
'endpoint_descriptor_idx': self.endpoint_descriptor_idx,
|
||||
'videocontrol_interface_descriptor_idx': self.videocontrol_interface_descriptor_idx,
|
||||
'videostreaming_interface_descriptor_idx': self.videostreaming_interface_descriptor_idx
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -429,12 +609,15 @@ class _LsUsb():
|
||||
self.attribute_value = False
|
||||
return True
|
||||
|
||||
# bus information is on the same line so need to extract data immediately and set indexes
|
||||
# bus information is on the same line so need to extract data
|
||||
# immediately and set indexes
|
||||
if line.startswith('Bus '):
|
||||
self.section = 'bus'
|
||||
self.bus_idx += 1
|
||||
self.interface_descriptor_idx = -1
|
||||
self.endpoint_descriptor_idx = -1
|
||||
self.videocontrol_interface_descriptor_idx = -1
|
||||
self.videostreaming_interface_descriptor_idx = -1
|
||||
self.attribute_value = False
|
||||
line_split = line.strip().split(maxsplit=6)
|
||||
self.bus_list.append(
|
||||
@@ -442,7 +625,8 @@ class _LsUsb():
|
||||
'bus': line_split[1],
|
||||
'device': line_split[3][:-1],
|
||||
'id': line_split[5],
|
||||
'description': (line_split[6:7] or [None])[0], # way to get a list item or None
|
||||
# way to get a list item or None
|
||||
'description': (line_split[6:7] or [None])[0],
|
||||
'_state': {
|
||||
'bus_idx': self.bus_idx
|
||||
}
|
||||
@@ -450,22 +634,36 @@ class _LsUsb():
|
||||
)
|
||||
return True
|
||||
|
||||
# This section is a list, so need to update indexes
|
||||
# These sections are lists, so need to update indexes
|
||||
if line.startswith(' Interface Descriptor:'):
|
||||
self.section = 'interface_descriptor'
|
||||
self.interface_descriptor_idx += 1
|
||||
self.endpoint_descriptor_idx = -1
|
||||
self.videocontrol_interface_descriptor_idx = -1
|
||||
self.videostreaming_interface_descriptor_idx = -1
|
||||
self.attribute_value = False
|
||||
return True
|
||||
|
||||
# This section is a list, so need to update the index
|
||||
if line.startswith(' Endpoint Descriptor:'):
|
||||
self.section = 'endpoint_descriptor'
|
||||
self.endpoint_descriptor_idx += 1
|
||||
self.attribute_value = False
|
||||
return True
|
||||
|
||||
# some device status information is displayed on the initial line so need to extract immediately
|
||||
if line.startswith(' VideoControl Interface Descriptor:'):
|
||||
self.section = 'videocontrol_interface_descriptor'
|
||||
self.videocontrol_interface_descriptor_idx += 1
|
||||
self.attribute_value = False
|
||||
return True
|
||||
|
||||
if line.startswith(' VideoStreaming Interface Descriptor:'):
|
||||
self.section = 'videostreaming_interface_descriptor'
|
||||
self.videostreaming_interface_descriptor_idx += 1
|
||||
self.attribute_value = False
|
||||
return True
|
||||
|
||||
# some device status information is displayed on the initial line so
|
||||
# need to extract immediately
|
||||
if line.startswith('Device Status:'):
|
||||
self.section = 'device_status'
|
||||
self.attribute_value = False
|
||||
@@ -507,18 +705,20 @@ class _LsUsb():
|
||||
|
||||
def _populate_lists(self, line):
|
||||
section_list_map = {
|
||||
'device_descriptor': self.device_descriptor_list,
|
||||
'configuration_descriptor': self.configuration_descriptor_list,
|
||||
'interface_association': self.interface_association_list,
|
||||
'device_descriptor': self.device_descriptor.list,
|
||||
'configuration_descriptor': self.configuration_descriptor.list,
|
||||
'interface_association': self.interface_association.list,
|
||||
'interface_descriptor': self.interface_descriptor_list,
|
||||
'cdc_header': self.cdc_header_list,
|
||||
'cdc_call_management': self.cdc_call_management_list,
|
||||
'cdc_acm': self.cdc_acm_list,
|
||||
'cdc_union': self.cdc_union_list,
|
||||
'hid_device_descriptor': self.hid_device_descriptor_list,
|
||||
'report_descriptors': self.report_descriptors_list,
|
||||
'endpoint_descriptor': self.endpoint_descriptor_list,
|
||||
'hub_descriptor': self.hub_descriptor_list,
|
||||
'cdc_header': self.cdc_header.list,
|
||||
'cdc_call_management': self.cdc_call_management.list,
|
||||
'cdc_acm': self.cdc_acm.list,
|
||||
'cdc_union': self.cdc_union.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,
|
||||
'videostreaming_interface_descriptor': self.videostreaming_interface_descriptors.list,
|
||||
'endpoint_descriptor': self.endpoint_descriptors.list,
|
||||
'hub_descriptor': self.hub_descriptor.list,
|
||||
'device_qualifier': self.device_qualifier_list
|
||||
}
|
||||
|
||||
@@ -528,7 +728,9 @@ class _LsUsb():
|
||||
return True
|
||||
|
||||
# special handling of these sections
|
||||
if line.startswith(' ') and self.section == 'hub_port_status':
|
||||
if line.startswith(' ') and not line.startswith(' ') \
|
||||
and self.section == 'hub_port_status':
|
||||
|
||||
self.hub_port_status_list.append(self._add_hub_port_status_attributes(line))
|
||||
return True
|
||||
|
||||
@@ -547,6 +749,10 @@ class _LsUsb():
|
||||
['device_descriptor']['configuration_descriptor']['interface_association'] = {}
|
||||
['device_descriptor']['configuration_descriptor']['interface_descriptors'] = []
|
||||
['device_descriptor']['configuration_descriptor']['interface_descriptors'][0] = {}
|
||||
['device_descriptor']['configuration_descriptor']['interface_descriptors'][0]['videocontrol_interface_descriptors'] = []
|
||||
['device_descriptor']['configuration_descriptor']['interface_descriptors'][0]['videocontrol_interface_descriptors'][0] = {}
|
||||
['device_descriptor']['configuration_descriptor']['interface_descriptors'][0]['videostreaming_interface_descriptors'] = []
|
||||
['device_descriptor']['configuration_descriptor']['interface_descriptors'][0]['videostreaming_interface_descriptors'][0] = {}
|
||||
['device_descriptor']['configuration_descriptor']['interface_descriptors'][0]['cdc_header'] = {}
|
||||
['device_descriptor']['configuration_descriptor']['interface_descriptors'][0]['cdc_call_management'] = {}
|
||||
['device_descriptor']['configuration_descriptor']['interface_descriptors'][0]['cdc_acm'] = {}
|
||||
@@ -568,81 +774,56 @@ class _LsUsb():
|
||||
del item['_state']
|
||||
self.output_line.update(item)
|
||||
|
||||
for dd in self.device_descriptor_list:
|
||||
keyname = tuple(dd.keys())[0]
|
||||
if '_state' in dd[keyname] and dd[keyname]['_state']['bus_idx'] == idx:
|
||||
# add initial root-level keys
|
||||
if self.device_descriptor._entries_for_this_bus_exist(idx):
|
||||
self.device_descriptor._update_output(idx, self.output_line)
|
||||
|
||||
# is this a top level value or an attribute?
|
||||
if dd[keyname]['_state']['attribute_value']:
|
||||
last_item = dd[keyname]['_state']['last_item']
|
||||
if 'attributes' not in self.output_line['device_descriptor'][last_item]:
|
||||
self.output_line['device_descriptor'][last_item]['attributes'] = []
|
||||
if self.configuration_descriptor._entries_for_this_bus_exist(idx):
|
||||
self.configuration_descriptor._update_output(
|
||||
idx, self.output_line['device_descriptor']
|
||||
)
|
||||
|
||||
this_attribute = f'{keyname} {dd[keyname].get("value", "")} {dd[keyname].get("description", "")}'.strip()
|
||||
self.output_line['device_descriptor'][last_item]['attributes'].append(this_attribute)
|
||||
continue
|
||||
if self.interface_association._entries_for_this_bus_exist(idx):
|
||||
self.interface_association._update_output(
|
||||
idx, self.output_line['device_descriptor']['configuration_descriptor']
|
||||
)
|
||||
|
||||
self.output_line['device_descriptor'].update(dd)
|
||||
del self.output_line['device_descriptor'][keyname]['_state']
|
||||
|
||||
for cd in self.configuration_descriptor_list:
|
||||
keyname = tuple(cd.keys())[0]
|
||||
if '_state' in cd[keyname] and cd[keyname]['_state']['bus_idx'] == idx:
|
||||
|
||||
# is this a top level value or an attribute?
|
||||
if cd[keyname]['_state']['attribute_value']:
|
||||
last_item = cd[keyname]['_state']['last_item']
|
||||
if 'attributes' not in self.output_line['device_descriptor']['configuration_descriptor'][last_item]:
|
||||
self.output_line['device_descriptor']['configuration_descriptor'][last_item]['attributes'] = []
|
||||
|
||||
this_attribute = f'{keyname} {cd[keyname].get("value", "")} {cd[keyname].get("description", "")}'.strip()
|
||||
self.output_line['device_descriptor']['configuration_descriptor'][last_item]['attributes'].append(this_attribute)
|
||||
continue
|
||||
|
||||
self.output_line['device_descriptor']['configuration_descriptor'].update(cd)
|
||||
del self.output_line['device_descriptor']['configuration_descriptor'][keyname]['_state']
|
||||
|
||||
for ia in self.interface_association_list:
|
||||
keyname = tuple(ia.keys())[0]
|
||||
if '_state' in ia[keyname] and ia[keyname]['_state']['bus_idx'] == idx:
|
||||
|
||||
# is this a top level value or an attribute?
|
||||
if ia[keyname]['_state']['attribute_value']:
|
||||
last_item = ia[keyname]['_state']['last_item']
|
||||
if 'attributes' not in self.output_line['device_descriptor']['configuration_descriptor']['interface_association'][last_item]:
|
||||
self.output_line['device_descriptor']['configuration_descriptor']['interface_association'][last_item]['attributes'] = []
|
||||
|
||||
this_attribute = f'{keyname} {ia[keyname].get("value", "")} {ia[keyname].get("description", "")}'.strip()
|
||||
self.output_line['device_descriptor']['configuration_descriptor']['interface_association'][last_item]['attributes'].append(this_attribute)
|
||||
continue
|
||||
|
||||
self.output_line['device_descriptor']['configuration_descriptor']['interface_association'].update(ia)
|
||||
del self.output_line['device_descriptor']['configuration_descriptor']['interface_association'][keyname]['_state']
|
||||
|
||||
# add interface_descriptor key if it doesn't exist and there are entries for this bus
|
||||
# add interface_descriptor key if it doesn't exist and there
|
||||
# are entries for this bus
|
||||
for iface_attrs in self.interface_descriptor_list:
|
||||
keyname = tuple(iface_attrs.keys())[0]
|
||||
if '_state' in iface_attrs[keyname] and iface_attrs[keyname]['_state']['bus_idx'] == idx:
|
||||
|
||||
if '_state' in iface_attrs[keyname] \
|
||||
and iface_attrs[keyname]['_state']['bus_idx'] == idx:
|
||||
|
||||
self.output_line['device_descriptor']['configuration_descriptor']['interface_descriptors'] = []
|
||||
|
||||
# find max index for this bus idx, then iterate over that range
|
||||
i_desc_iters = -1
|
||||
for iface_attrs in self.interface_descriptor_list:
|
||||
keyname = tuple(iface_attrs.keys())[0]
|
||||
if '_state' in iface_attrs[keyname] and iface_attrs[keyname]['_state']['bus_idx'] == idx:
|
||||
|
||||
if '_state' in iface_attrs[keyname] \
|
||||
and iface_attrs[keyname]['_state']['bus_idx'] == idx:
|
||||
|
||||
i_desc_iters = iface_attrs[keyname]['_state']['interface_descriptor_idx']
|
||||
|
||||
# create the interface descriptor object
|
||||
if i_desc_iters > -1:
|
||||
for iface_idx in range(i_desc_iters + 1):
|
||||
i_desc_obj = _NestedDict()
|
||||
|
||||
for iface_attrs in self.interface_descriptor_list:
|
||||
keyname = tuple(iface_attrs.keys())[0]
|
||||
if '_state' in iface_attrs[keyname] and iface_attrs[keyname]['_state']['bus_idx'] == idx and iface_attrs[keyname]['_state']['interface_descriptor_idx'] == iface_idx:
|
||||
|
||||
if '_state' in iface_attrs[keyname] \
|
||||
and iface_attrs[keyname]['_state']['bus_idx'] == idx \
|
||||
and iface_attrs[keyname]['_state']['interface_descriptor_idx'] == iface_idx:
|
||||
|
||||
# is this a top level value or an attribute?
|
||||
if iface_attrs[keyname]['_state']['attribute_value']:
|
||||
last_item = iface_attrs[keyname]['_state']['last_item']
|
||||
|
||||
if 'attributes' not in i_desc_obj[last_item]:
|
||||
i_desc_obj[last_item]['attributes'] = []
|
||||
|
||||
@@ -653,168 +834,70 @@ class _LsUsb():
|
||||
del iface_attrs[keyname]['_state']
|
||||
i_desc_obj.update(iface_attrs)
|
||||
|
||||
# add other nodes to the object (cdc_header, endpoint descriptors, etc.)
|
||||
for ch in self.cdc_header_list:
|
||||
keyname = tuple(ch.keys())[0]
|
||||
if '_state' in ch[keyname] and ch[keyname]['_state']['bus_idx'] == idx and ch[keyname]['_state']['interface_descriptor_idx'] == iface_idx:
|
||||
# add the rest of the interface descriptor keys to the object
|
||||
if self.cdc_header._entries_for_this_bus_and_interface_idx_exist(idx, iface_idx):
|
||||
self.cdc_header._update_output(idx, iface_idx, i_desc_obj)
|
||||
|
||||
# is this a top level value or an attribute?
|
||||
if ch[keyname]['_state']['attribute_value']:
|
||||
last_item = ch[keyname]['_state']['last_item']
|
||||
if 'attributes' not in i_desc_obj['cdc_header'][last_item]:
|
||||
i_desc_obj['cdc_header'][last_item]['attributes'] = []
|
||||
if self.cdc_call_management._entries_for_this_bus_and_interface_idx_exist(idx, iface_idx):
|
||||
self.cdc_call_management._update_output(idx, iface_idx, i_desc_obj)
|
||||
|
||||
this_attribute = f'{keyname} {ch[keyname].get("value", "")} {ch[keyname].get("description", "")}'.strip()
|
||||
i_desc_obj['cdc_header'][last_item]['attributes'].append(this_attribute)
|
||||
continue
|
||||
if self.cdc_acm._entries_for_this_bus_and_interface_idx_exist(idx, iface_idx):
|
||||
self.cdc_acm._update_output(idx, iface_idx, i_desc_obj)
|
||||
|
||||
i_desc_obj['cdc_header'].update(ch)
|
||||
del i_desc_obj['cdc_header'][keyname]['_state']
|
||||
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)
|
||||
|
||||
for ccm in self.cdc_call_management_list:
|
||||
keyname = tuple(ccm.keys())[0]
|
||||
if '_state' in ccm[keyname] and ccm[keyname]['_state']['bus_idx'] == idx and ccm[keyname]['_state']['interface_descriptor_idx'] == iface_idx:
|
||||
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)
|
||||
|
||||
# is this a top level value or an attribute?
|
||||
if ccm[keyname]['_state']['attribute_value']:
|
||||
last_item = ccm[keyname]['_state']['last_item']
|
||||
if 'attributes' not in i_desc_obj['cdc_call_management'][last_item]:
|
||||
i_desc_obj['cdc_call_management'][last_item]['attributes'] = []
|
||||
# Not Implemented: Report Descriptors (need more samples)
|
||||
# for rd in self.report_descriptors_list:
|
||||
# keyname = tuple(rd.keys())[0]
|
||||
# if '_state' in rd[keyname] and rd[keyname]['_state']['bus_idx'] == idx and rd[keyname]['_state']['interface_descriptor_idx'] == iface_idx:
|
||||
# i_desc_obj['hid_device_descriptor']['report_descriptors'].update(rd)
|
||||
# del i_desc_obj['hid_device_descriptor']['report_descriptors'][keyname]['_state']
|
||||
|
||||
this_attribute = f'{keyname} {ccm[keyname].get("value", "")} {ccm[keyname].get("description", "")}'.strip()
|
||||
i_desc_obj['cdc_call_management'][last_item]['attributes'].append(this_attribute)
|
||||
continue
|
||||
if self.videocontrol_interface_descriptors._entries_for_this_bus_and_interface_idx_exist(idx, iface_idx):
|
||||
i_desc_obj['videocontrol_interface_descriptors'] = []
|
||||
i_desc_obj['videocontrol_interface_descriptors'].extend(
|
||||
self.videocontrol_interface_descriptors._get_objects_list(idx, iface_idx)
|
||||
)
|
||||
|
||||
i_desc_obj['cdc_call_management'].update(ccm)
|
||||
del i_desc_obj['cdc_call_management'][keyname]['_state']
|
||||
if self.videostreaming_interface_descriptors._entries_for_this_bus_and_interface_idx_exist(idx, iface_idx):
|
||||
i_desc_obj['videostreaming_interface_descriptors'] = []
|
||||
i_desc_obj['videostreaming_interface_descriptors'].extend(
|
||||
self.videostreaming_interface_descriptors._get_objects_list(idx, iface_idx)
|
||||
)
|
||||
|
||||
for ca in self.cdc_acm_list:
|
||||
keyname = tuple(ca.keys())[0]
|
||||
if '_state' in ca[keyname] and ca[keyname]['_state']['bus_idx'] == idx and ca[keyname]['_state']['interface_descriptor_idx'] == iface_idx:
|
||||
|
||||
# is this a top level value or an attribute?
|
||||
if ca[keyname]['_state']['attribute_value']:
|
||||
last_item = ca[keyname]['_state']['last_item']
|
||||
if 'attributes' not in i_desc_obj['cdc_acm'][last_item]:
|
||||
i_desc_obj['cdc_acm'][last_item]['attributes'] = []
|
||||
|
||||
this_attribute = f'{keyname} {ca[keyname].get("value", "")} {ca[keyname].get("description", "")}'.strip()
|
||||
i_desc_obj['cdc_acm'][last_item]['attributes'].append(this_attribute)
|
||||
continue
|
||||
|
||||
i_desc_obj['cdc_acm'].update(ca)
|
||||
del i_desc_obj['cdc_acm'][keyname]['_state']
|
||||
|
||||
for cu in self.cdc_union_list:
|
||||
keyname = tuple(cu.keys())[0]
|
||||
if '_state' in cu[keyname] and cu[keyname]['_state']['bus_idx'] == idx and cu[keyname]['_state']['interface_descriptor_idx'] == iface_idx:
|
||||
|
||||
# is this a top level value or an attribute?
|
||||
if cu[keyname]['_state']['attribute_value']:
|
||||
last_item = cu[keyname]['_state']['last_item']
|
||||
if 'attributes' not in i_desc_obj['cdc_union'][last_item]:
|
||||
i_desc_obj['cdc_union'][last_item]['attributes'] = []
|
||||
|
||||
this_attribute = f'{keyname} {cu[keyname].get("value", "")} {cu[keyname].get("description", "")}'.strip()
|
||||
i_desc_obj['cdc_union'][last_item]['attributes'].append(this_attribute)
|
||||
continue
|
||||
|
||||
i_desc_obj['cdc_union'].update(cu)
|
||||
del i_desc_obj['cdc_union'][keyname]['_state']
|
||||
|
||||
for hidd in self.hid_device_descriptor_list:
|
||||
keyname = tuple(hidd.keys())[0]
|
||||
if '_state' in hidd[keyname] and hidd[keyname]['_state']['bus_idx'] == idx and hidd[keyname]['_state']['interface_descriptor_idx'] == iface_idx:
|
||||
|
||||
# is this a top level value or an attribute?
|
||||
if hidd[keyname]['_state']['attribute_value']:
|
||||
last_item = hidd[keyname]['_state']['last_item']
|
||||
if 'attributes' not in i_desc_obj['hid_device_descriptor'][last_item]:
|
||||
i_desc_obj['hid_device_descriptor'][last_item]['attributes'] = []
|
||||
|
||||
this_attribute = f'{keyname} {hidd[keyname].get("value", "")} {hidd[keyname].get("description", "")}'.strip()
|
||||
i_desc_obj['hid_device_descriptor'][last_item]['attributes'].append(this_attribute)
|
||||
continue
|
||||
|
||||
i_desc_obj['hid_device_descriptor'].update(hidd)
|
||||
del i_desc_obj['hid_device_descriptor'][keyname]['_state']
|
||||
|
||||
# Not Implemented: Report Descriptors (need more samples)
|
||||
# for rd in self.report_descriptors_list:
|
||||
# keyname = tuple(rd.keys())[0]
|
||||
# if '_state' in rd[keyname] and rd[keyname]['_state']['bus_idx'] == idx and rd[keyname]['_state']['interface_descriptor_idx'] == iface_idx:
|
||||
# i_desc_obj['hid_device_descriptor']['report_descriptors'].update(rd)
|
||||
# del i_desc_obj['hid_device_descriptor']['report_descriptors'][keyname]['_state']
|
||||
|
||||
# add endpoint_descriptor key if it doesn't exist and there are entries for this interface_descriptor
|
||||
for endpoint_attrs in self.endpoint_descriptor_list:
|
||||
keyname = tuple(endpoint_attrs.keys())[0]
|
||||
if '_state' in endpoint_attrs[keyname] and endpoint_attrs[keyname]['_state']['bus_idx'] == idx and endpoint_attrs[keyname]['_state']['interface_descriptor_idx'] == iface_idx:
|
||||
i_desc_obj['endpoint_descriptors'] = []
|
||||
|
||||
# find max index for this endpoint_descriptor idx, then iterate over that range
|
||||
e_desc_iters = -1
|
||||
for endpoint_attrs in self.endpoint_descriptor_list:
|
||||
keyname = tuple(endpoint_attrs.keys())[0]
|
||||
if '_state' in endpoint_attrs[keyname] and endpoint_attrs[keyname]['_state']['bus_idx'] == idx and endpoint_attrs[keyname]['_state']['interface_descriptor_idx'] == iface_idx:
|
||||
e_desc_iters = endpoint_attrs[keyname]['_state']['endpoint_descriptor_idx']
|
||||
|
||||
# create the endpoint descriptor object
|
||||
if e_desc_iters > -1:
|
||||
for endpoint_idx in range(e_desc_iters + 1):
|
||||
e_desc_obj = {}
|
||||
for endpoint_attrs in self.endpoint_descriptor_list:
|
||||
keyname = tuple(endpoint_attrs.keys())[0]
|
||||
if '_state' in endpoint_attrs[keyname] and endpoint_attrs[keyname]['_state']['bus_idx'] == idx and endpoint_attrs[keyname]['_state']['interface_descriptor_idx'] == iface_idx and endpoint_attrs[keyname]['_state']['endpoint_descriptor_idx'] == endpoint_idx:
|
||||
|
||||
# is this a top level value or an attribute?
|
||||
if endpoint_attrs[keyname]['_state']['attribute_value']:
|
||||
last_item = endpoint_attrs[keyname]['_state']['last_item']
|
||||
if 'attributes' not in e_desc_obj[last_item]:
|
||||
e_desc_obj[last_item]['attributes'] = []
|
||||
|
||||
this_attribute = f'{keyname} {endpoint_attrs[keyname].get("value", "")} {endpoint_attrs[keyname].get("description", "")}'.strip()
|
||||
e_desc_obj[last_item]['attributes'].append(this_attribute)
|
||||
continue
|
||||
|
||||
e_desc_obj.update(endpoint_attrs)
|
||||
del endpoint_attrs[keyname]['_state']
|
||||
|
||||
i_desc_obj['endpoint_descriptors'].append(e_desc_obj)
|
||||
if self.endpoint_descriptors._entries_for_this_bus_and_interface_idx_exist(idx, iface_idx):
|
||||
i_desc_obj['endpoint_descriptors'] = []
|
||||
i_desc_obj['endpoint_descriptors'].extend(
|
||||
self.endpoint_descriptors._get_objects_list(idx, iface_idx)
|
||||
)
|
||||
|
||||
# add the object to the list of interface descriptors
|
||||
self.output_line['device_descriptor']['configuration_descriptor']['interface_descriptors'].append(i_desc_obj)
|
||||
|
||||
for hd in self.hub_descriptor_list:
|
||||
keyname = tuple(hd.keys())[0]
|
||||
if '_state' in hd[keyname] and hd[keyname]['_state']['bus_idx'] == idx:
|
||||
|
||||
# is this a top level value or an attribute?
|
||||
if hd[keyname]['_state']['attribute_value']:
|
||||
last_item = hd[keyname]['_state']['last_item']
|
||||
if 'attributes' not in self.output_line['hub_descriptor'][last_item]:
|
||||
self.output_line['hub_descriptor'][last_item]['attributes'] = []
|
||||
|
||||
this_attribute = f'{keyname} {hd[keyname].get("value", "")} {hd[keyname].get("description", "")}'.strip()
|
||||
self.output_line['hub_descriptor'][last_item]['attributes'].append(this_attribute)
|
||||
continue
|
||||
|
||||
self.output_line['hub_descriptor'].update(hd)
|
||||
del self.output_line['hub_descriptor'][keyname]['_state']
|
||||
# add final root-level keys
|
||||
if self.hub_descriptor._entries_for_this_bus_exist(idx):
|
||||
self.hub_descriptor._update_output(idx, self.output_line)
|
||||
|
||||
for hps in self.hub_port_status_list:
|
||||
keyname = tuple(hps.keys())[0]
|
||||
|
||||
if '_state' in hps[keyname] and hps[keyname]['_state']['bus_idx'] == idx:
|
||||
self.output_line['hub_descriptor']['hub_port_status'].update(hps)
|
||||
del self.output_line['hub_descriptor']['hub_port_status'][keyname]['_state']
|
||||
|
||||
for dq in self.device_qualifier_list:
|
||||
keyname = tuple(dq.keys())[0]
|
||||
|
||||
if '_state' in dq[keyname] and dq[keyname]['_state']['bus_idx'] == idx:
|
||||
self.output_line['device_qualifier'].update(dq)
|
||||
del self.output_line['device_qualifier'][keyname]['_state']
|
||||
|
||||
for ds in self.device_status_list:
|
||||
|
||||
if '_state' in ds and ds['_state']['bus_idx'] == idx:
|
||||
self.output_line['device_status'].update(ds)
|
||||
del self.output_line['device_status']['_state']
|
||||
|
||||
@@ -20,7 +20,7 @@ Schema:
|
||||
"filesystem": string,
|
||||
"mount_point": string,
|
||||
"type": string,
|
||||
"access": [
|
||||
"options": [
|
||||
string
|
||||
]
|
||||
}
|
||||
@@ -34,7 +34,7 @@ Example:
|
||||
"filesystem": "sysfs",
|
||||
"mount_point": "/sys",
|
||||
"type": "sysfs",
|
||||
"access": [
|
||||
"options": [
|
||||
"rw",
|
||||
"nosuid",
|
||||
"nodev",
|
||||
@@ -46,7 +46,7 @@ Example:
|
||||
"filesystem": "proc",
|
||||
"mount_point": "/proc",
|
||||
"type": "proc",
|
||||
"access": [
|
||||
"options": [
|
||||
"rw",
|
||||
"nosuid",
|
||||
"nodev",
|
||||
@@ -58,7 +58,7 @@ Example:
|
||||
"filesystem": "udev",
|
||||
"mount_point": "/dev",
|
||||
"type": "devtmpfs",
|
||||
"access": [
|
||||
"options": [
|
||||
"rw",
|
||||
"nosuid",
|
||||
"relatime",
|
||||
@@ -75,11 +75,11 @@ import jc.utils
|
||||
|
||||
class info():
|
||||
"""Provides parser metadata (version, author, etc.)"""
|
||||
version = '1.7'
|
||||
version = '1.8'
|
||||
description = '`mount` command parser'
|
||||
author = 'Kelly Brazil'
|
||||
author_email = 'kellyjonbrazil@gmail.com'
|
||||
compatible = ['linux', 'darwin', 'freebsd']
|
||||
compatible = ['linux', 'darwin', 'freebsd', 'aix']
|
||||
magic_commands = ['mount']
|
||||
tags = ['command']
|
||||
|
||||
@@ -138,10 +138,38 @@ def _linux_parse(data):
|
||||
output_line['filesystem'] = parsed_line[0]
|
||||
output_line['mount_point'] = parsed_line[2]
|
||||
output_line['type'] = parsed_line[4]
|
||||
output_line['options'] = parsed_line[5].lstrip('(').rstrip(')').split(',')
|
||||
|
||||
options = parsed_line[5].lstrip('(').rstrip(')').split(',')
|
||||
output.append(output_line)
|
||||
|
||||
output_line['options'] = options
|
||||
return output
|
||||
|
||||
def _aix_parse(data):
|
||||
output = []
|
||||
|
||||
# AIX mount command starts with these headers:
|
||||
# node mounted mounted over vfs date options
|
||||
# -------- --------------- --------------- ------ ------------ ---------------
|
||||
# Remove them
|
||||
data.pop(0)
|
||||
data.pop(0)
|
||||
|
||||
for entry in data:
|
||||
output_line = {}
|
||||
parsed_line = entry.split()
|
||||
|
||||
# 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,
|
||||
# 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:
|
||||
parsed_line.pop(0)
|
||||
|
||||
output_line['filesystem'] = parsed_line[0]
|
||||
output_line['mount_point'] = parsed_line[1]
|
||||
output_line['type'] = parsed_line[2]
|
||||
output_line['options'] = parsed_line[6].lstrip('(').rstrip(')').split(',')
|
||||
|
||||
output.append(output_line)
|
||||
|
||||
@@ -171,9 +199,12 @@ def parse(data, raw=False, quiet=False):
|
||||
|
||||
if jc.utils.has_data(data):
|
||||
|
||||
# check for OSX output
|
||||
# check for OSX and AIX output
|
||||
if ' type ' not in cleandata[0]:
|
||||
raw_output = _osx_parse(cleandata)
|
||||
if 'node' in cleandata[0]:
|
||||
raw_output = _aix_parse(cleandata)
|
||||
else:
|
||||
raw_output = _osx_parse(cleandata)
|
||||
|
||||
else:
|
||||
raw_output = _linux_parse(cleandata)
|
||||
|
||||
@@ -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)
|
||||
29
jc/parsers/pyedid/main.py
Normal file
29
jc/parsers/pyedid/main.py
Normal file
@@ -0,0 +1,29 @@
|
||||
"""
|
||||
Entrypoint
|
||||
"""
|
||||
|
||||
from pyedid.edid import Edid
|
||||
from pyedid.helpers.edid_helper import EdidHelper
|
||||
from pyedid.helpers.registry import Registry
|
||||
|
||||
|
||||
def main():
|
||||
"""Main func"""
|
||||
|
||||
edid_csv_cache = "/tmp/pyedid-database.csv"
|
||||
|
||||
try:
|
||||
registry = Registry.from_csv(edid_csv_cache)
|
||||
except FileNotFoundError:
|
||||
print("Loading registry from web...")
|
||||
registry = Registry.from_web()
|
||||
print("Done!\n")
|
||||
registry.to_csv(edid_csv_cache)
|
||||
|
||||
for raw in EdidHelper.get_edids():
|
||||
edid = Edid(raw, registry)
|
||||
print(edid)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
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
|
||||
|
||||
688
jc/parsers/ssh_conf.py
Normal file
688
jc/parsers/ssh_conf.py
Normal file
@@ -0,0 +1,688 @@
|
||||
"""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"
|
||||
}
|
||||
]
|
||||
"""
|
||||
from typing import Set, List, Dict
|
||||
from jc.jc_types import JSONDictType
|
||||
import jc.utils
|
||||
|
||||
|
||||
class info():
|
||||
"""Provides parser metadata (version, author, etc.)"""
|
||||
version = '1.0'
|
||||
description = '`ssh` config file and `ssh -G` command parser'
|
||||
author = 'Kelly Brazil'
|
||||
author_email = 'kellyjonbrazil@gmail.com'
|
||||
compatible = ['linux', 'darwin', 'freebsd']
|
||||
magic_commands = ['ssh -G']
|
||||
tags = ['command', 'file']
|
||||
|
||||
|
||||
__version__ = info.version
|
||||
|
||||
|
||||
def _process(proc_data: List[JSONDictType]) -> List[JSONDictType]:
|
||||
"""
|
||||
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.
|
||||
"""
|
||||
split_fields_space: Set[str] = {
|
||||
'canonicaldomains', 'globalknownhostsfile', 'include', 'ipqos',
|
||||
'permitremoteopen', 'sendenv', 'setenv', 'userknownhostsfile'
|
||||
}
|
||||
|
||||
split_fields_comma: Set[str] = {
|
||||
'canonicalizepermittedcnames', 'casignaturealgorithms', 'ciphers',
|
||||
'hostbasedacceptedalgorithms', 'hostkeyalgorithms',
|
||||
'kbdinteractivedevices', 'kexalgorithms', 'logverbose', 'macs',
|
||||
'preferredauthentications', 'proxyjump', 'pubkeyacceptedalgorithms'
|
||||
}
|
||||
|
||||
int_list: Set[str] = {
|
||||
'canonicalizemaxdots', 'connectionattempts', 'connecttimeout',
|
||||
'forwardx11timeout', 'numberofpasswordprompts', 'port', 'protocol',
|
||||
'requiredrsasize', 'serveralivecountmax', 'serveraliveinterval'
|
||||
}
|
||||
|
||||
for host in proc_data:
|
||||
dict_copy = host.copy()
|
||||
for key, val in dict_copy.items():
|
||||
# these are list values
|
||||
if key == 'sendenv' or key == 'setenv' or key == 'include':
|
||||
new_list: List[str] = []
|
||||
for item in val:
|
||||
new_list.extend(item.split())
|
||||
host[key] = new_list
|
||||
continue
|
||||
|
||||
if key in split_fields_space:
|
||||
host[key] = val.split()
|
||||
continue
|
||||
|
||||
if key in split_fields_comma:
|
||||
host[key] = val.split(',')
|
||||
continue
|
||||
|
||||
for key, val in host.items():
|
||||
if key in int_list:
|
||||
host[key] = jc.utils.convert_to_int(val)
|
||||
|
||||
return proc_data
|
||||
|
||||
|
||||
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)
|
||||
|
||||
raw_output: List = []
|
||||
host: Dict = {}
|
||||
|
||||
multi_fields: Set[str] = {
|
||||
'certificatefile', 'identityfile', 'include', 'localforward',
|
||||
'sendenv', 'setenv'
|
||||
}
|
||||
|
||||
modified_fields: Set[str] = {
|
||||
'casignaturealgorithms', 'ciphers', 'hostbasedacceptedalgorithms',
|
||||
'HostKeyAlgorithms', 'kexalgorithms', 'macs',
|
||||
'pubkeyacceptedalgorithms'
|
||||
}
|
||||
|
||||
modifiers: Set[str] = {'+', '-', '^'}
|
||||
|
||||
match_block_found = False
|
||||
|
||||
if jc.utils.has_data(data):
|
||||
|
||||
for line in filter(None, data.splitlines()):
|
||||
# skip any lines with only whitespace
|
||||
if not line.strip():
|
||||
continue
|
||||
|
||||
# support configuration file by skipping commented lines
|
||||
if line.strip().startswith('#'):
|
||||
continue
|
||||
|
||||
if line.strip().startswith('Host '):
|
||||
if host:
|
||||
raw_output.append(host)
|
||||
|
||||
hostnames = line.split(maxsplit=1)[1]
|
||||
host = {
|
||||
'host': hostnames,
|
||||
'host_list': hostnames.split()
|
||||
}
|
||||
|
||||
# support configuration file by ignoring all lines between
|
||||
# Match xxx and Match any
|
||||
if line.strip().startswith('Match all'):
|
||||
match_block_found = False
|
||||
continue
|
||||
|
||||
if line.strip().startswith('Match'):
|
||||
match_block_found = True
|
||||
continue
|
||||
|
||||
if match_block_found:
|
||||
continue
|
||||
|
||||
key, val = line.split(maxsplit=1)
|
||||
|
||||
# support configuration file by converting to lower case
|
||||
key = key.lower()
|
||||
|
||||
if key in multi_fields:
|
||||
if key not in host:
|
||||
host[key] = []
|
||||
host[key].append(val)
|
||||
continue
|
||||
|
||||
if key in modified_fields and val[0] in modifiers:
|
||||
host[key] = val[1:]
|
||||
host[key + '_strategy'] = val[0]
|
||||
continue
|
||||
|
||||
host[key] = val
|
||||
continue
|
||||
|
||||
if host:
|
||||
raw_output.append(host)
|
||||
|
||||
return raw_output if raw else _process(raw_output)
|
||||
@@ -1,4 +1,4 @@
|
||||
"""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
|
||||
@@ -483,13 +483,13 @@ import jc.utils
|
||||
|
||||
class info():
|
||||
"""Provides parser metadata (version, author, etc.)"""
|
||||
version = '1.0'
|
||||
description = 'sshd config file and `sshd -T` command parser'
|
||||
version = '1.1'
|
||||
description = '`sshd` config file and `sshd -T` command parser'
|
||||
author = 'Kelly Brazil'
|
||||
author_email = 'kellyjonbrazil@gmail.com'
|
||||
compatible = ['linux', 'darwin', 'freebsd']
|
||||
magic_commands = ['sshd -T']
|
||||
tags = ['file']
|
||||
tags = ['command', 'file']
|
||||
|
||||
|
||||
__version__ = info.version
|
||||
@@ -622,6 +622,10 @@ def parse(
|
||||
if jc.utils.has_data(data):
|
||||
|
||||
for line in filter(None, data.splitlines()):
|
||||
# skip any lines with only whitespace
|
||||
if not line.strip():
|
||||
continue
|
||||
|
||||
# support configuration file by skipping commented lines
|
||||
if line.strip().startswith('#'):
|
||||
continue
|
||||
|
||||
149
jc/parsers/toml.py
Normal file
149
jc/parsers/toml.py
Normal file
@@ -0,0 +1,149 @@
|
||||
"""jc - JSON Convert TOML file parser
|
||||
|
||||
Usage (cli):
|
||||
|
||||
$ cat file.toml | jc --toml
|
||||
|
||||
Usage (module):
|
||||
|
||||
import jc
|
||||
result = jc.parse('toml', toml_file_output)
|
||||
|
||||
Schema:
|
||||
|
||||
TOML Document converted to a Dictionary.
|
||||
See https://toml.io/en/ for details.
|
||||
|
||||
{
|
||||
"key1": string/int/float/boolean/null/array/object,
|
||||
"key2": string/int/float/boolean/null/array/object
|
||||
}
|
||||
|
||||
Examples:
|
||||
|
||||
$ cat file.toml
|
||||
title = "TOML Example"
|
||||
|
||||
[owner]
|
||||
name = "Tom Preston-Werner"
|
||||
dob = 1979-05-27T07:32:00-08:00
|
||||
|
||||
[database]
|
||||
enabled = true
|
||||
ports = [ 8000, 8001, 8002 ]
|
||||
|
||||
$ cat file.toml | jc --toml -p
|
||||
{
|
||||
"title": "TOML Example",
|
||||
"owner": {
|
||||
"name": "Tom Preston-Werner",
|
||||
"dob": 296667120,
|
||||
"dob_iso": "1979-05-27T07:32:00-08:00"
|
||||
},
|
||||
"database": {
|
||||
"enabled": true,
|
||||
"ports": [
|
||||
8000,
|
||||
8001,
|
||||
8002
|
||||
]
|
||||
}
|
||||
}
|
||||
"""
|
||||
from typing import Any
|
||||
from jc.jc_types import JSONDictType
|
||||
import jc.utils
|
||||
from jc.parsers import tomli
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
class info():
|
||||
"""Provides parser metadata (version, author, etc.)"""
|
||||
version = '1.0'
|
||||
description = 'TOML file parser'
|
||||
author = 'Kelly Brazil'
|
||||
author_email = 'kellyjonbrazil@gmail.com'
|
||||
details = 'Using the tomli library at https://github.com/hukkin/tomli.'
|
||||
compatible = ['linux', 'darwin', 'cygwin', 'win32', 'aix', 'freebsd']
|
||||
tags = ['standard', 'file', 'string']
|
||||
|
||||
|
||||
__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.
|
||||
"""
|
||||
return proc_data
|
||||
|
||||
|
||||
def _fix_objects(obj: Any) -> JSONDictType:
|
||||
"""
|
||||
Recursively traverse the nested dictionary or list and convert objects
|
||||
into JSON serializable types.
|
||||
"""
|
||||
if isinstance(obj, dict):
|
||||
for k, v in obj.copy().items():
|
||||
|
||||
if isinstance(v, datetime):
|
||||
iso = v.isoformat()
|
||||
v = int(round(v.timestamp()))
|
||||
obj.update({k: v, f'{k}_iso': iso})
|
||||
continue
|
||||
|
||||
if isinstance(v, dict):
|
||||
obj.update({k: _fix_objects(v)})
|
||||
continue
|
||||
|
||||
if isinstance(v, list):
|
||||
newlist = []
|
||||
for i in v:
|
||||
newlist.append(_fix_objects(i))
|
||||
obj.update({k: newlist})
|
||||
continue
|
||||
|
||||
if isinstance(obj, list):
|
||||
new_list = []
|
||||
for i in obj:
|
||||
new_list.append(_fix_objects(i))
|
||||
obj = new_list
|
||||
|
||||
return obj
|
||||
|
||||
|
||||
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: JSONDictType = {}
|
||||
|
||||
if jc.utils.has_data(data):
|
||||
raw_output = _fix_objects(tomli.loads(data))
|
||||
|
||||
return raw_output if raw else _process(raw_output)
|
||||
21
jc/parsers/tomli/LICENSE
Normal file
21
jc/parsers/tomli/LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021 Taneli Hukkinen
|
||||
|
||||
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.
|
||||
11
jc/parsers/tomli/__init__.py
Normal file
11
jc/parsers/tomli/__init__.py
Normal file
@@ -0,0 +1,11 @@
|
||||
# SPDX-License-Identifier: MIT
|
||||
# SPDX-FileCopyrightText: 2021 Taneli Hukkinen
|
||||
# Licensed to PSF under a Contributor Agreement.
|
||||
|
||||
__all__ = ("loads", "load", "TOMLDecodeError")
|
||||
__version__ = "2.0.1" # DO NOT EDIT THIS LINE MANUALLY. LET bump2version UTILITY DO IT
|
||||
|
||||
from ._parser import TOMLDecodeError, load, loads
|
||||
|
||||
# Pretend this exception was created here.
|
||||
TOMLDecodeError.__module__ = __name__
|
||||
691
jc/parsers/tomli/_parser.py
Normal file
691
jc/parsers/tomli/_parser.py
Normal file
@@ -0,0 +1,691 @@
|
||||
# SPDX-License-Identifier: MIT
|
||||
# SPDX-FileCopyrightText: 2021 Taneli Hukkinen
|
||||
# Licensed to PSF under a Contributor Agreement.
|
||||
|
||||
# from __future__ import annotations
|
||||
|
||||
from collections import namedtuple
|
||||
import string
|
||||
# from types import MappingProxyType
|
||||
# from typing import Any, BinaryIO, NamedTuple
|
||||
|
||||
from ._re import (
|
||||
RE_DATETIME,
|
||||
RE_LOCALTIME,
|
||||
RE_NUMBER,
|
||||
match_to_datetime,
|
||||
match_to_localtime,
|
||||
match_to_number,
|
||||
)
|
||||
# from ._types import Key, ParseFloat, Pos
|
||||
|
||||
ASCII_CTRL = frozenset(chr(i) for i in range(32)) | frozenset(chr(127))
|
||||
|
||||
# Neither of these sets include quotation mark or backslash. They are
|
||||
# currently handled as separate cases in the parser functions.
|
||||
ILLEGAL_BASIC_STR_CHARS = ASCII_CTRL - frozenset("\t")
|
||||
ILLEGAL_MULTILINE_BASIC_STR_CHARS = ASCII_CTRL - frozenset("\t\n")
|
||||
|
||||
ILLEGAL_LITERAL_STR_CHARS = ILLEGAL_BASIC_STR_CHARS
|
||||
ILLEGAL_MULTILINE_LITERAL_STR_CHARS = ILLEGAL_MULTILINE_BASIC_STR_CHARS
|
||||
|
||||
ILLEGAL_COMMENT_CHARS = ILLEGAL_BASIC_STR_CHARS
|
||||
|
||||
TOML_WS = frozenset(" \t")
|
||||
TOML_WS_AND_NEWLINE = TOML_WS | frozenset("\n")
|
||||
BARE_KEY_CHARS = frozenset(string.ascii_letters + string.digits + "-_")
|
||||
KEY_INITIAL_CHARS = BARE_KEY_CHARS | frozenset("\"'")
|
||||
HEXDIGIT_CHARS = frozenset(string.hexdigits)
|
||||
|
||||
BASIC_STR_ESCAPE_REPLACEMENTS = {
|
||||
"\\b": "\u0008", # backspace
|
||||
"\\t": "\u0009", # tab
|
||||
"\\n": "\u000A", # linefeed
|
||||
"\\f": "\u000C", # form feed
|
||||
"\\r": "\u000D", # carriage return
|
||||
'\\"': "\u0022", # quote
|
||||
"\\\\": "\u005C", # backslash
|
||||
}
|
||||
|
||||
|
||||
class TOMLDecodeError(ValueError):
|
||||
"""An error raised if a document is not valid TOML."""
|
||||
|
||||
|
||||
def load(__fp, *, parse_float=float):
|
||||
"""Parse TOML from a binary file object."""
|
||||
b = __fp.read()
|
||||
try:
|
||||
s = b.decode()
|
||||
except AttributeError:
|
||||
raise TypeError(
|
||||
"File must be opened in binary mode, e.g. use `open('foo.toml', 'rb')`"
|
||||
) from None
|
||||
return loads(s, parse_float=parse_float)
|
||||
|
||||
|
||||
def loads(__s, *, parse_float=float):
|
||||
"""Parse TOML from a string."""
|
||||
|
||||
# The spec allows converting "\r\n" to "\n", even in string
|
||||
# literals. Let's do so to simplify parsing.
|
||||
src = __s.replace("\r\n", "\n")
|
||||
pos = 0
|
||||
out = Output(NestedDict(), Flags())
|
||||
header = ()
|
||||
parse_float = make_safe_parse_float(parse_float)
|
||||
|
||||
# Parse one statement at a time
|
||||
# (typically means one line in TOML source)
|
||||
while True:
|
||||
# 1. Skip line leading whitespace
|
||||
pos = skip_chars(src, pos, TOML_WS)
|
||||
|
||||
# 2. Parse rules. Expect one of the following:
|
||||
# - end of file
|
||||
# - end of line
|
||||
# - comment
|
||||
# - key/value pair
|
||||
# - append dict to list (and move to its namespace)
|
||||
# - create dict (and move to its namespace)
|
||||
# Skip trailing whitespace when applicable.
|
||||
try:
|
||||
char = src[pos]
|
||||
except IndexError:
|
||||
break
|
||||
if char == "\n":
|
||||
pos += 1
|
||||
continue
|
||||
if char in KEY_INITIAL_CHARS:
|
||||
pos = key_value_rule(src, pos, out, header, parse_float)
|
||||
pos = skip_chars(src, pos, TOML_WS)
|
||||
elif char == "[":
|
||||
try:
|
||||
second_char = src[pos + 1]
|
||||
except IndexError:
|
||||
second_char = None
|
||||
out.flags.finalize_pending()
|
||||
if second_char == "[":
|
||||
pos, header = create_list_rule(src, pos, out)
|
||||
else:
|
||||
pos, header = create_dict_rule(src, pos, out)
|
||||
pos = skip_chars(src, pos, TOML_WS)
|
||||
elif char != "#":
|
||||
raise suffixed_err(src, pos, "Invalid statement")
|
||||
|
||||
# 3. Skip comment
|
||||
pos = skip_comment(src, pos)
|
||||
|
||||
# 4. Expect end of line or end of file
|
||||
try:
|
||||
char = src[pos]
|
||||
except IndexError:
|
||||
break
|
||||
if char != "\n":
|
||||
raise suffixed_err(
|
||||
src, pos, "Expected newline or end of document after a statement"
|
||||
)
|
||||
pos += 1
|
||||
|
||||
return out.data.dict
|
||||
|
||||
|
||||
class Flags:
|
||||
"""Flags that map to parsed keys/namespaces."""
|
||||
|
||||
# Marks an immutable namespace (inline array or inline table).
|
||||
FROZEN = 0
|
||||
# Marks a nest that has been explicitly created and can no longer
|
||||
# be opened using the "[table]" syntax.
|
||||
EXPLICIT_NEST = 1
|
||||
|
||||
def __init__(self):
|
||||
self._flags = {}
|
||||
self._pending_flags = set()
|
||||
|
||||
def add_pending(self, key, flag):
|
||||
self._pending_flags.add((key, flag))
|
||||
|
||||
def finalize_pending(self):
|
||||
for key, flag in self._pending_flags:
|
||||
self.set(key, flag, recursive=False)
|
||||
self._pending_flags.clear()
|
||||
|
||||
def unset_all(self, key):
|
||||
cont = self._flags
|
||||
for k in key[:-1]:
|
||||
if k not in cont:
|
||||
return
|
||||
cont = cont[k]["nested"]
|
||||
cont.pop(key[-1], None)
|
||||
|
||||
def set(self, key, flag, *, recursive):
|
||||
cont = self._flags
|
||||
key_parent, key_stem = key[:-1], key[-1]
|
||||
for k in key_parent:
|
||||
if k not in cont:
|
||||
cont[k] = {"flags": set(), "recursive_flags": set(), "nested": {}}
|
||||
cont = cont[k]["nested"]
|
||||
if key_stem not in cont:
|
||||
cont[key_stem] = {"flags": set(), "recursive_flags": set(), "nested": {}}
|
||||
cont[key_stem]["recursive_flags" if recursive else "flags"].add(flag)
|
||||
|
||||
def is_(self, key, flag):
|
||||
if not key:
|
||||
return False # document root has no flags
|
||||
cont = self._flags
|
||||
for k in key[:-1]:
|
||||
if k not in cont:
|
||||
return False
|
||||
inner_cont = cont[k]
|
||||
if flag in inner_cont["recursive_flags"]:
|
||||
return True
|
||||
cont = inner_cont["nested"]
|
||||
key_stem = key[-1]
|
||||
if key_stem in cont:
|
||||
cont = cont[key_stem]
|
||||
return flag in cont["flags"] or flag in cont["recursive_flags"]
|
||||
return False
|
||||
|
||||
|
||||
class NestedDict:
|
||||
def __init__(self):
|
||||
# The parsed content of the TOML document
|
||||
self.dict = {}
|
||||
|
||||
def get_or_create_nest(
|
||||
self,
|
||||
key,
|
||||
*,
|
||||
access_lists: bool = True,
|
||||
) -> dict:
|
||||
cont = self.dict
|
||||
for k in key:
|
||||
if k not in cont:
|
||||
cont[k] = {}
|
||||
cont = cont[k]
|
||||
if access_lists and isinstance(cont, list):
|
||||
cont = cont[-1]
|
||||
if not isinstance(cont, dict):
|
||||
raise KeyError("There is no nest behind this key")
|
||||
return cont
|
||||
|
||||
def append_nest_to_list(self, key):
|
||||
cont = self.get_or_create_nest(key[:-1])
|
||||
last_key = key[-1]
|
||||
if last_key in cont:
|
||||
list_ = cont[last_key]
|
||||
if not isinstance(list_, list):
|
||||
raise KeyError("An object other than list found behind this key")
|
||||
list_.append({})
|
||||
else:
|
||||
cont[last_key] = [{}]
|
||||
|
||||
|
||||
# class Output(namedtuple):
|
||||
# data: NestedDict
|
||||
# flags: Flags
|
||||
|
||||
Output = namedtuple('Output', ['data', 'flags'])
|
||||
|
||||
|
||||
def skip_chars(src: str, pos, chars):
|
||||
try:
|
||||
while src[pos] in chars:
|
||||
pos += 1
|
||||
except IndexError:
|
||||
pass
|
||||
return pos
|
||||
|
||||
|
||||
def skip_until(
|
||||
src,
|
||||
pos,
|
||||
expect,
|
||||
*,
|
||||
error_on,
|
||||
error_on_eof,
|
||||
):
|
||||
try:
|
||||
new_pos = src.index(expect, pos)
|
||||
except ValueError:
|
||||
new_pos = len(src)
|
||||
if error_on_eof:
|
||||
raise suffixed_err(src, new_pos, f"Expected {expect!r}") from None
|
||||
|
||||
if not error_on.isdisjoint(src[pos:new_pos]):
|
||||
while src[pos] not in error_on:
|
||||
pos += 1
|
||||
raise suffixed_err(src, pos, f"Found invalid character {src[pos]!r}")
|
||||
return new_pos
|
||||
|
||||
|
||||
def skip_comment(src, pos):
|
||||
try:
|
||||
char = src[pos]
|
||||
except IndexError:
|
||||
char = None
|
||||
if char == "#":
|
||||
return skip_until(
|
||||
src, pos + 1, "\n", error_on=ILLEGAL_COMMENT_CHARS, error_on_eof=False
|
||||
)
|
||||
return pos
|
||||
|
||||
|
||||
def skip_comments_and_array_ws(src, pos):
|
||||
while True:
|
||||
pos_before_skip = pos
|
||||
pos = skip_chars(src, pos, TOML_WS_AND_NEWLINE)
|
||||
pos = skip_comment(src, pos)
|
||||
if pos == pos_before_skip:
|
||||
return pos
|
||||
|
||||
|
||||
def create_dict_rule(src, pos, out):
|
||||
pos += 1 # Skip "["
|
||||
pos = skip_chars(src, pos, TOML_WS)
|
||||
pos, key = parse_key(src, pos)
|
||||
|
||||
if out.flags.is_(key, Flags.EXPLICIT_NEST) or out.flags.is_(key, Flags.FROZEN):
|
||||
raise suffixed_err(src, pos, f"Cannot declare {key} twice")
|
||||
out.flags.set(key, Flags.EXPLICIT_NEST, recursive=False)
|
||||
try:
|
||||
out.data.get_or_create_nest(key)
|
||||
except KeyError:
|
||||
raise suffixed_err(src, pos, "Cannot overwrite a value") from None
|
||||
|
||||
if not src.startswith("]", pos):
|
||||
raise suffixed_err(src, pos, "Expected ']' at the end of a table declaration")
|
||||
return pos + 1, key
|
||||
|
||||
|
||||
def create_list_rule(src: str, pos, out):
|
||||
pos += 2 # Skip "[["
|
||||
pos = skip_chars(src, pos, TOML_WS)
|
||||
pos, key = parse_key(src, pos)
|
||||
|
||||
if out.flags.is_(key, Flags.FROZEN):
|
||||
raise suffixed_err(src, pos, f"Cannot mutate immutable namespace {key}")
|
||||
# Free the namespace now that it points to another empty list item...
|
||||
out.flags.unset_all(key)
|
||||
# ...but this key precisely is still prohibited from table declaration
|
||||
out.flags.set(key, Flags.EXPLICIT_NEST, recursive=False)
|
||||
try:
|
||||
out.data.append_nest_to_list(key)
|
||||
except KeyError:
|
||||
raise suffixed_err(src, pos, "Cannot overwrite a value") from None
|
||||
|
||||
if not src.startswith("]]", pos):
|
||||
raise suffixed_err(src, pos, "Expected ']]' at the end of an array declaration")
|
||||
return pos + 2, key
|
||||
|
||||
|
||||
def key_value_rule(
|
||||
src, pos, out, header, parse_float
|
||||
):
|
||||
pos, key, value = parse_key_value_pair(src, pos, parse_float)
|
||||
key_parent, key_stem = key[:-1], key[-1]
|
||||
abs_key_parent = header + key_parent
|
||||
|
||||
relative_path_cont_keys = (header + key[:i] for i in range(1, len(key)))
|
||||
for cont_key in relative_path_cont_keys:
|
||||
# Check that dotted key syntax does not redefine an existing table
|
||||
if out.flags.is_(cont_key, Flags.EXPLICIT_NEST):
|
||||
raise suffixed_err(src, pos, f"Cannot redefine namespace {cont_key}")
|
||||
# Containers in the relative path can't be opened with the table syntax or
|
||||
# dotted key/value syntax in following table sections.
|
||||
out.flags.add_pending(cont_key, Flags.EXPLICIT_NEST)
|
||||
|
||||
if out.flags.is_(abs_key_parent, Flags.FROZEN):
|
||||
raise suffixed_err(
|
||||
src, pos, f"Cannot mutate immutable namespace {abs_key_parent}"
|
||||
)
|
||||
|
||||
try:
|
||||
nest = out.data.get_or_create_nest(abs_key_parent)
|
||||
except KeyError:
|
||||
raise suffixed_err(src, pos, "Cannot overwrite a value") from None
|
||||
if key_stem in nest:
|
||||
raise suffixed_err(src, pos, "Cannot overwrite a value")
|
||||
# Mark inline table and array namespaces recursively immutable
|
||||
if isinstance(value, (dict, list)):
|
||||
out.flags.set(header + key, Flags.FROZEN, recursive=True)
|
||||
nest[key_stem] = value
|
||||
return pos
|
||||
|
||||
|
||||
def parse_key_value_pair(
|
||||
src, pos, parse_float
|
||||
):
|
||||
pos, key = parse_key(src, pos)
|
||||
try:
|
||||
char = src[pos]
|
||||
except IndexError:
|
||||
char = None
|
||||
if char != "=":
|
||||
raise suffixed_err(src, pos, "Expected '=' after a key in a key/value pair")
|
||||
pos += 1
|
||||
pos = skip_chars(src, pos, TOML_WS)
|
||||
pos, value = parse_value(src, pos, parse_float)
|
||||
return pos, key, value
|
||||
|
||||
|
||||
def parse_key(src, pos):
|
||||
pos, key_part = parse_key_part(src, pos)
|
||||
key = (key_part,)
|
||||
pos = skip_chars(src, pos, TOML_WS)
|
||||
while True:
|
||||
try:
|
||||
char = src[pos]
|
||||
except IndexError:
|
||||
char = None
|
||||
if char != ".":
|
||||
return pos, key
|
||||
pos += 1
|
||||
pos = skip_chars(src, pos, TOML_WS)
|
||||
pos, key_part = parse_key_part(src, pos)
|
||||
key += (key_part,)
|
||||
pos = skip_chars(src, pos, TOML_WS)
|
||||
|
||||
|
||||
def parse_key_part(src, pos):
|
||||
try:
|
||||
char = src[pos]
|
||||
except IndexError:
|
||||
char = None
|
||||
if char in BARE_KEY_CHARS:
|
||||
start_pos = pos
|
||||
pos = skip_chars(src, pos, BARE_KEY_CHARS)
|
||||
return pos, src[start_pos:pos]
|
||||
if char == "'":
|
||||
return parse_literal_str(src, pos)
|
||||
if char == '"':
|
||||
return parse_one_line_basic_str(src, pos)
|
||||
raise suffixed_err(src, pos, "Invalid initial character for a key part")
|
||||
|
||||
|
||||
def parse_one_line_basic_str(src, pos):
|
||||
pos += 1
|
||||
return parse_basic_str(src, pos, multiline=False)
|
||||
|
||||
|
||||
def parse_array(src, pos, parse_float):
|
||||
pos += 1
|
||||
array = []
|
||||
|
||||
pos = skip_comments_and_array_ws(src, pos)
|
||||
if src.startswith("]", pos):
|
||||
return pos + 1, array
|
||||
while True:
|
||||
pos, val = parse_value(src, pos, parse_float)
|
||||
array.append(val)
|
||||
pos = skip_comments_and_array_ws(src, pos)
|
||||
|
||||
c = src[pos : pos + 1]
|
||||
if c == "]":
|
||||
return pos + 1, array
|
||||
if c != ",":
|
||||
raise suffixed_err(src, pos, "Unclosed array")
|
||||
pos += 1
|
||||
|
||||
pos = skip_comments_and_array_ws(src, pos)
|
||||
if src.startswith("]", pos):
|
||||
return pos + 1, array
|
||||
|
||||
|
||||
def parse_inline_table(src, pos, parse_float):
|
||||
pos += 1
|
||||
nested_dict = NestedDict()
|
||||
flags = Flags()
|
||||
|
||||
pos = skip_chars(src, pos, TOML_WS)
|
||||
if src.startswith("}", pos):
|
||||
return pos + 1, nested_dict.dict
|
||||
while True:
|
||||
pos, key, value = parse_key_value_pair(src, pos, parse_float)
|
||||
key_parent, key_stem = key[:-1], key[-1]
|
||||
if flags.is_(key, Flags.FROZEN):
|
||||
raise suffixed_err(src, pos, f"Cannot mutate immutable namespace {key}")
|
||||
try:
|
||||
nest = nested_dict.get_or_create_nest(key_parent, access_lists=False)
|
||||
except KeyError:
|
||||
raise suffixed_err(src, pos, "Cannot overwrite a value") from None
|
||||
if key_stem in nest:
|
||||
raise suffixed_err(src, pos, f"Duplicate inline table key {key_stem!r}")
|
||||
nest[key_stem] = value
|
||||
pos = skip_chars(src, pos, TOML_WS)
|
||||
c = src[pos : pos + 1]
|
||||
if c == "}":
|
||||
return pos + 1, nested_dict.dict
|
||||
if c != ",":
|
||||
raise suffixed_err(src, pos, "Unclosed inline table")
|
||||
if isinstance(value, (dict, list)):
|
||||
flags.set(key, Flags.FROZEN, recursive=True)
|
||||
pos += 1
|
||||
pos = skip_chars(src, pos, TOML_WS)
|
||||
|
||||
|
||||
def parse_basic_str_escape(
|
||||
src, pos, *, multiline = False
|
||||
):
|
||||
escape_id = src[pos : pos + 2]
|
||||
pos += 2
|
||||
if multiline and escape_id in {"\\ ", "\\\t", "\\\n"}:
|
||||
# Skip whitespace until next non-whitespace character or end of
|
||||
# the doc. Error if non-whitespace is found before newline.
|
||||
if escape_id != "\\\n":
|
||||
pos = skip_chars(src, pos, TOML_WS)
|
||||
try:
|
||||
char = src[pos]
|
||||
except IndexError:
|
||||
return pos, ""
|
||||
if char != "\n":
|
||||
raise suffixed_err(src, pos, "Unescaped '\\' in a string")
|
||||
pos += 1
|
||||
pos = skip_chars(src, pos, TOML_WS_AND_NEWLINE)
|
||||
return pos, ""
|
||||
if escape_id == "\\u":
|
||||
return parse_hex_char(src, pos, 4)
|
||||
if escape_id == "\\U":
|
||||
return parse_hex_char(src, pos, 8)
|
||||
try:
|
||||
return pos, BASIC_STR_ESCAPE_REPLACEMENTS[escape_id]
|
||||
except KeyError:
|
||||
raise suffixed_err(src, pos, "Unescaped '\\' in a string") from None
|
||||
|
||||
|
||||
def parse_basic_str_escape_multiline(src, pos):
|
||||
return parse_basic_str_escape(src, pos, multiline=True)
|
||||
|
||||
|
||||
def parse_hex_char(src, pos, hex_len):
|
||||
hex_str = src[pos : pos + hex_len]
|
||||
if len(hex_str) != hex_len or not HEXDIGIT_CHARS.issuperset(hex_str):
|
||||
raise suffixed_err(src, pos, "Invalid hex value")
|
||||
pos += hex_len
|
||||
hex_int = int(hex_str, 16)
|
||||
if not is_unicode_scalar_value(hex_int):
|
||||
raise suffixed_err(src, pos, "Escaped character is not a Unicode scalar value")
|
||||
return pos, chr(hex_int)
|
||||
|
||||
|
||||
def parse_literal_str(src, pos):
|
||||
pos += 1 # Skip starting apostrophe
|
||||
start_pos = pos
|
||||
pos = skip_until(
|
||||
src, pos, "'", error_on=ILLEGAL_LITERAL_STR_CHARS, error_on_eof=True
|
||||
)
|
||||
return pos + 1, src[start_pos:pos] # Skip ending apostrophe
|
||||
|
||||
|
||||
def parse_multiline_str(src, pos, *, literal):
|
||||
pos += 3
|
||||
if src.startswith("\n", pos):
|
||||
pos += 1
|
||||
|
||||
if literal:
|
||||
delim = "'"
|
||||
end_pos = skip_until(
|
||||
src,
|
||||
pos,
|
||||
"'''",
|
||||
error_on=ILLEGAL_MULTILINE_LITERAL_STR_CHARS,
|
||||
error_on_eof=True,
|
||||
)
|
||||
result = src[pos:end_pos]
|
||||
pos = end_pos + 3
|
||||
else:
|
||||
delim = '"'
|
||||
pos, result = parse_basic_str(src, pos, multiline=True)
|
||||
|
||||
# Add at maximum two extra apostrophes/quotes if the end sequence
|
||||
# is 4 or 5 chars long instead of just 3.
|
||||
if not src.startswith(delim, pos):
|
||||
return pos, result
|
||||
pos += 1
|
||||
if not src.startswith(delim, pos):
|
||||
return pos, result + delim
|
||||
pos += 1
|
||||
return pos, result + (delim * 2)
|
||||
|
||||
|
||||
def parse_basic_str(src, pos, *, multiline):
|
||||
if multiline:
|
||||
error_on = ILLEGAL_MULTILINE_BASIC_STR_CHARS
|
||||
parse_escapes = parse_basic_str_escape_multiline
|
||||
else:
|
||||
error_on = ILLEGAL_BASIC_STR_CHARS
|
||||
parse_escapes = parse_basic_str_escape
|
||||
result = ""
|
||||
start_pos = pos
|
||||
while True:
|
||||
try:
|
||||
char = src[pos]
|
||||
except IndexError:
|
||||
raise suffixed_err(src, pos, "Unterminated string") from None
|
||||
if char == '"':
|
||||
if not multiline:
|
||||
return pos + 1, result + src[start_pos:pos]
|
||||
if src.startswith('"""', pos):
|
||||
return pos + 3, result + src[start_pos:pos]
|
||||
pos += 1
|
||||
continue
|
||||
if char == "\\":
|
||||
result += src[start_pos:pos]
|
||||
pos, parsed_escape = parse_escapes(src, pos)
|
||||
result += parsed_escape
|
||||
start_pos = pos
|
||||
continue
|
||||
if char in error_on:
|
||||
raise suffixed_err(src, pos, f"Illegal character {char!r}")
|
||||
pos += 1
|
||||
|
||||
|
||||
def parse_value( # noqa: C901
|
||||
src, pos, parse_float
|
||||
):
|
||||
try:
|
||||
char = src[pos]
|
||||
except IndexError:
|
||||
char = None
|
||||
|
||||
# IMPORTANT: order conditions based on speed of checking and likelihood
|
||||
|
||||
# Basic strings
|
||||
if char == '"':
|
||||
if src.startswith('"""', pos):
|
||||
return parse_multiline_str(src, pos, literal=False)
|
||||
return parse_one_line_basic_str(src, pos)
|
||||
|
||||
# Literal strings
|
||||
if char == "'":
|
||||
if src.startswith("'''", pos):
|
||||
return parse_multiline_str(src, pos, literal=True)
|
||||
return parse_literal_str(src, pos)
|
||||
|
||||
# Booleans
|
||||
if char == "t":
|
||||
if src.startswith("true", pos):
|
||||
return pos + 4, True
|
||||
if char == "f":
|
||||
if src.startswith("false", pos):
|
||||
return pos + 5, False
|
||||
|
||||
# Arrays
|
||||
if char == "[":
|
||||
return parse_array(src, pos, parse_float)
|
||||
|
||||
# Inline tables
|
||||
if char == "{":
|
||||
return parse_inline_table(src, pos, parse_float)
|
||||
|
||||
# Dates and times
|
||||
datetime_match = RE_DATETIME.match(src, pos)
|
||||
if datetime_match:
|
||||
try:
|
||||
datetime_obj = match_to_datetime(datetime_match)
|
||||
except ValueError as e:
|
||||
raise suffixed_err(src, pos, "Invalid date or datetime") from e
|
||||
return datetime_match.end(), datetime_obj
|
||||
localtime_match = RE_LOCALTIME.match(src, pos)
|
||||
if localtime_match:
|
||||
return localtime_match.end(), match_to_localtime(localtime_match)
|
||||
|
||||
# Integers and "normal" floats.
|
||||
# The regex will greedily match any type starting with a decimal
|
||||
# char, so needs to be located after handling of dates and times.
|
||||
number_match = RE_NUMBER.match(src, pos)
|
||||
if number_match:
|
||||
return number_match.end(), match_to_number(number_match, parse_float)
|
||||
|
||||
# Special floats
|
||||
first_three = src[pos : pos + 3]
|
||||
if first_three in {"inf", "nan"}:
|
||||
return pos + 3, parse_float(first_three)
|
||||
first_four = src[pos : pos + 4]
|
||||
if first_four in {"-inf", "+inf", "-nan", "+nan"}:
|
||||
return pos + 4, parse_float(first_four)
|
||||
|
||||
raise suffixed_err(src, pos, "Invalid value")
|
||||
|
||||
|
||||
def suffixed_err(src, pos, msg):
|
||||
"""Return a `TOMLDecodeError` where error message is suffixed with
|
||||
coordinates in source."""
|
||||
|
||||
def coord_repr(src, pos):
|
||||
if pos >= len(src):
|
||||
return "end of document"
|
||||
line = src.count("\n", 0, pos) + 1
|
||||
if line == 1:
|
||||
column = pos + 1
|
||||
else:
|
||||
column = pos - src.rindex("\n", 0, pos)
|
||||
return f"line {line}, column {column}"
|
||||
|
||||
return TOMLDecodeError(f"{msg} (at {coord_repr(src, pos)})")
|
||||
|
||||
|
||||
def is_unicode_scalar_value(codepoint: int) -> bool:
|
||||
return (0 <= codepoint <= 55295) or (57344 <= codepoint <= 1114111)
|
||||
|
||||
|
||||
def make_safe_parse_float(parse_float):
|
||||
"""A decorator to make `parse_float` safe.
|
||||
|
||||
`parse_float` must not return dicts or lists, because these types
|
||||
would be mixed with parsed TOML tables and arrays, thus confusing
|
||||
the parser. The returned decorated callable raises `ValueError`
|
||||
instead of returning illegal types.
|
||||
"""
|
||||
# The default `float` callable never returns illegal types. Optimize it.
|
||||
if parse_float is float:
|
||||
return float
|
||||
|
||||
def safe_parse_float(float_str):
|
||||
float_value = parse_float(float_str)
|
||||
if isinstance(float_value, (dict, list)):
|
||||
raise ValueError("parse_float must not return dicts or lists")
|
||||
return float_value
|
||||
|
||||
return safe_parse_float
|
||||
101
jc/parsers/tomli/_re.py
Normal file
101
jc/parsers/tomli/_re.py
Normal file
@@ -0,0 +1,101 @@
|
||||
# SPDX-License-Identifier: MIT
|
||||
# SPDX-FileCopyrightText: 2021 Taneli Hukkinen
|
||||
# Licensed to PSF under a Contributor Agreement.
|
||||
from datetime import date, datetime, time, timedelta, timezone, tzinfo
|
||||
from functools import lru_cache
|
||||
import re
|
||||
|
||||
# E.g.
|
||||
# - 00:32:00.999999
|
||||
# - 00:32:00
|
||||
_TIME_RE_STR = r"([01][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9])(?:\.([0-9]{1,6})[0-9]*)?"
|
||||
|
||||
RE_NUMBER = re.compile(
|
||||
r"""
|
||||
0
|
||||
(?:
|
||||
x[0-9A-Fa-f](?:_?[0-9A-Fa-f])* # hex
|
||||
|
|
||||
b[01](?:_?[01])* # bin
|
||||
|
|
||||
o[0-7](?:_?[0-7])* # oct
|
||||
)
|
||||
|
|
||||
[+-]?(?:0|[1-9](?:_?[0-9])*) # dec, integer part
|
||||
(?P<floatpart>
|
||||
(?:\.[0-9](?:_?[0-9])*)? # optional fractional part
|
||||
(?:[eE][+-]?[0-9](?:_?[0-9])*)? # optional exponent part
|
||||
)
|
||||
""",
|
||||
flags=re.VERBOSE,
|
||||
)
|
||||
RE_LOCALTIME = re.compile(_TIME_RE_STR)
|
||||
RE_DATETIME = re.compile(
|
||||
rf"""
|
||||
([0-9]{{4}})-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01]) # date, e.g. 1988-10-27
|
||||
(?:
|
||||
[Tt ]
|
||||
{_TIME_RE_STR}
|
||||
(?:([Zz])|([+-])([01][0-9]|2[0-3]):([0-5][0-9]))? # optional time offset
|
||||
)?
|
||||
""",
|
||||
flags=re.VERBOSE,
|
||||
)
|
||||
|
||||
|
||||
def match_to_datetime(match):
|
||||
"""Convert a `RE_DATETIME` match to `datetime.datetime` or `datetime.date`.
|
||||
|
||||
Raises ValueError if the match does not correspond to a valid date
|
||||
or datetime.
|
||||
"""
|
||||
(
|
||||
year_str,
|
||||
month_str,
|
||||
day_str,
|
||||
hour_str,
|
||||
minute_str,
|
||||
sec_str,
|
||||
micros_str,
|
||||
zulu_time,
|
||||
offset_sign_str,
|
||||
offset_hour_str,
|
||||
offset_minute_str,
|
||||
) = match.groups()
|
||||
year, month, day = int(year_str), int(month_str), int(day_str)
|
||||
if hour_str is None:
|
||||
return date(year, month, day)
|
||||
hour, minute, sec = int(hour_str), int(minute_str), int(sec_str)
|
||||
micros = int(micros_str.ljust(6, "0")) if micros_str else 0
|
||||
if offset_sign_str:
|
||||
tz = cached_tz(
|
||||
offset_hour_str, offset_minute_str, offset_sign_str
|
||||
)
|
||||
elif zulu_time:
|
||||
tz = timezone.utc
|
||||
else: # local date-time
|
||||
tz = None
|
||||
return datetime(year, month, day, hour, minute, sec, micros, tzinfo=tz)
|
||||
|
||||
|
||||
@lru_cache(maxsize=None)
|
||||
def cached_tz(hour_str, minute_str, sign_str):
|
||||
sign = 1 if sign_str == "+" else -1
|
||||
return timezone(
|
||||
timedelta(
|
||||
hours=sign * int(hour_str),
|
||||
minutes=sign * int(minute_str),
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def match_to_localtime(match):
|
||||
hour_str, minute_str, sec_str, micros_str = match.groups()
|
||||
micros = int(micros_str.ljust(6, "0")) if micros_str else 0
|
||||
return time(int(hour_str), int(minute_str), int(sec_str), micros)
|
||||
|
||||
|
||||
def match_to_number(match, parse_float):
|
||||
if match.group("floatpart"):
|
||||
return parse_float(match.group())
|
||||
return int(match.group(), 0)
|
||||
8
jc/parsers/tomli/_types.py
Normal file
8
jc/parsers/tomli/_types.py
Normal file
@@ -0,0 +1,8 @@
|
||||
# SPDX-License-Identifier: MIT
|
||||
# SPDX-FileCopyrightText: 2021 Taneli Hukkinen
|
||||
# Licensed to PSF under a Contributor Agreement.
|
||||
|
||||
# Type annotations
|
||||
# ParseFloat = Callable[[str], Any]
|
||||
# Key = Tuple[str, ...]
|
||||
# Pos = int
|
||||
1
jc/parsers/tomli/py.typed
Normal file
1
jc/parsers/tomli/py.typed
Normal file
@@ -0,0 +1 @@
|
||||
# Marker file for PEP 561
|
||||
@@ -28,8 +28,7 @@ def simple_table_parse(data: Iterable[str]) -> List[Dict]:
|
||||
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:
|
||||
|
||||
|
||||
209
jc/parsers/ver.py
Normal file
209
jc/parsers/ver.py
Normal file
@@ -0,0 +1,209 @@
|
||||
"""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
|
||||
libary.
|
||||
|
||||
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
|
||||
}
|
||||
"""
|
||||
import re
|
||||
from typing import Dict
|
||||
from jc.jc_types import JSONDictType
|
||||
import jc.utils
|
||||
|
||||
|
||||
class info():
|
||||
"""Provides parser metadata (version, author, etc.)"""
|
||||
version = '1.0'
|
||||
description = 'Version string parser'
|
||||
author = 'Kelly Brazil'
|
||||
author_email = 'kellyjonbrazil@gmail.com'
|
||||
details = 'Based on distutils/version.py from CPython 3.9.5.'
|
||||
compatible = ['linux', 'darwin', 'cygwin', 'win32', 'aix', 'freebsd']
|
||||
tags = ['generic', 'string']
|
||||
|
||||
|
||||
__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:
|
||||
|
||||
List of Dictionaries. Structured to conform to the schema.
|
||||
"""
|
||||
int_list = {'major', 'minor', 'patch', 'prerelease', 'prerelease_num'}
|
||||
|
||||
for k, v in proc_data.items():
|
||||
if k in int_list:
|
||||
try:
|
||||
proc_data[k] = int(v)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
if 'components' in proc_data:
|
||||
for i, obj in enumerate(proc_data['components']):
|
||||
try:
|
||||
proc_data['components'][i] = int(obj)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
return proc_data
|
||||
|
||||
|
||||
def strict_parse(vstring):
|
||||
version_re = re.compile(r'^(\d+) \. (\d+) (\. (\d+))? ([ab](\d+))?$', re.VERBOSE)
|
||||
match = version_re.match(vstring)
|
||||
if not match:
|
||||
raise ValueError("invalid version number '%s'" % vstring)
|
||||
|
||||
(major, minor, patch, prerelease, prerelease_num) = \
|
||||
match.group(1, 2, 4, 5, 6)
|
||||
|
||||
if not patch:
|
||||
patch = '0'
|
||||
|
||||
if prerelease:
|
||||
prerelease = prerelease[0]
|
||||
else:
|
||||
prerelease = None
|
||||
|
||||
return {
|
||||
'major': major,
|
||||
'minor': minor,
|
||||
'patch': patch,
|
||||
'prerelease': prerelease,
|
||||
'prerelease_num': prerelease_num
|
||||
}
|
||||
|
||||
|
||||
def loose_parse(vstring):
|
||||
component_re = re.compile(r'(\d+ | [a-z]+ | \.)', re.VERBOSE)
|
||||
components = [x for x in component_re.split(vstring) if x and x != '.']
|
||||
|
||||
return components
|
||||
|
||||
|
||||
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.
|
||||
"""
|
||||
jc.utils.compatibility(__name__, info.compatible, quiet)
|
||||
jc.utils.input_type_check(data)
|
||||
|
||||
raw_output: Dict = {}
|
||||
strict = True
|
||||
|
||||
if jc.utils.has_data(data):
|
||||
|
||||
# based on distutils/version.py from CPython 3.9.5
|
||||
# PSF License (see https://opensource.org/licenses/Python-2.0)
|
||||
|
||||
data = data.strip()
|
||||
|
||||
try:
|
||||
raw_output = strict_parse(data)
|
||||
|
||||
except ValueError:
|
||||
raw_output['components'] = loose_parse(data)
|
||||
strict = False
|
||||
|
||||
if raw_output:
|
||||
raw_output['strict'] = strict
|
||||
|
||||
return raw_output if raw else _process(raw_output)
|
||||
@@ -3,6 +3,7 @@
|
||||
Usage (cli):
|
||||
|
||||
$ xrandr | jc --xrandr
|
||||
$ xrandr --properties | jc --xrandr
|
||||
|
||||
or
|
||||
|
||||
@@ -44,13 +45,17 @@ 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": [
|
||||
@@ -127,7 +132,71 @@ 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,
|
||||
"associated_device": {
|
||||
"associated_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"
|
||||
}
|
||||
}
|
||||
],
|
||||
@@ -137,14 +206,17 @@ Examples:
|
||||
import re
|
||||
from typing import Dict, List, Optional, Union
|
||||
import jc.utils
|
||||
from jc.parsers.pyedid.edid import Edid
|
||||
from jc.parsers.pyedid.helpers.edid_helper import EdidHelper
|
||||
|
||||
|
||||
class info:
|
||||
"""Provides parser metadata (version, author, etc.)"""
|
||||
version = "1.1"
|
||||
version = "1.2"
|
||||
description = "`xrandr` command parser"
|
||||
author = "Kevin Lyter"
|
||||
author_email = "lyter_git at sent.com"
|
||||
details = 'Using parts of the pyedid library at https://github.com/jojonas/pyedid.'
|
||||
compatible = ["linux", "darwin", "cygwin", "aix", "freebsd"]
|
||||
magic_commands = ["xrandr"]
|
||||
tags = ['command']
|
||||
@@ -172,10 +244,21 @@ try:
|
||||
"frequencies": List[Frequency],
|
||||
},
|
||||
)
|
||||
Model = TypedDict(
|
||||
"Model",
|
||||
{
|
||||
"name": str,
|
||||
"product_id": str,
|
||||
"serial_number": str,
|
||||
},
|
||||
)
|
||||
Device = TypedDict(
|
||||
"Device",
|
||||
{
|
||||
"device_name": str,
|
||||
"model_name": str,
|
||||
"product_id": str,
|
||||
"serial_number": str,
|
||||
"is_connected": bool,
|
||||
"is_primary": bool,
|
||||
"resolution_width": int,
|
||||
@@ -185,6 +268,8 @@ try:
|
||||
"dimension_width": int,
|
||||
"dimension_height": int,
|
||||
"associated_modes": List[Mode],
|
||||
"rotation": str,
|
||||
"reflection": str,
|
||||
},
|
||||
)
|
||||
Screen = TypedDict(
|
||||
@@ -212,6 +297,7 @@ except ImportError:
|
||||
Device = Dict[str, Union[str, int, bool]]
|
||||
Frequency = Dict[str, Union[float, bool]]
|
||||
Mode = Dict[str, Union[int, bool, List[Frequency]]]
|
||||
Model = Dict[str, str]
|
||||
Response = Dict[str, Union[Device, Mode, Screen]]
|
||||
|
||||
|
||||
@@ -252,7 +338,8 @@ _device_pattern = (
|
||||
+ r"(?P<is_primary> primary)? ?"
|
||||
+ r"((?P<resolution_width>\d+)x(?P<resolution_height>\d+)"
|
||||
+ r"\+(?P<offset_width>\d+)\+(?P<offset_height>\d+))? "
|
||||
+ r"(?P<rotation>(inverted|left|right))? ?"
|
||||
+ r"(?P<rotation>(normal|right|left|inverted)?) ?"
|
||||
+ r"(?P<reflection>(X axis|Y axis|X and Y axis)?) ?"
|
||||
+ r"\(normal left inverted right x axis y axis\)"
|
||||
+ r"( ((?P<dimension_width>\d+)mm x (?P<dimension_height>\d+)mm)?)?"
|
||||
)
|
||||
@@ -277,9 +364,10 @@ def _parse_device(next_lines: List[str], quiet: bool = False) -> Optional[Device
|
||||
and len(matches["is_primary"]) > 0,
|
||||
"device_name": matches["device_name"],
|
||||
"rotation": matches["rotation"] or "normal",
|
||||
"reflection": matches["reflection"] or "normal",
|
||||
}
|
||||
for k, v in matches.items():
|
||||
if k not in {"is_connected", "is_primary", "device_name", "rotation"}:
|
||||
if k not in {"is_connected", "is_primary", "device_name", "rotation", "reflection"}:
|
||||
try:
|
||||
if v:
|
||||
device[k] = int(v)
|
||||
@@ -288,15 +376,67 @@ def _parse_device(next_lines: List[str], quiet: bool = False) -> Optional[Device
|
||||
[f"{next_line} : {k} - {v} is not int-able"]
|
||||
)
|
||||
|
||||
model: Optional[Model] = _parse_model(next_lines, quiet)
|
||||
if model:
|
||||
device["model_name"] = model["name"]
|
||||
device["product_id"] = model["product_id"]
|
||||
device["serial_number"] = model["serial_number"]
|
||||
|
||||
while next_lines:
|
||||
next_line = next_lines.pop()
|
||||
next_mode: Optional[Mode] = _parse_mode(next_line)
|
||||
if next_mode:
|
||||
device["associated_modes"].append(next_mode)
|
||||
else:
|
||||
if re.match(_device_pattern, next_line):
|
||||
next_lines.append(next_line)
|
||||
break
|
||||
return device
|
||||
|
||||
|
||||
# EDID:
|
||||
# 00ffffffffffff004ca3523100000000
|
||||
# 0014010380221378eac8959e57549226
|
||||
# 0f505400000001010101010101010101
|
||||
# 010101010101381d56d4500016303020
|
||||
# 250058c2100000190000000f00000000
|
||||
# 000000000025d9066a00000000fe0053
|
||||
# 414d53554e470a204ca34154000000fe
|
||||
# 004c544e313536415432343430310018
|
||||
_edid_head_pattern = r"\s*EDID:\s*"
|
||||
_edid_line_pattern = r"\s*(?P<edid_line>[0-9a-fA-F]{32})\s*"
|
||||
|
||||
|
||||
def _parse_model(next_lines: List[str], quiet: bool = False) -> Optional[Model]:
|
||||
if not next_lines:
|
||||
return None
|
||||
|
||||
next_line = next_lines.pop()
|
||||
if not re.match(_edid_head_pattern, next_line):
|
||||
next_lines.append(next_line)
|
||||
return None
|
||||
|
||||
edid_hex_value = ""
|
||||
|
||||
while next_lines:
|
||||
next_line = next_lines.pop()
|
||||
result = re.match(_edid_line_pattern, next_line)
|
||||
|
||||
if not result:
|
||||
next_lines.append(next_line)
|
||||
break
|
||||
return device
|
||||
|
||||
matches = result.groupdict()
|
||||
edid_hex_value += matches["edid_line"]
|
||||
|
||||
edid = Edid(EdidHelper.hex2bytes(edid_hex_value))
|
||||
|
||||
model: Model = {
|
||||
"name": edid.name or "Generic",
|
||||
"product_id": str(edid.product),
|
||||
"serial_number": str(edid.serial),
|
||||
}
|
||||
return model
|
||||
|
||||
|
||||
# 1920x1080i 60.03*+ 59.93
|
||||
@@ -330,8 +470,8 @@ def _parse_mode(line: str) -> Optional[Mode]:
|
||||
for match in result:
|
||||
d = match.groupdict()
|
||||
frequency = float(d["frequency"])
|
||||
is_current = len(d["star"]) > 0
|
||||
is_preferred = len(d["plus"]) > 0
|
||||
is_current = len(d["star"].strip()) > 0
|
||||
is_preferred = len(d["plus"].strip()) > 0
|
||||
f: Frequency = {
|
||||
"frequency": frequency,
|
||||
"is_current": is_current,
|
||||
|
||||
@@ -13,8 +13,8 @@ Usage (module):
|
||||
|
||||
Schema:
|
||||
|
||||
YAML Document converted to a Dictionary
|
||||
See https://pypi.org/project/ruamel.yaml for details
|
||||
YAML Document converted to a Dictionary.
|
||||
See https://pypi.org/project/ruamel.yaml for details.
|
||||
|
||||
[
|
||||
{
|
||||
@@ -25,7 +25,7 @@ Schema:
|
||||
|
||||
Examples:
|
||||
|
||||
$ cat istio-mtls-permissive.yaml
|
||||
$ cat file.yaml
|
||||
apiVersion: "authentication.istio.io/v1alpha1"
|
||||
kind: "Policy"
|
||||
metadata:
|
||||
@@ -46,7 +46,7 @@ Examples:
|
||||
tls:
|
||||
mode: ISTIO_MUTUAL
|
||||
|
||||
$ cat istio-mtls-permissive.yaml | jc --yaml -p
|
||||
$ cat file.yaml | jc --yaml -p
|
||||
[
|
||||
{
|
||||
"apiVersion": "authentication.istio.io/v1alpha1",
|
||||
|
||||
@@ -77,7 +77,7 @@ import jc.parsers.universal
|
||||
|
||||
class info():
|
||||
"""Provides parser metadata (version, author, etc.)"""
|
||||
version = '1.1'
|
||||
version = '1.2'
|
||||
description = '`zipinfo` command parser'
|
||||
author = 'Matt J'
|
||||
author_email = 'https://github.com/listuser'
|
||||
@@ -170,7 +170,8 @@ def parse(data, raw=False, quiet=False):
|
||||
# 1st line
|
||||
# Archive: log4j-core-2.16.0.jar
|
||||
line = archive_item.pop(0)
|
||||
_, archive = line.split()
|
||||
# remove prefix but don't split on spaces for files/paths with spaces
|
||||
archive = line.replace('Archive: ', '', 1)
|
||||
|
||||
# 2nd line
|
||||
# Zip file size: 1789565 bytes, number of entries: 1218
|
||||
|
||||
202
jc/parsers/zpool_iostat.py
Normal file
202
jc/parsers/zpool_iostat.py
Normal file
@@ -0,0 +1,202 @@
|
||||
"""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"
|
||||
},
|
||||
...
|
||||
]
|
||||
"""
|
||||
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 = '`zpool iostat` command parser'
|
||||
author = 'Kelly Brazil'
|
||||
author_email = 'kellyjonbrazil@gmail.com'
|
||||
compatible = ['linux', 'darwin', 'freebsd']
|
||||
tags = ['command']
|
||||
magic_commands = ['zpool iostat']
|
||||
|
||||
|
||||
__version__ = info.version
|
||||
|
||||
|
||||
def _process(proc_data: List[JSONDictType]) -> List[JSONDictType]:
|
||||
"""
|
||||
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.
|
||||
"""
|
||||
unit_values = {'cap_alloc', 'cap_free', 'bw_read', 'bw_write'}
|
||||
int_list = {'ops_read', 'ops_write'}
|
||||
|
||||
for obj in proc_data:
|
||||
for k, v in obj.copy().items():
|
||||
if k in unit_values:
|
||||
obj[k + '_unit'] = v[-1]
|
||||
obj[k] = jc.utils.convert_to_float(v[:-1])
|
||||
|
||||
if k in int_list:
|
||||
obj[k] = jc.utils.convert_to_int(v)
|
||||
|
||||
return proc_data
|
||||
|
||||
|
||||
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)
|
||||
|
||||
raw_output: List[Dict] = []
|
||||
output_line: Dict = {}
|
||||
pool_parent = ''
|
||||
|
||||
if jc.utils.has_data(data):
|
||||
|
||||
for line in filter(None, data.splitlines()):
|
||||
|
||||
# skip non-data lines
|
||||
if '---' in line or \
|
||||
line.strip().endswith('bandwidth') or \
|
||||
line.strip().endswith('write'):
|
||||
continue
|
||||
|
||||
# data lines
|
||||
line_list = line.strip().split()
|
||||
if line.startswith(' '):
|
||||
output_line = {
|
||||
"pool": line_list[0],
|
||||
"parent": pool_parent
|
||||
}
|
||||
|
||||
else:
|
||||
pool_parent = line_list[0]
|
||||
output_line = {
|
||||
"pool": pool_parent
|
||||
}
|
||||
|
||||
output_line.update(
|
||||
{
|
||||
'cap_alloc': line_list[1],
|
||||
'cap_free': line_list[2],
|
||||
'ops_read': line_list[3],
|
||||
'ops_write': line_list[4],
|
||||
'bw_read': line_list[5],
|
||||
'bw_write': line_list[6]
|
||||
}
|
||||
)
|
||||
raw_output.append(output_line)
|
||||
|
||||
return raw_output if raw else _process(raw_output)
|
||||
254
jc/parsers/zpool_status.py
Normal file
254
jc/parsers/zpool_status.py
Normal file
@@ -0,0 +1,254 @@
|
||||
"""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"
|
||||
}
|
||||
]
|
||||
"""
|
||||
from typing import List, Dict
|
||||
from jc.jc_types import JSONDictType
|
||||
import jc.utils
|
||||
from jc.parsers.kv import parse as kv_parse
|
||||
|
||||
|
||||
class info():
|
||||
"""Provides parser metadata (version, author, etc.)"""
|
||||
version = '1.0'
|
||||
description = '`zpool status` command parser'
|
||||
author = 'Kelly Brazil'
|
||||
author_email = 'kellyjonbrazil@gmail.com'
|
||||
compatible = ['linux', 'darwin', 'freebsd']
|
||||
tags = ['command']
|
||||
magic_commands = ['zpool status']
|
||||
|
||||
|
||||
__version__ = info.version
|
||||
|
||||
|
||||
def _process(proc_data: List[JSONDictType]) -> List[JSONDictType]:
|
||||
"""
|
||||
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 = {'read', 'write', 'checksum'}
|
||||
|
||||
for obj in proc_data:
|
||||
if 'config' in obj:
|
||||
for conf in obj['config']:
|
||||
for k, v in conf.items():
|
||||
if k in int_list:
|
||||
conf[k] = jc.utils.convert_to_int(v)
|
||||
|
||||
return proc_data
|
||||
|
||||
|
||||
def _build_config_list(string: str) -> List[Dict]:
|
||||
config_list: List = []
|
||||
for line in filter(None, string.splitlines()):
|
||||
if line.strip().endswith('READ WRITE CKSUM'):
|
||||
continue
|
||||
|
||||
line_list = line.strip().split(maxsplit=5)
|
||||
config_obj: Dict = {}
|
||||
config_obj['name'] = line_list[0]
|
||||
config_obj['state'] = line_list[1]
|
||||
config_obj['read'] = line_list[2]
|
||||
config_obj['write'] = line_list[3]
|
||||
config_obj['checksum'] = line_list[4]
|
||||
if len(line_list) == 6:
|
||||
config_obj['errors'] = line_list[5]
|
||||
config_list.append(config_obj)
|
||||
|
||||
return config_list
|
||||
|
||||
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)
|
||||
|
||||
raw_output: List[Dict] = []
|
||||
pool_str: str = ''
|
||||
pool_obj: Dict = {}
|
||||
|
||||
if jc.utils.has_data(data):
|
||||
|
||||
for line in filter(None, data.splitlines()):
|
||||
|
||||
if line.lstrip().startswith('pool: '):
|
||||
if pool_str:
|
||||
pool_obj = kv_parse(pool_str)
|
||||
if 'config' in pool_obj:
|
||||
pool_obj['config'] = _build_config_list(pool_obj['config'])
|
||||
raw_output.append(pool_obj)
|
||||
pool_str = ''
|
||||
pool_str += line + '\n'
|
||||
continue
|
||||
|
||||
# preserve indentation in continuation lines
|
||||
if line.startswith(' '):
|
||||
pool_str += line + '\n'
|
||||
continue
|
||||
|
||||
# indent path lines for errors field
|
||||
if line.startswith('/'):
|
||||
pool_str += ' ' + line + '\n'
|
||||
continue
|
||||
|
||||
# remove initial spaces from field start lines so we don't confuse line continuation
|
||||
pool_str += line.strip() + '\n'
|
||||
|
||||
if pool_str:
|
||||
pool_obj = kv_parse(pool_str)
|
||||
if 'config' in pool_obj:
|
||||
pool_obj['config'] = _build_config_list(pool_obj['config'])
|
||||
raw_output.append(pool_obj)
|
||||
|
||||
return raw_output if raw else _process(raw_output)
|
||||
244
man/jc.1
244
man/jc.1
@@ -1,28 +1,35 @@
|
||||
.TH jc 1 2022-12-30 1.22.4 "JSON Convert"
|
||||
.TH jc 1 2023-02-27 1.23.0 "JSON Convert"
|
||||
.SH NAME
|
||||
\fBjc\fP \- JSON Convert JSONifies the output of many CLI tools, file-types, and strings
|
||||
\fBjc\fP \- JSON Convert JSONifies the output of many CLI tools, file-types,
|
||||
and strings
|
||||
.SH SYNOPSIS
|
||||
|
||||
Standard syntax:
|
||||
|
||||
.RS
|
||||
COMMAND | \fBjc\fP [OPTIONS] PARSER
|
||||
COMMAND | \fBjc\fP [SLICE] [OPTIONS] PARSER
|
||||
|
||||
cat FILE | \fBjc\fP [OPTIONS] PARSER
|
||||
cat FILE | \fBjc\fP [SLICE] [OPTIONS] PARSER
|
||||
|
||||
echo STRING | \fBjc\fP [OPTIONS] PARSER
|
||||
echo STRING | \fBjc\fP [SLICE] [OPTIONS] PARSER
|
||||
.RE
|
||||
|
||||
Magic syntax:
|
||||
|
||||
.RS
|
||||
\fBjc\fP [OPTIONS] COMMAND
|
||||
\fBjc\fP [SLICE] [OPTIONS] COMMAND
|
||||
|
||||
\fBjc\fP [OPTIONS] /proc/<path-to-procfile>
|
||||
\fBjc\fP [SLICE] [OPTIONS] /proc/<path-to-procfile>
|
||||
.RE
|
||||
|
||||
.SH DESCRIPTION
|
||||
\fBjc\fP JSONifies the output of many CLI tools, file-types, and common strings for easier parsing in scripts. \fBjc\fP accepts piped input from \fBSTDIN\fP and outputs a JSON representation of the previous command's output to \fBSTDOUT\fP. Alternatively, the "Magic" syntax can be used by prepending \fBjc\fP to the command to be converted. Options can be passed to \fBjc\fP immediately before the command is given. (Note: "Magic" syntax does not support shell builtins or command aliases)
|
||||
\fBjc\fP JSONifies the output of many CLI tools, file-types, and common strings
|
||||
for easier parsing in scripts. \fBjc\fP accepts piped input from \fBSTDIN\fP and
|
||||
outputs a JSON representation of the previous command's output to \fBSTDOUT\fP.
|
||||
Alternatively, the "Magic" syntax can be used by prepending \fBjc\fP to the
|
||||
command to be converted. Options can be passed to \fBjc\fP immediately before
|
||||
the command is given. (Note: "Magic" syntax does not support shell builtins or
|
||||
command aliases)
|
||||
|
||||
.SH OPTIONS
|
||||
.B
|
||||
@@ -265,6 +272,11 @@ hashsum command parser (`md5sum`, `shasum`, etc.)
|
||||
\fB--ini\fP
|
||||
INI file parser
|
||||
|
||||
.TP
|
||||
.B
|
||||
\fB--ini-dup\fP
|
||||
INI with duplicate key file parser
|
||||
|
||||
.TP
|
||||
.B
|
||||
\fB--iostat\fP
|
||||
@@ -760,10 +772,15 @@ Semantic Version string parser
|
||||
\fB--ss\fP
|
||||
`ss` command parser
|
||||
|
||||
.TP
|
||||
.B
|
||||
\fB--ssh-conf\fP
|
||||
`ssh` config file and `ssh -G` command parser
|
||||
|
||||
.TP
|
||||
.B
|
||||
\fB--sshd-conf\fP
|
||||
sshd config file and `sshd -T` command parser
|
||||
`sshd` config file and `sshd -T` command parser
|
||||
|
||||
.TP
|
||||
.B
|
||||
@@ -840,6 +857,11 @@ Syslog RFC 3164 string streaming parser
|
||||
\fB--timestamp\fP
|
||||
Unix Epoch Timestamp string parser
|
||||
|
||||
.TP
|
||||
.B
|
||||
\fB--toml\fP
|
||||
TOML file parser
|
||||
|
||||
.TP
|
||||
.B
|
||||
\fB--top\fP
|
||||
@@ -905,6 +927,11 @@ Unix Epoch Timestamp string parser
|
||||
\fB--url\fP
|
||||
URL string parser
|
||||
|
||||
.TP
|
||||
.B
|
||||
\fB--ver\fP
|
||||
Version string parser
|
||||
|
||||
.TP
|
||||
.B
|
||||
\fB--vmstat\fP
|
||||
@@ -955,6 +982,16 @@ YAML file parser
|
||||
\fB--zipinfo\fP
|
||||
`zipinfo` command parser
|
||||
|
||||
.TP
|
||||
.B
|
||||
\fB--zpool-iostat\fP
|
||||
`zpool iostat` command parser
|
||||
|
||||
.TP
|
||||
.B
|
||||
\fB--zpool-status\fP
|
||||
`zpool status` command parser
|
||||
|
||||
|
||||
.RE
|
||||
.PP
|
||||
@@ -969,7 +1006,8 @@ About \fBjc\fP (JSON or YAML output)
|
||||
.TP
|
||||
.B
|
||||
\fB-C\fP, \fB--force-color\fP
|
||||
Force color output even when using pipes (overrides \fB-m\fP and the \fBNO_COLOR\fP env variable)
|
||||
Force color output even when using pipes (overrides \fB-m\fP and the
|
||||
\fBNO_COLOR\fP env variable)
|
||||
.TP
|
||||
.B
|
||||
\fB-d\fP, \fB--debug\fP
|
||||
@@ -977,7 +1015,8 @@ Debug - show traceback (use \fB-dd\fP for verbose traceback)
|
||||
.TP
|
||||
.B
|
||||
\fB-h\fP, \fB--help\fP
|
||||
Help (\fB--help --parser_name\fP for parser documentation). Use twice to show hidden parsers (e.g. \fB-hh\fP)
|
||||
Help (\fB--help --parser_name\fP for parser documentation). Use twice to show
|
||||
hidden parsers (e.g. \fB-hh\fP)
|
||||
.TP
|
||||
.B
|
||||
\fB-m\fP, \fB--monochrome\fP
|
||||
@@ -985,7 +1024,8 @@ Monochrome output
|
||||
.TP
|
||||
.B
|
||||
\fB-M\fP, \fB--meta-out\fP
|
||||
Add metadata to output including timestamp, parser name, magic command, magic command exit code, etc.
|
||||
Add metadata to output including timestamp, parser name, magic command, magic
|
||||
command exit code, etc.
|
||||
.TP
|
||||
.B
|
||||
\fB-p\fP, \fB--pretty\fP
|
||||
@@ -993,11 +1033,13 @@ Pretty print output
|
||||
.TP
|
||||
.B
|
||||
\fB-q\fP, \fB--quiet\fP
|
||||
Quiet mode. Suppresses parser warning messages (use -qq to ignore streaming parser errors)
|
||||
Quiet mode. Suppresses parser warning messages (use -qq to ignore streaming
|
||||
parser errors)
|
||||
.TP
|
||||
.B
|
||||
\fB-r\fP, \fB--raw\fP
|
||||
Raw output. Provides more literal output, typically with string values and no additional semantic processing
|
||||
Raw output. Provides more literal output, typically with string values and no
|
||||
additional semantic processing
|
||||
.TP
|
||||
.B
|
||||
\fB-u\fP, \fB--unbuffer\fP
|
||||
@@ -1019,10 +1061,93 @@ Generate Bash shell completion script
|
||||
\fB-Z\fP, \fB--zsh-comp\fP
|
||||
Generate Zsh shell completion script
|
||||
|
||||
.SH EXIT CODES
|
||||
Any fatal errors within \fBjc\fP will generate an exit code of \fB100\fP, otherwise the exit code will be \fB0\fP.
|
||||
.RE
|
||||
.PP
|
||||
.B
|
||||
Slice:
|
||||
.RS
|
||||
Line slicing is supported using the \fBSTART:STOP\fP syntax similar to Python
|
||||
slicing. This allows you to skip lines at the beginning and/or end of the
|
||||
\fBSTDIN\fP input you would like \fBjc\fP to convert.
|
||||
|
||||
When using the "magic" syntax (e.g. \fBjc ifconfig eth0\fP), \fBjc\fP will store the exit code of the program being parsed and add it to the \fBjc\fP exit code. This way it is easier to determine if an error was from the parsed program or \fBjc\fP.
|
||||
\fBSTART\fP and \fBSTOP\fP 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:
|
||||
|
||||
.RS
|
||||
.nf
|
||||
$ 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"}]
|
||||
.fi
|
||||
.RE
|
||||
|
||||
In this example \fB1:-1\fP and \fB1:4\fP line slices provide the same output.
|
||||
|
||||
When using positive integers the index location of \fBSTOP\fP is non-inclusive.
|
||||
Positive slices count from the first line of the input toward the end
|
||||
starting at \fB0\fP as the first line. Negative slices count from the last line
|
||||
toward the beginning starting at \fB-1\fP as the last line. This is also the way
|
||||
Python's slicing feature works.
|
||||
|
||||
Here is a breakdown of line slice options:
|
||||
|
||||
.TP
|
||||
.B
|
||||
\fBSTART:STOP\fP
|
||||
lines \fBSTART\fP through \fBSTOP - 1\fP
|
||||
.TP
|
||||
.B
|
||||
\fBSTART:\fP
|
||||
lines \fBSTART\fP through the rest of the output
|
||||
.TP
|
||||
.B
|
||||
\fB:STOP\fP
|
||||
lines from the beginning through \fBSTOP - 1\fP
|
||||
.TP
|
||||
.B
|
||||
\fB-START:STOP\fP
|
||||
\fBSTART\fP lines from the end through \fBSTOP - 1\fP
|
||||
.TP
|
||||
.B
|
||||
\fBSTART:-STOP\fP
|
||||
lines \fBSTART\fP through \fBSTOP\fP lines from the end
|
||||
.TP
|
||||
.B
|
||||
\fB-START:-STOP\fP
|
||||
\fBSTART\fP lines from the end through \fBSTOP\fP lines from the end
|
||||
.TP
|
||||
.B
|
||||
\fB-START:\fP
|
||||
\fBSTART\fP lines from the end through the rest of the output
|
||||
.TP
|
||||
.B
|
||||
\fB:-STOP\fP
|
||||
lines from the beginning through \fBSTOP\fP lines from the end
|
||||
.TP
|
||||
.B
|
||||
\fB:\fP
|
||||
all lines
|
||||
|
||||
.SH EXIT CODES
|
||||
Any fatal errors within \fBjc\fP will generate an exit code of \fB100\fP,
|
||||
otherwise the exit code will be \fB0\fP.
|
||||
|
||||
When using the "magic" syntax (e.g. \fBjc ifconfig eth0\fP), \fBjc\fP will store
|
||||
the exit code of the program being parsed and add it to the \fBjc\fP exit code.
|
||||
This way it is easier to determine if an error was from the parsed program or
|
||||
\fBjc\fP.
|
||||
|
||||
Consider the following examples using \fBifconfig\fP:
|
||||
|
||||
@@ -1037,9 +1162,9 @@ ifconfig exit code = \fB1\fP, jc exit code = \fB100\fP, combined exit code = \fB
|
||||
.RE
|
||||
|
||||
When using the "magic" syntax you can also retrieve the exit code of the called
|
||||
program by using the \fB--meta-out\fP or \fB-M\fP option. This will append a \fB_jc_meta\fP
|
||||
object to the output that will include the magic command information, including
|
||||
the exit code.
|
||||
program by using the \fB--meta-out\fP or \fB-M\fP option. This will append a
|
||||
\fB_jc_meta\fP object to the output that will include the magic command
|
||||
information, including the exit code.
|
||||
|
||||
Here is an example with \fBping\fP:
|
||||
.RS
|
||||
@@ -1081,11 +1206,16 @@ $ echo $?
|
||||
|
||||
\fBCustom Colors\fP
|
||||
|
||||
You can specify custom colors via the \fBJC_COLORS\fP environment variable. The \fBJC_COLORS\fP environment variable takes four comma separated string values in the following format:
|
||||
You can specify custom colors via the \fBJC_COLORS\fP environment variable. The
|
||||
\fBJC_COLORS\fP environment variable takes four comma separated string values in
|
||||
the following format:
|
||||
|
||||
JC_COLORS=<keyname_color>,<keyword_color>,<number_color>,<string_color>
|
||||
|
||||
Where colors are: \fBblack\fP, \fBred\fP, \fBgreen\fP, \fByellow\fP, \fBblue\fP, \fBmagenta\fP, \fBcyan\fP, \fBgray\fP, \fBbrightblack\fP, \fBbrightred\fP, \fBbrightgreen\fP, \fBbrightyellow\fP, \fBbrightblue\fP, \fBbrightmagenta\fP, \fBbrightcyan\fP, \fBwhite\fP, or \fBdefault\fP
|
||||
Where colors are: \fBblack\fP, \fBred\fP, \fBgreen\fP, \fByellow\fP, \fBblue\fP,
|
||||
\fBmagenta\fP, \fBcyan\fP, \fBgray\fP, \fBbrightblack\fP, \fBbrightred\fP,
|
||||
\fBbrightgreen\fP, \fBbrightyellow\fP, \fBbrightblue\fP, \fBbrightmagenta\fP,
|
||||
\fBbrightcyan\fP, \fBwhite\fP, or \fBdefault\fP
|
||||
|
||||
For example, to set to the default colors:
|
||||
|
||||
@@ -1099,10 +1229,20 @@ JC_COLORS=default,default,default,default
|
||||
|
||||
\fBDisable Color Output\fP
|
||||
|
||||
You can set the \fBNO_COLOR\fP environment variable to any value to disable color output in \fBjc\fP. Note that using the \fB-C\fP option to force color output will override both the \fBNO_COLOR\fP environment variable and the \fB-m\fP option.
|
||||
You can set the \fBNO_COLOR\fP environment variable to any value to disable
|
||||
color output in \fBjc\fP. Note that using the \fB-C\fP option to force color
|
||||
output will override both the \fBNO_COLOR\fP environment variable and the
|
||||
\fB-m\fP option.
|
||||
|
||||
.SH STREAMING PARSERS
|
||||
Most parsers load all of the data from \fBSTDIN\fP, parse it, then output the entire JSON document serially. There are some streaming parsers (e.g. \fBls-s\fP, \fBping-s\fP, etc.) that immediately start processing and outputting the data line-by-line as JSON Lines (aka NDJSON) while it is being received from \fBSTDIN\fP. This can significantly reduce the amount of memory required to parse large amounts of command output (e.g. \fBls -lR /\fP) and can sometimes process the data more quickly. Streaming parsers have slightly different behavior than standard parsers as outlined below.
|
||||
Most parsers load all of the data from \fBSTDIN\fP, parse it, then output the
|
||||
entire JSON document serially. There are some streaming parsers (e.g.
|
||||
\fBls-s\fP, \fBping-s\fP, etc.) that immediately start processing and outputting
|
||||
the data line-by-line as JSON Lines (aka NDJSON) while it is being received from
|
||||
\fBSTDIN\fP. This can significantly reduce the amount of memory required to
|
||||
parse large amounts of command output (e.g. \fBls -lR /\fP) and can sometimes
|
||||
process the data more quickly. Streaming parsers have slightly different
|
||||
behavior than standard parsers as outlined below.
|
||||
|
||||
.RS
|
||||
Note: Streaming parsers cannot be used with the "magic" syntax
|
||||
@@ -1110,7 +1250,14 @@ Note: Streaming parsers cannot be used with the "magic" syntax
|
||||
|
||||
\fBIgnoring Errors\fP
|
||||
|
||||
You may want to ignore parsing errors when using streaming parsers since these may be used in long-lived processing pipelines and errors can break the pipe. To ignore parsing errors, use the \fB-qq\fP cli option. This will add a \fB_jc_meta\fP object to the JSON output with a \fBsuccess\fP attribute. If \fBsuccess\fP is \fBtrue\fP, then there were no issues parsing the line. If \fBsuccess\fP is \fBfalse\fP, then a parsing issue was found and \fBerror\fP and \fBline\fP fields will be added to include a short error description and the contents of the unparsable line, respectively:
|
||||
You may want to ignore parsing errors when using streaming parsers since these
|
||||
may be used in long-lived processing pipelines and errors can break the pipe. To
|
||||
ignore parsing errors, use the \fB-qq\fP cli option. This will add a
|
||||
\fB_jc_meta\fP object to the JSON output with a \fBsuccess\fP attribute. If
|
||||
\fBsuccess\fP is \fBtrue\fP, then there were no issues parsing the line. If
|
||||
\fBsuccess\fP is \fBfalse\fP, then a parsing issue was found and \fBerror\fP and
|
||||
\fBline\fP fields will be added to include a short error description and the
|
||||
contents of the unparsable line, respectively:
|
||||
|
||||
.RS
|
||||
Successfully parsed line with \fB-qq\fP option:
|
||||
@@ -1141,7 +1288,11 @@ Unsuccessfully parsed line with \fB-qq\fP option:
|
||||
.RE
|
||||
\fBUnbuffering Output\fP
|
||||
|
||||
Most operating systems will buffer output that is being piped from process to process. The buffer is usually around 4KB. When viewing the output in the terminal the OS buffer is not engaged so output is immediately displayed on the screen. When piping multiple processes together, though, it may seem as if the output is hanging when the input data is very slow (e.g. \fBping\fP):
|
||||
Most operating systems will buffer output that is being piped from process to
|
||||
process. The buffer is usually around 4KB. When viewing the output in the
|
||||
terminal the OS buffer is not engaged so output is immediately displayed on the
|
||||
screen. When piping multiple processes together, though, it may seem as if the
|
||||
output is hanging when the input data is very slow (e.g. \fBping\fP):
|
||||
|
||||
.RS
|
||||
.nf
|
||||
@@ -1150,7 +1301,9 @@ $ ping 1.1.1.1 | jc \fB--ping-s\fP | jq
|
||||
.fi
|
||||
.RE
|
||||
|
||||
This is because the OS engages the 4KB buffer between \fBjc\fP and \fBjq\fP in this example. To display the data on the terminal in realtime, you can disable the buffer with the \fB-u\fP (unbuffer) cli option:
|
||||
This is because the OS engages the 4KB buffer between \fBjc\fP and \fBjq\fP in
|
||||
this example. To display the data on the terminal in realtime, you can disable
|
||||
the buffer with the \fB-u\fP (unbuffer) cli option:
|
||||
|
||||
.RS
|
||||
.nf
|
||||
@@ -1164,7 +1317,8 @@ Note: Unbuffered output can be slower for large data streams.
|
||||
.RE
|
||||
|
||||
.SH CUSTOM PARSERS
|
||||
Custom local parser plugins may be placed in a \fBjc/jcparsers\fP folder in your local "App data directory":
|
||||
Custom local parser plugins may be placed in a \fBjc/jcparsers\fP folder in your
|
||||
local "App data directory":
|
||||
|
||||
.RS
|
||||
.nf
|
||||
@@ -1174,11 +1328,16 @@ Custom local parser plugins may be placed in a \fBjc/jcparsers\fP folder in your
|
||||
.fi
|
||||
.RE
|
||||
|
||||
Local parser plugins are standard python module files. Use the \fBjc/parsers/foo.py\fP or \fBjc/parsers/foo_s.py\fP (streaming) parser as a template and simply place a \fB.py\fP file in the \fBjcparsers\fP subfolder.
|
||||
Local parser plugins are standard python module files. Use the
|
||||
\fBjc/parsers/foo.py\fP or \fBjc/parsers/foo_s.py\fP (streaming) parser as a
|
||||
template and simply place a \fB.py\fP file in the \fBjcparsers\fP subfolder.
|
||||
|
||||
Local 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.
|
||||
Local 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.
|
||||
|
||||
Note: The application data directory follows the XDG Base Directory Specification
|
||||
Note: The application data directory follows the \fBXDG Base Directory
|
||||
Specification\fP
|
||||
|
||||
.SH CAVEATS
|
||||
\fBLocale\fP
|
||||
@@ -1203,9 +1362,13 @@ escape sequences if the \fBC\fP locale does not support UTF-8 encoding.
|
||||
|
||||
\fBTimezones\fP
|
||||
|
||||
Some parsers have calculated epoch timestamp fields added to the output. Unless a timestamp field name has a \fB_utc\fP suffix it is considered naive. (i.e. based on the local timezone of the system the \fBjc\fP parser was run on).
|
||||
Some parsers have calculated epoch timestamp fields added to the output. Unless
|
||||
a timestamp field name has a \fB_utc\fP suffix it is considered naive. (i.e.
|
||||
based on the local timezone of the system the \fBjc\fP parser was run on).
|
||||
|
||||
If a UTC timezone can be detected in the text of the command output, the timestamp will be timezone aware and have a \fB_utc\fP suffix on the key name. (e.g. \fBepoch_utc\fP) No other timezones are supported for aware timestamps.
|
||||
If a UTC timezone can be detected in the text of the command output, the
|
||||
timestamp will be timezone aware and have a \fB_utc\fP suffix on the key name.
|
||||
(e.g. \fBepoch_utc\fP) No other timezones are supported for aware timestamps.
|
||||
|
||||
.SH EXAMPLES
|
||||
Standard Syntax:
|
||||
@@ -1222,16 +1385,29 @@ $ jc \fB--pretty\fP dig www.google.com
|
||||
$ jc \fB--pretty\fP /proc/meminfo
|
||||
.RE
|
||||
|
||||
Line Slicing:
|
||||
.RS
|
||||
$ cat file.csv | jc \fB:101\fP \fB--csv\fP # parse first 100 lines
|
||||
.RE
|
||||
|
||||
For parser documentation:
|
||||
.RS
|
||||
$ jc \fB--help\fP \fB--dig\fP
|
||||
.RE
|
||||
|
||||
More Help:
|
||||
.RS
|
||||
$ jc \fB-hh\fP # show hidden parsers
|
||||
|
||||
$ jc \fB-hhh\fP # list parsers by category tags
|
||||
.RE
|
||||
|
||||
.SH AUTHOR
|
||||
Kelly Brazil (kellyjonbrazil@gmail.com)
|
||||
|
||||
https://github.com/kellyjonbrazil/jc
|
||||
|
||||
.SH COPYRIGHT
|
||||
Copyright (c) 2019-2022 Kelly Brazil
|
||||
Copyright (c) 2019-2023 Kelly Brazil
|
||||
|
||||
License: MIT License
|
||||
2
setup.py
2
setup.py
@@ -5,7 +5,7 @@ with open('README.md', 'r') as f:
|
||||
|
||||
setuptools.setup(
|
||||
name='jc',
|
||||
version='1.22.4',
|
||||
version='1.23.0',
|
||||
author='Kelly Brazil',
|
||||
author_email='kellyjonbrazil@gmail.com',
|
||||
description='Converts the output of popular command-line tools and file-types to JSON.',
|
||||
|
||||
@@ -1,28 +1,35 @@
|
||||
.TH jc 1 {{ today }} {{ jc.version}} "JSON Convert"
|
||||
.SH NAME
|
||||
\fBjc\fP \- JSON Convert JSONifies the output of many CLI tools, file-types, and strings
|
||||
\fBjc\fP \- JSON Convert JSONifies the output of many CLI tools, file-types,
|
||||
and strings
|
||||
.SH SYNOPSIS
|
||||
|
||||
Standard syntax:
|
||||
|
||||
.RS
|
||||
COMMAND | \fBjc\fP [OPTIONS] PARSER
|
||||
COMMAND | \fBjc\fP [SLICE] [OPTIONS] PARSER
|
||||
|
||||
cat FILE | \fBjc\fP [OPTIONS] PARSER
|
||||
cat FILE | \fBjc\fP [SLICE] [OPTIONS] PARSER
|
||||
|
||||
echo STRING | \fBjc\fP [OPTIONS] PARSER
|
||||
echo STRING | \fBjc\fP [SLICE] [OPTIONS] PARSER
|
||||
.RE
|
||||
|
||||
Magic syntax:
|
||||
|
||||
.RS
|
||||
\fBjc\fP [OPTIONS] COMMAND
|
||||
\fBjc\fP [SLICE] [OPTIONS] COMMAND
|
||||
|
||||
\fBjc\fP [OPTIONS] /proc/<path-to-procfile>
|
||||
\fBjc\fP [SLICE] [OPTIONS] /proc/<path-to-procfile>
|
||||
.RE
|
||||
|
||||
.SH DESCRIPTION
|
||||
\fBjc\fP JSONifies the output of many CLI tools, file-types, and common strings for easier parsing in scripts. \fBjc\fP accepts piped input from \fBSTDIN\fP and outputs a JSON representation of the previous command's output to \fBSTDOUT\fP. Alternatively, the "Magic" syntax can be used by prepending \fBjc\fP to the command to be converted. Options can be passed to \fBjc\fP immediately before the command is given. (Note: "Magic" syntax does not support shell builtins or command aliases)
|
||||
\fBjc\fP JSONifies the output of many CLI tools, file-types, and common strings
|
||||
for easier parsing in scripts. \fBjc\fP accepts piped input from \fBSTDIN\fP and
|
||||
outputs a JSON representation of the previous command's output to \fBSTDOUT\fP.
|
||||
Alternatively, the "Magic" syntax can be used by prepending \fBjc\fP to the
|
||||
command to be converted. Options can be passed to \fBjc\fP immediately before
|
||||
the command is given. (Note: "Magic" syntax does not support shell builtins or
|
||||
command aliases)
|
||||
|
||||
.SH OPTIONS
|
||||
.B
|
||||
@@ -49,7 +56,8 @@ About \fBjc\fP (JSON or YAML output)
|
||||
.TP
|
||||
.B
|
||||
\fB-C\fP, \fB--force-color\fP
|
||||
Force color output even when using pipes (overrides \fB-m\fP and the \fBNO_COLOR\fP env variable)
|
||||
Force color output even when using pipes (overrides \fB-m\fP and the
|
||||
\fBNO_COLOR\fP env variable)
|
||||
.TP
|
||||
.B
|
||||
\fB-d\fP, \fB--debug\fP
|
||||
@@ -57,7 +65,8 @@ Debug - show traceback (use \fB-dd\fP for verbose traceback)
|
||||
.TP
|
||||
.B
|
||||
\fB-h\fP, \fB--help\fP
|
||||
Help (\fB--help --parser_name\fP for parser documentation). Use twice to show hidden parsers (e.g. \fB-hh\fP)
|
||||
Help (\fB--help --parser_name\fP for parser documentation). Use twice to show
|
||||
hidden parsers (e.g. \fB-hh\fP)
|
||||
.TP
|
||||
.B
|
||||
\fB-m\fP, \fB--monochrome\fP
|
||||
@@ -65,7 +74,8 @@ Monochrome output
|
||||
.TP
|
||||
.B
|
||||
\fB-M\fP, \fB--meta-out\fP
|
||||
Add metadata to output including timestamp, parser name, magic command, magic command exit code, etc.
|
||||
Add metadata to output including timestamp, parser name, magic command, magic
|
||||
command exit code, etc.
|
||||
.TP
|
||||
.B
|
||||
\fB-p\fP, \fB--pretty\fP
|
||||
@@ -73,11 +83,13 @@ Pretty print output
|
||||
.TP
|
||||
.B
|
||||
\fB-q\fP, \fB--quiet\fP
|
||||
Quiet mode. Suppresses parser warning messages (use -qq to ignore streaming parser errors)
|
||||
Quiet mode. Suppresses parser warning messages (use -qq to ignore streaming
|
||||
parser errors)
|
||||
.TP
|
||||
.B
|
||||
\fB-r\fP, \fB--raw\fP
|
||||
Raw output. Provides more literal output, typically with string values and no additional semantic processing
|
||||
Raw output. Provides more literal output, typically with string values and no
|
||||
additional semantic processing
|
||||
.TP
|
||||
.B
|
||||
\fB-u\fP, \fB--unbuffer\fP
|
||||
@@ -99,10 +111,93 @@ Generate Bash shell completion script
|
||||
\fB-Z\fP, \fB--zsh-comp\fP
|
||||
Generate Zsh shell completion script
|
||||
|
||||
.SH EXIT CODES
|
||||
Any fatal errors within \fBjc\fP will generate an exit code of \fB100\fP, otherwise the exit code will be \fB0\fP.
|
||||
.RE
|
||||
.PP
|
||||
.B
|
||||
Slice:
|
||||
.RS
|
||||
Line slicing is supported using the \fBSTART:STOP\fP syntax similar to Python
|
||||
slicing. This allows you to skip lines at the beginning and/or end of the
|
||||
\fBSTDIN\fP input you would like \fBjc\fP to convert.
|
||||
|
||||
When using the "magic" syntax (e.g. \fBjc ifconfig eth0\fP), \fBjc\fP will store the exit code of the program being parsed and add it to the \fBjc\fP exit code. This way it is easier to determine if an error was from the parsed program or \fBjc\fP.
|
||||
\fBSTART\fP and \fBSTOP\fP 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:
|
||||
|
||||
.RS
|
||||
.nf
|
||||
$ 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"}]
|
||||
.fi
|
||||
.RE
|
||||
|
||||
In this example \fB1:-1\fP and \fB1:4\fP line slices provide the same output.
|
||||
|
||||
When using positive integers the index location of \fBSTOP\fP is non-inclusive.
|
||||
Positive slices count from the first line of the input toward the end
|
||||
starting at \fB0\fP as the first line. Negative slices count from the last line
|
||||
toward the beginning starting at \fB-1\fP as the last line. This is also the way
|
||||
Python's slicing feature works.
|
||||
|
||||
Here is a breakdown of line slice options:
|
||||
|
||||
.TP
|
||||
.B
|
||||
\fBSTART:STOP\fP
|
||||
lines \fBSTART\fP through \fBSTOP - 1\fP
|
||||
.TP
|
||||
.B
|
||||
\fBSTART:\fP
|
||||
lines \fBSTART\fP through the rest of the output
|
||||
.TP
|
||||
.B
|
||||
\fB:STOP\fP
|
||||
lines from the beginning through \fBSTOP - 1\fP
|
||||
.TP
|
||||
.B
|
||||
\fB-START:STOP\fP
|
||||
\fBSTART\fP lines from the end through \fBSTOP - 1\fP
|
||||
.TP
|
||||
.B
|
||||
\fBSTART:-STOP\fP
|
||||
lines \fBSTART\fP through \fBSTOP\fP lines from the end
|
||||
.TP
|
||||
.B
|
||||
\fB-START:-STOP\fP
|
||||
\fBSTART\fP lines from the end through \fBSTOP\fP lines from the end
|
||||
.TP
|
||||
.B
|
||||
\fB-START:\fP
|
||||
\fBSTART\fP lines from the end through the rest of the output
|
||||
.TP
|
||||
.B
|
||||
\fB:-STOP\fP
|
||||
lines from the beginning through \fBSTOP\fP lines from the end
|
||||
.TP
|
||||
.B
|
||||
\fB:\fP
|
||||
all lines
|
||||
|
||||
.SH EXIT CODES
|
||||
Any fatal errors within \fBjc\fP will generate an exit code of \fB100\fP,
|
||||
otherwise the exit code will be \fB0\fP.
|
||||
|
||||
When using the "magic" syntax (e.g. \fBjc ifconfig eth0\fP), \fBjc\fP will store
|
||||
the exit code of the program being parsed and add it to the \fBjc\fP exit code.
|
||||
This way it is easier to determine if an error was from the parsed program or
|
||||
\fBjc\fP.
|
||||
|
||||
Consider the following examples using \fBifconfig\fP:
|
||||
|
||||
@@ -117,9 +212,9 @@ ifconfig exit code = \fB1\fP, jc exit code = \fB100\fP, combined exit code = \fB
|
||||
.RE
|
||||
|
||||
When using the "magic" syntax you can also retrieve the exit code of the called
|
||||
program by using the \fB--meta-out\fP or \fB-M\fP option. This will append a \fB_jc_meta\fP
|
||||
object to the output that will include the magic command information, including
|
||||
the exit code.
|
||||
program by using the \fB--meta-out\fP or \fB-M\fP option. This will append a
|
||||
\fB_jc_meta\fP object to the output that will include the magic command
|
||||
information, including the exit code.
|
||||
|
||||
Here is an example with \fBping\fP:
|
||||
.RS
|
||||
@@ -161,11 +256,16 @@ $ echo $?
|
||||
|
||||
\fBCustom Colors\fP
|
||||
|
||||
You can specify custom colors via the \fBJC_COLORS\fP environment variable. The \fBJC_COLORS\fP environment variable takes four comma separated string values in the following format:
|
||||
You can specify custom colors via the \fBJC_COLORS\fP environment variable. The
|
||||
\fBJC_COLORS\fP environment variable takes four comma separated string values in
|
||||
the following format:
|
||||
|
||||
JC_COLORS=<keyname_color>,<keyword_color>,<number_color>,<string_color>
|
||||
|
||||
Where colors are: \fBblack\fP, \fBred\fP, \fBgreen\fP, \fByellow\fP, \fBblue\fP, \fBmagenta\fP, \fBcyan\fP, \fBgray\fP, \fBbrightblack\fP, \fBbrightred\fP, \fBbrightgreen\fP, \fBbrightyellow\fP, \fBbrightblue\fP, \fBbrightmagenta\fP, \fBbrightcyan\fP, \fBwhite\fP, or \fBdefault\fP
|
||||
Where colors are: \fBblack\fP, \fBred\fP, \fBgreen\fP, \fByellow\fP, \fBblue\fP,
|
||||
\fBmagenta\fP, \fBcyan\fP, \fBgray\fP, \fBbrightblack\fP, \fBbrightred\fP,
|
||||
\fBbrightgreen\fP, \fBbrightyellow\fP, \fBbrightblue\fP, \fBbrightmagenta\fP,
|
||||
\fBbrightcyan\fP, \fBwhite\fP, or \fBdefault\fP
|
||||
|
||||
For example, to set to the default colors:
|
||||
|
||||
@@ -179,10 +279,20 @@ JC_COLORS=default,default,default,default
|
||||
|
||||
\fBDisable Color Output\fP
|
||||
|
||||
You can set the \fBNO_COLOR\fP environment variable to any value to disable color output in \fBjc\fP. Note that using the \fB-C\fP option to force color output will override both the \fBNO_COLOR\fP environment variable and the \fB-m\fP option.
|
||||
You can set the \fBNO_COLOR\fP environment variable to any value to disable
|
||||
color output in \fBjc\fP. Note that using the \fB-C\fP option to force color
|
||||
output will override both the \fBNO_COLOR\fP environment variable and the
|
||||
\fB-m\fP option.
|
||||
|
||||
.SH STREAMING PARSERS
|
||||
Most parsers load all of the data from \fBSTDIN\fP, parse it, then output the entire JSON document serially. There are some streaming parsers (e.g. \fBls-s\fP, \fBping-s\fP, etc.) that immediately start processing and outputting the data line-by-line as JSON Lines (aka NDJSON) while it is being received from \fBSTDIN\fP. This can significantly reduce the amount of memory required to parse large amounts of command output (e.g. \fBls -lR /\fP) and can sometimes process the data more quickly. Streaming parsers have slightly different behavior than standard parsers as outlined below.
|
||||
Most parsers load all of the data from \fBSTDIN\fP, parse it, then output the
|
||||
entire JSON document serially. There are some streaming parsers (e.g.
|
||||
\fBls-s\fP, \fBping-s\fP, etc.) that immediately start processing and outputting
|
||||
the data line-by-line as JSON Lines (aka NDJSON) while it is being received from
|
||||
\fBSTDIN\fP. This can significantly reduce the amount of memory required to
|
||||
parse large amounts of command output (e.g. \fBls -lR /\fP) and can sometimes
|
||||
process the data more quickly. Streaming parsers have slightly different
|
||||
behavior than standard parsers as outlined below.
|
||||
|
||||
.RS
|
||||
Note: Streaming parsers cannot be used with the "magic" syntax
|
||||
@@ -190,7 +300,14 @@ Note: Streaming parsers cannot be used with the "magic" syntax
|
||||
|
||||
\fBIgnoring Errors\fP
|
||||
|
||||
You may want to ignore parsing errors when using streaming parsers since these may be used in long-lived processing pipelines and errors can break the pipe. To ignore parsing errors, use the \fB-qq\fP cli option. This will add a \fB_jc_meta\fP object to the JSON output with a \fBsuccess\fP attribute. If \fBsuccess\fP is \fBtrue\fP, then there were no issues parsing the line. If \fBsuccess\fP is \fBfalse\fP, then a parsing issue was found and \fBerror\fP and \fBline\fP fields will be added to include a short error description and the contents of the unparsable line, respectively:
|
||||
You may want to ignore parsing errors when using streaming parsers since these
|
||||
may be used in long-lived processing pipelines and errors can break the pipe. To
|
||||
ignore parsing errors, use the \fB-qq\fP cli option. This will add a
|
||||
\fB_jc_meta\fP object to the JSON output with a \fBsuccess\fP attribute. If
|
||||
\fBsuccess\fP is \fBtrue\fP, then there were no issues parsing the line. If
|
||||
\fBsuccess\fP is \fBfalse\fP, then a parsing issue was found and \fBerror\fP and
|
||||
\fBline\fP fields will be added to include a short error description and the
|
||||
contents of the unparsable line, respectively:
|
||||
|
||||
.RS
|
||||
Successfully parsed line with \fB-qq\fP option:
|
||||
@@ -221,7 +338,11 @@ Unsuccessfully parsed line with \fB-qq\fP option:
|
||||
.RE
|
||||
\fBUnbuffering Output\fP
|
||||
|
||||
Most operating systems will buffer output that is being piped from process to process. The buffer is usually around 4KB. When viewing the output in the terminal the OS buffer is not engaged so output is immediately displayed on the screen. When piping multiple processes together, though, it may seem as if the output is hanging when the input data is very slow (e.g. \fBping\fP):
|
||||
Most operating systems will buffer output that is being piped from process to
|
||||
process. The buffer is usually around 4KB. When viewing the output in the
|
||||
terminal the OS buffer is not engaged so output is immediately displayed on the
|
||||
screen. When piping multiple processes together, though, it may seem as if the
|
||||
output is hanging when the input data is very slow (e.g. \fBping\fP):
|
||||
|
||||
.RS
|
||||
.nf
|
||||
@@ -230,7 +351,9 @@ $ ping 1.1.1.1 | jc \fB--ping-s\fP | jq
|
||||
.fi
|
||||
.RE
|
||||
|
||||
This is because the OS engages the 4KB buffer between \fBjc\fP and \fBjq\fP in this example. To display the data on the terminal in realtime, you can disable the buffer with the \fB-u\fP (unbuffer) cli option:
|
||||
This is because the OS engages the 4KB buffer between \fBjc\fP and \fBjq\fP in
|
||||
this example. To display the data on the terminal in realtime, you can disable
|
||||
the buffer with the \fB-u\fP (unbuffer) cli option:
|
||||
|
||||
.RS
|
||||
.nf
|
||||
@@ -244,7 +367,8 @@ Note: Unbuffered output can be slower for large data streams.
|
||||
.RE
|
||||
|
||||
.SH CUSTOM PARSERS
|
||||
Custom local parser plugins may be placed in a \fBjc/jcparsers\fP folder in your local "App data directory":
|
||||
Custom local parser plugins may be placed in a \fBjc/jcparsers\fP folder in your
|
||||
local "App data directory":
|
||||
|
||||
.RS
|
||||
.nf
|
||||
@@ -254,11 +378,16 @@ Custom local parser plugins may be placed in a \fBjc/jcparsers\fP folder in your
|
||||
.fi
|
||||
.RE
|
||||
|
||||
Local parser plugins are standard python module files. Use the \fBjc/parsers/foo.py\fP or \fBjc/parsers/foo_s.py\fP (streaming) parser as a template and simply place a \fB.py\fP file in the \fBjcparsers\fP subfolder.
|
||||
Local parser plugins are standard python module files. Use the
|
||||
\fBjc/parsers/foo.py\fP or \fBjc/parsers/foo_s.py\fP (streaming) parser as a
|
||||
template and simply place a \fB.py\fP file in the \fBjcparsers\fP subfolder.
|
||||
|
||||
Local 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.
|
||||
Local 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.
|
||||
|
||||
Note: The application data directory follows the XDG Base Directory Specification
|
||||
Note: The application data directory follows the \fBXDG Base Directory
|
||||
Specification\fP
|
||||
|
||||
.SH CAVEATS
|
||||
\fBLocale\fP
|
||||
@@ -283,9 +412,13 @@ escape sequences if the \fBC\fP locale does not support UTF-8 encoding.
|
||||
|
||||
\fBTimezones\fP
|
||||
|
||||
Some parsers have calculated epoch timestamp fields added to the output. Unless a timestamp field name has a \fB_utc\fP suffix it is considered naive. (i.e. based on the local timezone of the system the \fBjc\fP parser was run on).
|
||||
Some parsers have calculated epoch timestamp fields added to the output. Unless
|
||||
a timestamp field name has a \fB_utc\fP suffix it is considered naive. (i.e.
|
||||
based on the local timezone of the system the \fBjc\fP parser was run on).
|
||||
|
||||
If a UTC timezone can be detected in the text of the command output, the timestamp will be timezone aware and have a \fB_utc\fP suffix on the key name. (e.g. \fBepoch_utc\fP) No other timezones are supported for aware timestamps.
|
||||
If a UTC timezone can be detected in the text of the command output, the
|
||||
timestamp will be timezone aware and have a \fB_utc\fP suffix on the key name.
|
||||
(e.g. \fBepoch_utc\fP) No other timezones are supported for aware timestamps.
|
||||
|
||||
.SH EXAMPLES
|
||||
Standard Syntax:
|
||||
@@ -302,10 +435,23 @@ $ jc \fB--pretty\fP dig www.google.com
|
||||
$ jc \fB--pretty\fP /proc/meminfo
|
||||
.RE
|
||||
|
||||
Line Slicing:
|
||||
.RS
|
||||
$ cat file.csv | jc \fB:101\fP \fB--csv\fP # parse first 100 lines
|
||||
.RE
|
||||
|
||||
For parser documentation:
|
||||
.RS
|
||||
$ jc \fB--help\fP \fB--dig\fP
|
||||
.RE
|
||||
|
||||
More Help:
|
||||
.RS
|
||||
$ jc \fB-hh\fP # show hidden parsers
|
||||
|
||||
$ jc \fB-hhh\fP # list parsers by category tags
|
||||
.RE
|
||||
|
||||
.SH AUTHOR
|
||||
{{ jc.author }} ({{ jc.author_email }})
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
> 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
|
||||
Ansible filter plugin in the `community.general` collection. See this
|
||||
@@ -44,8 +44,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 +133,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 +143,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,7 +154,7 @@ option.
|
||||
|
||||
| Argument | Command or Filetype | Documentation |
|
||||
|-------------------|---------------------------------------------------------|----------------------------------------------------------------------------|{% for parser in parsers %}
|
||||
| `{{ "{:>15}".format(parser.argument) }}` | {{ "{:<55}".format(parser.description) }} | {{ "{:<74}".format("[details](https://kellyjonbrazil.github.io/jc/docs/parsers/" + parser.name + ")") }} |{% endfor %}
|
||||
| {{ "{:>17}".format("`" + parser.argument + "`") }} | {{ "{:<55}".format(parser.description) }} | {{ "{:<74}".format("[details](https://kellyjonbrazil.github.io/jc/docs/parsers/" + parser.name + ")") }} |{% endfor %}
|
||||
|
||||
### Options
|
||||
|
||||
@@ -175,6 +175,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`.
|
||||
@@ -622,37 +670,31 @@ ifconfig | jc -p --ifconfig # or: jc -p ifconfig
|
||||
cat example.ini
|
||||
```
|
||||
```
|
||||
[DEFAULT]
|
||||
ServerAliveInterval = 45
|
||||
Compression = yes
|
||||
CompressionLevel = 9
|
||||
ForwardX11 = yes
|
||||
foo = fiz
|
||||
bar = buz
|
||||
|
||||
[bitbucket.org]
|
||||
User = hg
|
||||
[section1]
|
||||
fruit = apple
|
||||
color = blue
|
||||
|
||||
[topsecret.server.com]
|
||||
Port = 50022
|
||||
ForwardX11 = no
|
||||
[section2]
|
||||
fruit = pear
|
||||
color = green
|
||||
```
|
||||
```bash
|
||||
cat example.ini | jc -p --ini
|
||||
```
|
||||
```json
|
||||
{
|
||||
"bitbucket.org": {
|
||||
"ServeraLiveInterval": "45",
|
||||
"Compression": "yes",
|
||||
"CompressionLevel": "9",
|
||||
"ForwardX11": "yes",
|
||||
"User": "hg"
|
||||
"foo": "fiz",
|
||||
"bar": "buz",
|
||||
"section1": {
|
||||
"fruit": "apple",
|
||||
"color": "blue"
|
||||
},
|
||||
"topsecret.server.com": {
|
||||
"ServeraLiveInterval": "45",
|
||||
"Compression": "yes",
|
||||
"CompressionLevel": "9",
|
||||
"ForwardX11": "no",
|
||||
"Port": "50022"
|
||||
"section2": {
|
||||
"fruit": "pear",
|
||||
"color": "green"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
1
tests/fixtures/aix-7.1/arp-a.json
vendored
Normal file
1
tests/fixtures/aix-7.1/arp-a.json
vendored
Normal file
@@ -0,0 +1 @@
|
||||
[{"name":null,"address":"1.2.3.4","hwtype":"ethernet","hwaddress":"5:6:7:8:9:a","permanent":true},{"name":"v630gw9-3-63-1","address":"9.3.63.1","hwtype":"ethernet","hwaddress":"0:0:5e:0:1:5c"},{"name":"v630vrrp9-3-63-2","address":"9.3.63.2","hwtype":"ethernet","hwaddress":"4c:96:14:59:d7:f0"},{"name":"v630vrrp9-3-63-3","address":"9.3.63.3","hwtype":"ethernet","hwaddress":"3c:8a:b0:0:8f:f0"},{"name":"caju6","address":"9.3.63.4","hwtype":null,"hwaddress":null},{"name":"rock-lp8","address":"9.3.63.173","hwtype":"ethernet","hwaddress":"b6:1b:da:7e:1f:2"}]
|
||||
163
tests/fixtures/aix-7.1/arp-a.out
vendored
Normal file
163
tests/fixtures/aix-7.1/arp-a.out
vendored
Normal file
@@ -0,0 +1,163 @@
|
||||
? (1.2.3.4) at 5:6:7:8:9:a [ethernet] permanent published stored in bucket 93
|
||||
|
||||
v630gw9-3-63-1 (9.3.63.1) at 0:0:5e:0:1:5c [ethernet] stored in bucket 97
|
||||
|
||||
v630vrrp9-3-63-2 (9.3.63.2) at 4c:96:14:59:d7:f0 [ethernet] stored in bucket 98
|
||||
|
||||
v630vrrp9-3-63-3 (9.3.63.3) at 3c:8a:b0:0:8f:f0 [ethernet] stored in bucket 99
|
||||
|
||||
caju6 (9.3.63.4) at (incomplete)
|
||||
rock-lp8 (9.3.63.173) at b6:1b:da:7e:1f:2 [ethernet] stored in bucket 120
|
||||
|
||||
bucket: 0 contains: 0 entries
|
||||
bucket: 1 contains: 0 entries
|
||||
bucket: 2 contains: 0 entries
|
||||
bucket: 3 contains: 0 entries
|
||||
bucket: 4 contains: 0 entries
|
||||
bucket: 5 contains: 0 entries
|
||||
bucket: 6 contains: 0 entries
|
||||
bucket: 7 contains: 0 entries
|
||||
bucket: 8 contains: 0 entries
|
||||
bucket: 9 contains: 0 entries
|
||||
bucket: 10 contains: 0 entries
|
||||
bucket: 11 contains: 0 entries
|
||||
bucket: 12 contains: 0 entries
|
||||
bucket: 13 contains: 0 entries
|
||||
bucket: 14 contains: 0 entries
|
||||
bucket: 15 contains: 0 entries
|
||||
bucket: 16 contains: 0 entries
|
||||
bucket: 17 contains: 0 entries
|
||||
bucket: 18 contains: 0 entries
|
||||
bucket: 19 contains: 0 entries
|
||||
bucket: 20 contains: 0 entries
|
||||
bucket: 21 contains: 0 entries
|
||||
bucket: 22 contains: 0 entries
|
||||
bucket: 23 contains: 0 entries
|
||||
bucket: 24 contains: 0 entries
|
||||
bucket: 25 contains: 0 entries
|
||||
bucket: 26 contains: 0 entries
|
||||
bucket: 27 contains: 0 entries
|
||||
bucket: 28 contains: 0 entries
|
||||
bucket: 29 contains: 0 entries
|
||||
bucket: 30 contains: 0 entries
|
||||
bucket: 31 contains: 0 entries
|
||||
bucket: 32 contains: 0 entries
|
||||
bucket: 33 contains: 0 entries
|
||||
bucket: 34 contains: 0 entries
|
||||
bucket: 35 contains: 0 entries
|
||||
bucket: 36 contains: 0 entries
|
||||
bucket: 37 contains: 0 entries
|
||||
bucket: 38 contains: 0 entries
|
||||
bucket: 39 contains: 0 entries
|
||||
bucket: 40 contains: 0 entries
|
||||
bucket: 41 contains: 0 entries
|
||||
bucket: 42 contains: 0 entries
|
||||
bucket: 43 contains: 0 entries
|
||||
bucket: 44 contains: 0 entries
|
||||
bucket: 45 contains: 0 entries
|
||||
bucket: 46 contains: 0 entries
|
||||
bucket: 47 contains: 0 entries
|
||||
bucket: 48 contains: 0 entries
|
||||
bucket: 49 contains: 0 entries
|
||||
bucket: 50 contains: 0 entries
|
||||
bucket: 51 contains: 0 entries
|
||||
bucket: 52 contains: 0 entries
|
||||
bucket: 53 contains: 0 entries
|
||||
bucket: 54 contains: 0 entries
|
||||
bucket: 55 contains: 0 entries
|
||||
bucket: 56 contains: 0 entries
|
||||
bucket: 57 contains: 0 entries
|
||||
bucket: 58 contains: 0 entries
|
||||
bucket: 59 contains: 0 entries
|
||||
bucket: 60 contains: 0 entries
|
||||
bucket: 61 contains: 0 entries
|
||||
bucket: 62 contains: 0 entries
|
||||
bucket: 63 contains: 0 entries
|
||||
bucket: 64 contains: 0 entries
|
||||
bucket: 65 contains: 0 entries
|
||||
bucket: 66 contains: 0 entries
|
||||
bucket: 67 contains: 0 entries
|
||||
bucket: 68 contains: 0 entries
|
||||
bucket: 69 contains: 0 entries
|
||||
bucket: 70 contains: 0 entries
|
||||
bucket: 71 contains: 0 entries
|
||||
bucket: 72 contains: 0 entries
|
||||
bucket: 73 contains: 0 entries
|
||||
bucket: 74 contains: 0 entries
|
||||
bucket: 75 contains: 0 entries
|
||||
bucket: 76 contains: 0 entries
|
||||
bucket: 77 contains: 0 entries
|
||||
bucket: 78 contains: 0 entries
|
||||
bucket: 79 contains: 0 entries
|
||||
bucket: 80 contains: 0 entries
|
||||
bucket: 81 contains: 0 entries
|
||||
bucket: 82 contains: 0 entries
|
||||
bucket: 83 contains: 0 entries
|
||||
bucket: 84 contains: 0 entries
|
||||
bucket: 85 contains: 0 entries
|
||||
bucket: 86 contains: 0 entries
|
||||
bucket: 87 contains: 0 entries
|
||||
bucket: 88 contains: 0 entries
|
||||
bucket: 89 contains: 0 entries
|
||||
bucket: 90 contains: 0 entries
|
||||
bucket: 91 contains: 0 entries
|
||||
bucket: 92 contains: 0 entries
|
||||
bucket: 93 contains: 1 entries
|
||||
bucket: 94 contains: 0 entries
|
||||
bucket: 95 contains: 0 entries
|
||||
bucket: 96 contains: 0 entries
|
||||
bucket: 97 contains: 1 entries
|
||||
bucket: 98 contains: 1 entries
|
||||
bucket: 99 contains: 1 entries
|
||||
bucket: 100 contains: 0 entries
|
||||
bucket: 101 contains: 0 entries
|
||||
bucket: 102 contains: 0 entries
|
||||
bucket: 103 contains: 0 entries
|
||||
bucket: 104 contains: 0 entries
|
||||
bucket: 105 contains: 0 entries
|
||||
bucket: 106 contains: 0 entries
|
||||
bucket: 107 contains: 0 entries
|
||||
bucket: 108 contains: 0 entries
|
||||
bucket: 109 contains: 0 entries
|
||||
bucket: 110 contains: 0 entries
|
||||
bucket: 111 contains: 0 entries
|
||||
bucket: 112 contains: 0 entries
|
||||
bucket: 113 contains: 0 entries
|
||||
bucket: 114 contains: 0 entries
|
||||
bucket: 115 contains: 0 entries
|
||||
bucket: 116 contains: 0 entries
|
||||
bucket: 117 contains: 0 entries
|
||||
bucket: 118 contains: 0 entries
|
||||
bucket: 119 contains: 0 entries
|
||||
bucket: 120 contains: 1 entries
|
||||
bucket: 121 contains: 0 entries
|
||||
bucket: 122 contains: 0 entries
|
||||
bucket: 123 contains: 0 entries
|
||||
bucket: 124 contains: 0 entries
|
||||
bucket: 125 contains: 0 entries
|
||||
bucket: 126 contains: 0 entries
|
||||
bucket: 127 contains: 0 entries
|
||||
bucket: 128 contains: 0 entries
|
||||
bucket: 129 contains: 0 entries
|
||||
bucket: 130 contains: 0 entries
|
||||
bucket: 131 contains: 0 entries
|
||||
bucket: 132 contains: 0 entries
|
||||
bucket: 133 contains: 0 entries
|
||||
bucket: 134 contains: 0 entries
|
||||
bucket: 135 contains: 0 entries
|
||||
bucket: 136 contains: 0 entries
|
||||
bucket: 137 contains: 0 entries
|
||||
bucket: 138 contains: 0 entries
|
||||
bucket: 139 contains: 0 entries
|
||||
bucket: 140 contains: 0 entries
|
||||
bucket: 141 contains: 0 entries
|
||||
bucket: 142 contains: 0 entries
|
||||
bucket: 143 contains: 0 entries
|
||||
bucket: 144 contains: 0 entries
|
||||
bucket: 145 contains: 0 entries
|
||||
bucket: 146 contains: 0 entries
|
||||
bucket: 147 contains: 0 entries
|
||||
bucket: 148 contains: 0 entries
|
||||
|
||||
There are 5 entries in the arp table.
|
||||
|
||||
1
tests/fixtures/aix-7.1/mount.json
vendored
Normal file
1
tests/fixtures/aix-7.1/mount.json
vendored
Normal file
@@ -0,0 +1 @@
|
||||
[{"filesystem":"/dev/hd4","mount_point":"/","type":"jfs2","options":["rw","log=/dev/hd8"]},{"filesystem":"/dev/hd2","mount_point":"/usr","type":"jfs2","options":["rw","log=/dev/hd8"]},{"filesystem":"/dev/hd9var","mount_point":"/var","type":"jfs2","options":["rw","log=/dev/hd8"]},{"filesystem":"/dev/hd3","mount_point":"/tmp","type":"jfs2","options":["rw","log=/dev/hd8"]},{"filesystem":"/dev/hd1","mount_point":"/home","type":"jfs2","options":["rw","log=/dev/hd8"]},{"filesystem":"/dev/hd11admin","mount_point":"/admin","type":"jfs2","options":["rw","log=/dev/hd8"]},{"filesystem":"/proc","mount_point":"/proc","type":"procfs","options":["rw"]},{"filesystem":"/dev/hd10opt","mount_point":"/opt","type":"jfs2","options":["rw","log=/dev/hd8"]},{"filesystem":"/dev/livedump","mount_point":"/var/adm/ras/livedump","type":"jfs2","options":["rw","log=/dev/hd8"]},{"filesystem":"/dev/lvvarlog","mount_point":"/var/log","type":"jfs2","options":["rw","log=/dev/hd8"]},{"filesystem":"/dev/lvafslogs","mount_point":"/usr/afs/logs","type":"jfs2","options":["rw","log=/dev/hd8"]},{"filesystem":"/dev/fslv00","mount_point":"/sandbox","type":"jfs2","options":["rw","log=/dev/sboxlv_log"]},{"filesystem":"/dev/ramdisk0","mount_point":"/usr/vice/cache","type":"jfs","options":["rw","nointegrity"]},{"filesystem":"AFS","mount_point":"/afs","type":"afs","options":["rw"]},{"filesystem":"/local","mount_point":"/remote","type":"nfs3","options":["hard","intr","vers=3","sec=sys","proto=tcp","grpid","rsize=65536","wsize=65536","biods=16","nosuid"]}]
|
||||
17
tests/fixtures/aix-7.1/mount.out
vendored
Normal file
17
tests/fixtures/aix-7.1/mount.out
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
node mounted mounted over vfs date options
|
||||
-------- --------------- --------------- ------ ------------ ---------------
|
||||
/dev/hd4 / jfs2 Sep 06 11:46 rw,log=/dev/hd8
|
||||
/dev/hd2 /usr jfs2 Sep 06 11:46 rw,log=/dev/hd8
|
||||
/dev/hd9var /var jfs2 Sep 06 11:46 rw,log=/dev/hd8
|
||||
/dev/hd3 /tmp jfs2 Sep 06 11:46 rw,log=/dev/hd8
|
||||
/dev/hd1 /home jfs2 Sep 06 11:47 rw,log=/dev/hd8
|
||||
/dev/hd11admin /admin jfs2 Sep 06 11:47 rw,log=/dev/hd8
|
||||
/proc /proc procfs Sep 06 11:47 rw
|
||||
/dev/hd10opt /opt jfs2 Sep 06 11:47 rw,log=/dev/hd8
|
||||
/dev/livedump /var/adm/ras/livedump jfs2 Sep 06 11:47 rw,log=/dev/hd8
|
||||
/dev/lvvarlog /var/log jfs2 Sep 06 11:47 rw,log=/dev/hd8
|
||||
/dev/lvafslogs /usr/afs/logs jfs2 Sep 06 11:47 rw,log=/dev/hd8
|
||||
/dev/fslv00 /sandbox jfs2 Sep 06 11:47 rw,log=/dev/sboxlv_log
|
||||
/dev/ramdisk0 /usr/vice/cache jfs Sep 06 11:47 rw,nointegrity
|
||||
AFS /afs afs Sep 06 11:47 rw
|
||||
remote /local /remote nfs3 Sep 06 11:49 hard,intr,vers=3,sec=sys,proto=tcp,grpid,rsize=65536,wsize=65536,biods=16,nosuid
|
||||
1
tests/fixtures/generic/acpi-V-never-fully-discharge.json
vendored
Normal file
1
tests/fixtures/generic/acpi-V-never-fully-discharge.json
vendored
Normal file
@@ -0,0 +1 @@
|
||||
[{"type":"Battery","id":0,"state":"Discharging","charge_percent":87,"design_capacity_mah":2110,"last_full_capacity":2271,"last_full_capacity_percent":100},{"type":"Battery","id":1,"state":"Discharging","charge_percent":98,"charge_remaining":"01:43:14","design_capacity_mah":4400,"last_full_capacity":3013,"last_full_capacity_percent":68,"charge_remaining_hours":1,"charge_remaining_minutes":43,"charge_remaining_seconds":14,"charge_remaining_total_seconds":6194},{"type":"Battery","id":2,"state":"Discharging","charge_percent":0},{"type":"Battery","id":3,"state":"Full","charge_percent":100},{"type":"Adapter","id":0,"on-line":true},{"type":"Adapter","id":1,"on-line":false},{"type":"Thermal","id":0,"mode":"ok","temperature":46.0,"temperature_unit":"C","trip_points":[{"id":0,"switches_to_mode":"critical","temperature":127.0,"temperature_unit":"C"},{"id":1,"switches_to_mode":"hot","temperature":127.0,"temperature_unit":"C"}]},{"type":"Thermal","id":1,"mode":"ok","temperature":55.0,"temperature_unit":"C","trip_points":[{"id":0,"switches_to_mode":"critical","temperature":130.0,"temperature_unit":"C"},{"id":1,"switches_to_mode":"hot","temperature":100.0,"temperature_unit":"C"}]},{"type":"Cooling","id":0,"messages":["Processor 0 of 10"]},{"type":"Cooling","id":1,"messages":["Processor 0 of 10"]},{"type":"Cooling","id":2,"messages":["x86_pkg_temp no state information available"]},{"type":"Cooling","id":3,"messages":["Processor 0 of 10"]},{"type":"Cooling","id":4,"messages":["intel_powerclamp no state information available","another message"]},{"type":"Cooling","id":5,"messages":["Processor 0 of 10"]}]
|
||||
21
tests/fixtures/generic/acpi-V-never-fully-discharge.out
vendored
Normal file
21
tests/fixtures/generic/acpi-V-never-fully-discharge.out
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
Battery 0: Discharging, 87%, discharging at zero rate - will never fully discharge.
|
||||
Battery 0: design capacity 2110 mAh, last full capacity 2271 mAh = 100%
|
||||
Battery 1: Discharging, 98%, 01:43:14 remaining
|
||||
Battery 1: design capacity 4400 mAh, last full capacity 3013 mAh = 68%
|
||||
Battery 2: Discharging, 0%, rate information unavailable
|
||||
Battery 3: Full, 100%
|
||||
Adapter 0: on-line
|
||||
Adapter 1: off-line
|
||||
Thermal 0: ok, 46.0 degrees C
|
||||
Thermal 0: trip point 0 switches to mode critical at temperature 127.0 degrees C
|
||||
Thermal 0: trip point 1 switches to mode hot at temperature 127.0 degrees C
|
||||
Thermal 1: ok, 55.0 degrees C
|
||||
Thermal 1: trip point 0 switches to mode critical at temperature 130.0 degrees C
|
||||
Thermal 1: trip point 1 switches to mode hot at temperature 100.0 degrees C
|
||||
Cooling 0: Processor 0 of 10
|
||||
Cooling 1: Processor 0 of 10
|
||||
Cooling 2: x86_pkg_temp no state information available
|
||||
Cooling 3: Processor 0 of 10
|
||||
Cooling 4: intel_powerclamp no state information available
|
||||
Cooling 4: another message
|
||||
Cooling 5: Processor 0 of 10
|
||||
1
tests/fixtures/generic/crontab-no-normal-entries.json
vendored
Normal file
1
tests/fixtures/generic/crontab-no-normal-entries.json
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"variables":[],"schedule":[{"occurrence":"daily","command":"/bin/sh do_the_thing"}]}
|
||||
2
tests/fixtures/generic/crontab-no-normal-entries.out
vendored
Normal file
2
tests/fixtures/generic/crontab-no-normal-entries.out
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
#this is a test for the jc module
|
||||
@daily /bin/sh do_the_thing
|
||||
1
tests/fixtures/generic/crontab-u-no-normal-entries.json
vendored
Normal file
1
tests/fixtures/generic/crontab-u-no-normal-entries.json
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"variables":[],"schedule":[{"occurrence":"daily","user":"root","command":"/bin/sh do_the_thing"}]}
|
||||
2
tests/fixtures/generic/crontab-u-no-normal-entries.out
vendored
Normal file
2
tests/fixtures/generic/crontab-u-no-normal-entries.out
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
#this is a test for the jc module
|
||||
@daily root /bin/sh do_the_thing
|
||||
1
tests/fixtures/generic/ini-dup-double-quote.json
vendored
Normal file
1
tests/fixtures/generic/ini-dup-double-quote.json
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"client":{"user":["foo"],"host":["localhost"],"password":["bar"]}}
|
||||
1
tests/fixtures/generic/ini-dup-iptelserver.json
vendored
Normal file
1
tests/fixtures/generic/ini-dup-iptelserver.json
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"Settings":{"DetailedLog":["1"],"RunStatus":["1"],"StatusPort":["6090"],"StatusRefresh":["10"],"Archive":["1"],"LogFile":["/opt/ecs/mvuser/MV_IPTel/log/MV_IPTel.log"],"Version":["0.9 Build 4 Created July 11 2004 14:00"],"ServerName":["Unknown"]},"FTP":{"RunFTP":["1"],"FTPPort":["21"],"FTPDataPort":["20"],"FTPDir":["/opt/ecs/mvuser/MV_IPTel/data/FTPdata"],"FTP_TimeOut":["5"],"EnableSU":["1"],"SUUserName":["mvuser"],"SUPassword":["Avaya"]},"FTPS":{"RunFTPS":["0"],"FTPPort":["990"],"FTPDataPort":["889"]},"TFTP":{"RunTrivialFTP":["1"],"TrivialFTPPort":["69"],"TFTPDir":["/opt/ecs/mvuser/MV_IPTel/data/TFTPdata"]},"HTTP":{"RunHTTP":["1"],"HTTPPort":["81"],"HTTPDir":["/opt/ecs/mvuser/MV_IPTel/data/HTTPdata"]},"HTTPS":{"RunHTTPS":["0"],"HTTPSPort":["411"],"HTTPSDir":["/opt/ecs/mvuser/MV_IPTel/data/HTTPSdata"],"CertFile":["/opt/ecs/mvuser/MV_IPTel/certs/IPTelcert.pem"],"KeyFile":["/opt/ecs/mvuser/MV_IPTel/certs/IPTelkey.pem"],"ClientAuth":["0"],"IPTel":["0"],"SSLV2":["0"],"SSLV3":["0"],"TLSV1":["1"],"UseProxy":["0"],"ProxyAddr":["simon.avaya.com"],"ProxyPort":["9000"]},"BACKUP_SERVERS":{"FileServer":["0"],"RequestUpdates":["0"],"RequestBackup":["0"],"UsePrimarySvr":["0"],"PrimaryIP":["192.168.0.13"],"UseSecondarySvr":["0"],"SecondaryIP":["192.168.0.10"],"UpdateInterval":["2"],"CustomFTP":["1"],"CustomFTPDir":["home/mvuser/backup"],"CustomFTPUName":["tom"],"CustomFTPPwd":["jerry"],"CDRBackup":["0"],"BCMSBackup":["0"],"RetainDays":["7.0"]},"SNMP":{"UseSNMP":["1"]}}
|
||||
1
tests/fixtures/generic/ini-dup-single-quote.json
vendored
Normal file
1
tests/fixtures/generic/ini-dup-single-quote.json
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"client":{"user":["foo"],"host":["localhost"],"password":["bar"]}}
|
||||
1
tests/fixtures/generic/ini-dup-test.json
vendored
Normal file
1
tests/fixtures/generic/ini-dup-test.json
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"DEFAULT":{"ServerAliveInterval":["45"],"Compression":["yes"],"CompressionLevel":["9"],"ForwardX11":["yes"]},"bitbucket.org":{"User":["hg"]},"topsecret.server.com":{"Port":["50022"],"ForwardX11":["no"]}}
|
||||
2
tests/fixtures/generic/ini-test.json
vendored
2
tests/fixtures/generic/ini-test.json
vendored
@@ -1 +1 @@
|
||||
{"bitbucket.org":{"ServerAliveInterval":"45","Compression":"yes","CompressionLevel":"9","ForwardX11":"yes","User":"hg"},"topsecret.server.com":{"ServerAliveInterval":"45","Compression":"yes","CompressionLevel":"9","ForwardX11":"no","Port":"50022"}}
|
||||
{"DEFAULT":{"ServerAliveInterval":"45","Compression":"yes","CompressionLevel":"9","ForwardX11":"yes"},"bitbucket.org":{"User":"hg"},"topsecret.server.com":{"Port":"50022","ForwardX11":"no"}}
|
||||
|
||||
1
tests/fixtures/generic/lsusb-extra-hub-port-status-info.json
vendored
Normal file
1
tests/fixtures/generic/lsusb-extra-hub-port-status-info.json
vendored
Normal file
File diff suppressed because one or more lines are too long
4140
tests/fixtures/generic/lsusb-extra-hub-port-status-info.out
vendored
Normal file
4140
tests/fixtures/generic/lsusb-extra-hub-port-status-info.out
vendored
Normal file
File diff suppressed because it is too large
Load Diff
45
tests/fixtures/generic/ssh_config1
vendored
Normal file
45
tests/fixtures/generic/ssh_config1
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
## override as per host ##
|
||||
Host server1
|
||||
HostName server1.cyberciti.biz
|
||||
User nixcraft
|
||||
Port 4242
|
||||
IdentityFile /nfs/shared/users/nixcraft/keys/server1/id_rsa
|
||||
|
||||
## Home nas server ##
|
||||
Host nas01
|
||||
HostName 192.168.1.100
|
||||
User root
|
||||
IdentityFile ~/.ssh/nas01.key
|
||||
|
||||
## Login AWS Cloud ##
|
||||
Host aws.apache
|
||||
HostName 1.2.3.4
|
||||
User wwwdata
|
||||
IdentityFile ~/.ssh/aws.apache.key
|
||||
|
||||
## Login to internal lan server at 192.168.0.251 via our public uk office ssh based gateway using ##
|
||||
## $ ssh uk.gw.lan ##
|
||||
Host 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
|
||||
|
||||
## Our Us Proxy Server ##
|
||||
## Forward all local port 3128 traffic to port 3128 on the remote vps1.cyberciti.biz server ##
|
||||
## $ ssh -f -N proxyus ##
|
||||
Host proxyus
|
||||
HostName vps1.cyberciti.biz
|
||||
User breakfree
|
||||
IdentityFile ~/.ssh/vps1.cyberciti.biz.key
|
||||
LocalForward 3128 127.0.0.1:3128
|
||||
|
||||
### default for all ##
|
||||
Host *
|
||||
ForwardAgent no
|
||||
ForwardX11 no
|
||||
ForwardX11Trusted yes
|
||||
User nixcraft
|
||||
Port 22
|
||||
Protocol 2
|
||||
ServerAliveInterval 60
|
||||
ServerAliveCountMax 30
|
||||
1
tests/fixtures/generic/ssh_config1.json
vendored
Normal file
1
tests/fixtures/generic/ssh_config1.json
vendored
Normal file
@@ -0,0 +1 @@
|
||||
[{"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}]
|
||||
21
tests/fixtures/generic/ssh_config2
vendored
Normal file
21
tests/fixtures/generic/ssh_config2
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
Host targaryen
|
||||
HostName 192.168.1.10
|
||||
User daenerys
|
||||
Port 7654
|
||||
IdentityFile ~/.ssh/targaryen.key
|
||||
|
||||
Host tyrell
|
||||
HostName 192.168.10.20
|
||||
|
||||
Host martell
|
||||
HostName 192.168.10.50
|
||||
|
||||
Host *ell
|
||||
user oberyn
|
||||
|
||||
Host * !martell
|
||||
LogLevel INFO
|
||||
|
||||
Host *
|
||||
User root
|
||||
Compression yes
|
||||
1
tests/fixtures/generic/ssh_config2.json
vendored
Normal file
1
tests/fixtures/generic/ssh_config2.json
vendored
Normal file
@@ -0,0 +1 @@
|
||||
[{"host":"targaryen","host_list":["targaryen"],"hostname":"192.168.1.10","user":"daenerys","port":7654,"identityfile":["~/.ssh/targaryen.key"]},{"host":"tyrell","host_list":["tyrell"],"hostname":"192.168.10.20"},{"host":"martell","host_list":["martell"],"hostname":"192.168.10.50"},{"host":"*ell","host_list":["*ell"],"user":"oberyn"},{"host":"* !martell","host_list":["*","!martell"],"loglevel":"INFO"},{"host":"*","host_list":["*"],"user":"root","compression":"yes"}]
|
||||
33
tests/fixtures/generic/ssh_config3
vendored
Normal file
33
tests/fixtures/generic/ssh_config3
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
Host server1
|
||||
HostName server1.cyberciti.biz
|
||||
User nixcraft
|
||||
Port 4242
|
||||
IdentityFile /nfs/shared/users/nixcraft/keys/server1/id_rsa
|
||||
|
||||
## Home nas server ##
|
||||
Host nas01
|
||||
HostName 192.168.1.100
|
||||
User root
|
||||
IdentityFile ~/.ssh/nas01.key
|
||||
|
||||
## Login AWS Cloud ##
|
||||
Host aws.apache
|
||||
HostName 1.2.3.4
|
||||
User wwwdata
|
||||
IdentityFile ~/.ssh/aws.apache.key
|
||||
|
||||
## Login to internal lan server at 192.168.0.251 via our public uk office ssh based gateway using ##
|
||||
## $ ssh uk.gw.lan ##
|
||||
Host 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
|
||||
|
||||
## Our Us Proxy Server ##
|
||||
## Forward all local port 3128 traffic to port 3128 on the remote vps1.cyberciti.biz server ##
|
||||
## $ ssh -f -N proxyus ##
|
||||
Host proxyus
|
||||
HostName vps1.cyberciti.biz
|
||||
User breakfree
|
||||
IdentityFile ~/.ssh/vps1.cyberciti.biz.key
|
||||
LocalForward 3128 127.0.0.1:3128
|
||||
1
tests/fixtures/generic/ssh_config3.json
vendored
Normal file
1
tests/fixtures/generic/ssh_config3.json
vendored
Normal file
@@ -0,0 +1 @@
|
||||
[{"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"]}]
|
||||
105
tests/fixtures/generic/ssh_config4
vendored
Normal file
105
tests/fixtures/generic/ssh_config4
vendored
Normal file
@@ -0,0 +1,105 @@
|
||||
Host *
|
||||
AddKeysToAgent ask
|
||||
AddressFamily inet
|
||||
BatchMode no
|
||||
BindAddress 1.1.1.1
|
||||
BindInterface en0
|
||||
CanonicalDomains abc.com xyz.com
|
||||
CanonicalizeFallbackLocal yes
|
||||
CanonicalizeHostname none
|
||||
CanonicalizeMaxDots 2
|
||||
CanonicalizePermittedCNAMEs *.a.example.com:*.b.example.com,*.c.example.com
|
||||
CASignatureAlgorithms ssh-ed25519,ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521,sk-ssh-ed25519@openssh.com
|
||||
CertificateFile ~/certificates/cert1.pem
|
||||
CertificateFile ~/certificates/cert2.pem
|
||||
CheckHostIP yes
|
||||
Ciphers 3des-cbc,aes128-cbc,aes192-cbc
|
||||
ClearAllForwardings yes
|
||||
Compression yes
|
||||
ConnectionAttempts 9
|
||||
ConnectTimeout 30
|
||||
ControlMaster ask
|
||||
ControlPath none
|
||||
ControlPersist yes
|
||||
DynamicForward 1.1.1.1:443
|
||||
EnableEscapeCommandline no
|
||||
EnableSSHKeysign yes
|
||||
EscapeChar none
|
||||
ExitOnForwardFailure yes
|
||||
FingerprintHash md5
|
||||
ForkAfterAuthentication yes
|
||||
ForwardAgent $mypath
|
||||
ForwardX11 no
|
||||
ForwardX11Timeout 500
|
||||
ForwardX11Trusted yes
|
||||
GatewayPorts yes
|
||||
GlobalKnownHostsFile /etc/ssh/ssh_known_hosts /etc/ssh/ssh_known_hosts2
|
||||
GSSAPIAuthentication yes
|
||||
GSSAPIDelegateCredentials yes
|
||||
HashKnownHosts yes
|
||||
HostbasedAcceptedAlgorithms ssh-ed25519-cert-v01@openssh.com,ecdsa-sha2-nistp256-cert-v01@openssh.com
|
||||
HostbasedAuthentication yes
|
||||
HostKeyAlgorithms ssh-ed25519-cert-v01@openssh.com,ecdsa-sha2-nistp256-cert-v01@openssh.com
|
||||
HostKeyAlias foobar
|
||||
Hostname localhost
|
||||
IdentitiesOnly yes
|
||||
IdentityAgent SSH_AUTH_SOCK
|
||||
IdentityFile ~/.ssh/vps1.cyberciti.biz.key
|
||||
IdentityFile ~/.ssh/vps2.cyberciti.biz.key
|
||||
IgnoreUnknown helloworld
|
||||
Include ~/.ssh/config-extras ~/foo/bar
|
||||
Include ~/.ssh/config-extra-extras
|
||||
IPQoS af11 af12
|
||||
KbdInteractiveAuthentication yes
|
||||
KbdInteractiveDevices bsdauth,pam,skey
|
||||
KexAlgorithms +sntrup761x25519-sha512@openssh.com,curve25519-sha256,curve25519-sha256@libssh.org
|
||||
KnownHostsCommand ~/checkknownhosts
|
||||
LocalCommand ~/mycommand
|
||||
LocalForward 3128 127.0.0.1:3128
|
||||
LocalForward 3129 127.0.0.1:3129
|
||||
LogLevel INFO
|
||||
LogVerbose kex.c:*:1000,*:kex_exchange_identification():*,packet.c:*
|
||||
MACs ^umac-64-etm@openssh.com,umac-128-etm@openssh.com,hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com
|
||||
NoHostAuthenticationForLocalhost yes
|
||||
NumberOfPasswordPrompts 3
|
||||
PasswordAuthentication yes
|
||||
PermitLocalCommand yes
|
||||
PermitRemoteOpen 1.1.1.1:443 2.2.2.2:443
|
||||
PKCS11Provider ~/pkcs11provider
|
||||
Port 22
|
||||
PreferredAuthentications gssapi-with-mic,hostbased,publickey,keyboard-interactive,password
|
||||
Protocol 2
|
||||
ProxyCommand ssh nixcraft@gateway.uk.cyberciti.biz nc %h %p 2> /dev/null
|
||||
ProxyJump 1.1.1.1:22,2.2.2.2:22
|
||||
ProxyUseFdpass yes
|
||||
PubkeyAcceptedAlgorithms -ssh-ed25519-cert-v01@openssh.com,ecdsa-sha2-nistp256-cert-v01@openssh.com
|
||||
PubkeyAuthentication unbound
|
||||
RekeyLimit 4G
|
||||
RemoteCommand ~/mycommand
|
||||
RemoteForward 1.1.1.1:22 2.2.2.2:22
|
||||
RequestTTY force
|
||||
RequiredRSASize 2048
|
||||
RevokedHostKeys ~/revokedkeyfile
|
||||
SecurityKeyProvider ~/keyprovider
|
||||
SendEnv ENV1 ENV2
|
||||
SendEnv ENV3
|
||||
ServerAliveCountMax 3
|
||||
ServerAliveInterval 3
|
||||
SessionType none
|
||||
SetEnv ENV1 ENV2
|
||||
SetEnv ENV3
|
||||
StdinNull yes
|
||||
StreamLocalBindMask 0000
|
||||
StreamLocalBindUnlink yes
|
||||
StrictHostKeyChecking ask
|
||||
SyslogFacility USER
|
||||
TCPKeepAlive yes
|
||||
Tunnel ethernet
|
||||
TunnelDevice tun1:tun2
|
||||
UpdateHostKeys ask
|
||||
User nixcraft
|
||||
UserKnownHostsFile ~/.ssh/knownhosts1 ~/.ssh/knownhosts2
|
||||
VerifyHostKeyDNS ask
|
||||
VisualHostKey yes
|
||||
XAuthLocation /usr/X11R6/bin/xauth
|
||||
|
||||
1
tests/fixtures/generic/ssh_config4.json
vendored
Normal file
1
tests/fixtures/generic/ssh_config4.json
vendored
Normal file
@@ -0,0 +1 @@
|
||||
[{"host":"*","host_list":["*"],"addkeystoagent":"ask","addressfamily":"inet","batchmode":"no","bindaddress":"1.1.1.1","bindinterface":"en0","canonicaldomains":["abc.com","xyz.com"],"canonicalizefallbacklocal":"yes","canonicalizehostname":"none","canonicalizemaxdots":2,"canonicalizepermittedcnames":["*.a.example.com:*.b.example.com","*.c.example.com"],"casignaturealgorithms":["ssh-ed25519","ecdsa-sha2-nistp256","ecdsa-sha2-nistp384","ecdsa-sha2-nistp521","sk-ssh-ed25519@openssh.com"],"certificatefile":["~/certificates/cert1.pem","~/certificates/cert2.pem"],"checkhostip":"yes","ciphers":["3des-cbc","aes128-cbc","aes192-cbc"],"clearallforwardings":"yes","compression":"yes","connectionattempts":9,"connecttimeout":30,"controlmaster":"ask","controlpath":"none","controlpersist":"yes","dynamicforward":"1.1.1.1:443","enableescapecommandline":"no","enablesshkeysign":"yes","escapechar":"none","exitonforwardfailure":"yes","fingerprinthash":"md5","forkafterauthentication":"yes","forwardagent":"$mypath","forwardx11":"no","forwardx11timeout":500,"forwardx11trusted":"yes","gatewayports":"yes","globalknownhostsfile":["/etc/ssh/ssh_known_hosts","/etc/ssh/ssh_known_hosts2"],"gssapiauthentication":"yes","gssapidelegatecredentials":"yes","hashknownhosts":"yes","hostbasedacceptedalgorithms":["ssh-ed25519-cert-v01@openssh.com","ecdsa-sha2-nistp256-cert-v01@openssh.com"],"hostbasedauthentication":"yes","hostkeyalgorithms":["ssh-ed25519-cert-v01@openssh.com","ecdsa-sha2-nistp256-cert-v01@openssh.com"],"hostkeyalias":"foobar","hostname":"localhost","identitiesonly":"yes","identityagent":"SSH_AUTH_SOCK","identityfile":["~/.ssh/vps1.cyberciti.biz.key","~/.ssh/vps2.cyberciti.biz.key"],"ignoreunknown":"helloworld","include":["~/.ssh/config-extras","~/foo/bar","~/.ssh/config-extra-extras"],"ipqos":["af11","af12"],"kbdinteractiveauthentication":"yes","kbdinteractivedevices":["bsdauth","pam","skey"],"kexalgorithms":["sntrup761x25519-sha512@openssh.com","curve25519-sha256","curve25519-sha256@libssh.org"],"kexalgorithms_strategy":"+","knownhostscommand":"~/checkknownhosts","localcommand":"~/mycommand","localforward":["3128 127.0.0.1:3128","3129 127.0.0.1:3129"],"loglevel":"INFO","logverbose":["kex.c:*:1000","*:kex_exchange_identification():*","packet.c:*"],"macs":["umac-64-etm@openssh.com","umac-128-etm@openssh.com","hmac-sha2-256-etm@openssh.com","hmac-sha2-512-etm@openssh.com"],"macs_strategy":"^","nohostauthenticationforlocalhost":"yes","numberofpasswordprompts":3,"passwordauthentication":"yes","permitlocalcommand":"yes","permitremoteopen":["1.1.1.1:443","2.2.2.2:443"],"pkcs11provider":"~/pkcs11provider","port":22,"preferredauthentications":["gssapi-with-mic","hostbased","publickey","keyboard-interactive","password"],"protocol":2,"proxycommand":"ssh nixcraft@gateway.uk.cyberciti.biz nc %h %p 2> /dev/null","proxyjump":["1.1.1.1:22","2.2.2.2:22"],"proxyusefdpass":"yes","pubkeyacceptedalgorithms":["ssh-ed25519-cert-v01@openssh.com","ecdsa-sha2-nistp256-cert-v01@openssh.com"],"pubkeyacceptedalgorithms_strategy":"-","pubkeyauthentication":"unbound","rekeylimit":"4G","remotecommand":"~/mycommand","remoteforward":"1.1.1.1:22 2.2.2.2:22","requesttty":"force","requiredrsasize":2048,"revokedhostkeys":"~/revokedkeyfile","securitykeyprovider":"~/keyprovider","sendenv":["ENV1","ENV2","ENV3"],"serveralivecountmax":3,"serveraliveinterval":3,"sessiontype":"none","setenv":["ENV1","ENV2","ENV3"],"stdinnull":"yes","streamlocalbindmask":"0000","streamlocalbindunlink":"yes","stricthostkeychecking":"ask","syslogfacility":"USER","tcpkeepalive":"yes","tunnel":"ethernet","tunneldevice":"tun1:tun2","updatehostkeys":"ask","user":"nixcraft","userknownhostsfile":["~/.ssh/knownhosts1","~/.ssh/knownhosts2"],"verifyhostkeydns":"ask","visualhostkey":"yes","xauthlocation":"/usr/X11R6/bin/xauth"}]
|
||||
14
tests/fixtures/generic/ssh_config5
vendored
Normal file
14
tests/fixtures/generic/ssh_config5
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
|
||||
# comment
|
||||
Host *
|
||||
User something
|
||||
|
||||
# comment 2
|
||||
Host svu
|
||||
Hostname www.svuniversity.ac.in
|
||||
# within-host-comment
|
||||
Port 22
|
||||
ProxyCommand nc -w 300 -x localhost:9050 %h %p
|
||||
|
||||
# another comment
|
||||
# bla bla
|
||||
1
tests/fixtures/generic/ssh_config5.json
vendored
Normal file
1
tests/fixtures/generic/ssh_config5.json
vendored
Normal file
@@ -0,0 +1 @@
|
||||
[{"host":"*","host_list":["*"],"user":"something"},{"host":"svu","host_list":["svu"],"hostname":"www.svuniversity.ac.in","port":22,"proxycommand":"nc -w 300 -x localhost:9050 %h %p"}]
|
||||
1
tests/fixtures/generic/toml-example.json
vendored
Normal file
1
tests/fixtures/generic/toml-example.json
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"title":"TOML Example","owner":{"name":"Lance Uppercut","dob":296667120,"dob_iso":"1979-05-27T07:32:00-08:00"},"database":{"server":"192.168.1.1","ports":[8001,8001,8002],"connection_max":5000,"enabled":true},"servers":{"alpha":{"ip":"10.0.0.1","dc":"eqdc10"},"beta":{"ip":"10.0.0.2","dc":"eqdc10"}},"clients":{"data":[["gamma","delta"],[1,2]],"hosts":["alpha","omega"]}}
|
||||
33
tests/fixtures/generic/toml-example.toml
vendored
Normal file
33
tests/fixtures/generic/toml-example.toml
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
# This is a TOML document. Boom.
|
||||
|
||||
title = "TOML Example"
|
||||
|
||||
[owner]
|
||||
name = "Lance Uppercut"
|
||||
dob = 1979-05-27T07:32:00-08:00 # First class dates? Why not?
|
||||
|
||||
[database]
|
||||
server = "192.168.1.1"
|
||||
ports = [ 8001, 8001, 8002 ]
|
||||
connection_max = 5000
|
||||
enabled = true
|
||||
|
||||
[servers]
|
||||
|
||||
# You can indent as you please. Tabs or spaces. TOML don't care.
|
||||
[servers.alpha]
|
||||
ip = "10.0.0.1"
|
||||
dc = "eqdc10"
|
||||
|
||||
[servers.beta]
|
||||
ip = "10.0.0.2"
|
||||
dc = "eqdc10"
|
||||
|
||||
[clients]
|
||||
data = [ ["gamma", "delta"], [1, 2] ]
|
||||
|
||||
# Line breaks are OK when inside arrays
|
||||
hosts = [
|
||||
"alpha",
|
||||
"omega"
|
||||
]
|
||||
27
tests/fixtures/generic/toml-example2.json
vendored
Normal file
27
tests/fixtures/generic/toml-example2.json
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"fruit": [
|
||||
{
|
||||
"name": "apple",
|
||||
"physical": {
|
||||
"color": "red",
|
||||
"shape": "round"
|
||||
},
|
||||
"variety": [
|
||||
{
|
||||
"name": "red delicious"
|
||||
},
|
||||
{
|
||||
"name": "granny smith"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "banana",
|
||||
"variety": [
|
||||
{
|
||||
"name": "plantain"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
18
tests/fixtures/generic/toml-example2.toml
vendored
Normal file
18
tests/fixtures/generic/toml-example2.toml
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
[[fruit]]
|
||||
name = "apple"
|
||||
|
||||
[fruit.physical]
|
||||
color = "red"
|
||||
shape = "round"
|
||||
|
||||
[[fruit.variety]]
|
||||
name = "red delicious"
|
||||
|
||||
[[fruit.variety]]
|
||||
name = "granny smith"
|
||||
|
||||
[[fruit]]
|
||||
name = "banana"
|
||||
|
||||
[[fruit.variety]]
|
||||
name = "plantain"
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user