1
0
mirror of https://github.com/kellyjonbrazil/jc.git synced 2026-04-03 17:44:07 +02:00

Compare commits

...

177 Commits

Author SHA1 Message Date
Kelly Brazil
d298e101e9 Merge pull request #473 from kellyjonbrazil/dev
Dev v1.23.5
2023-10-21 12:23:25 -07:00
Kelly Brazil
cea975d7f1 doc update 2023-10-21 12:12:43 -07:00
Kelly Brazil
1ed69f9e6a doc update and fix tests 2023-10-21 12:09:18 -07:00
Kelly Brazil
ab0e05ec82 only set colors if pygments is installed 2023-10-21 12:01:09 -07:00
Kelly Brazil
c16cce4bf0 add tests 2023-10-13 08:52:14 -07:00
Kelly Brazil
d3489536a1 add "7" as a netstat raw state 2023-10-12 17:25:40 -07:00
Sebastian Uhlik
041050ce28 Fix bug in split when program running on UDP contains space in name (#447)
* Add condition before split.

* Safe detection of 'state' presence.

---------

Co-authored-by: Kelly Brazil <kellyjonbrazil@gmail.com>
2023-10-12 17:21:06 -07:00
pettai
7de1a8a5d6 add more tests (#468)
add all test-cases
2023-10-05 17:09:35 -07:00
Kelly Brazil
d4604743d1 add multiline value support to env parser 2023-10-02 16:30:56 -07:00
Kelly Brazil
0b8fb31298 doc update 2023-10-02 08:35:36 -07:00
Kelly Brazil
dcdd79e28c doc update 2023-10-02 08:34:14 -07:00
Kelly Brazil
5291baeb8e fixup variable names 2023-10-02 08:32:41 -07:00
Kelly Brazil
6867102c66 doc update 2023-10-01 18:13:27 -07:00
Kelly Brazil
36ed2c7e2e add lsb_release parser 2023-10-01 18:12:22 -07:00
Kelly Brazil
4ab0aba9d3 doc update 2023-10-01 17:42:42 -07:00
Kelly Brazil
e643badaf7 add os-release parser 2023-10-01 17:42:00 -07:00
Kelly Brazil
d96e96219e add comment support to xml parser 2023-10-01 11:49:50 -07:00
Kelly Brazil
e42af3353e fix pidstat parsers for -T ALL option 2023-10-01 11:25:56 -07:00
Kelly Brazil
4ec2b16f42 doc fix 2023-09-30 16:18:32 -07:00
Kelly Brazil
0a028456bf add nsd-control parser 2023-09-30 15:45:29 -07:00
pettai
a1f10928e1 Add nsd-control (#454)
* Create nsd_control.py

Init nsd_control.py

* cleanup nsd-control + add test data

- Cleanup nsd-control parser
- Add test data

* add test script

add test script + fix test data

* Update test_nsd_control.py

fix a default test

* Update test_nsd_control.py

nit
2023-09-30 15:36:52 -07:00
Kelly Brazil
eae1d4b89a doc update 2023-09-30 15:34:29 -07:00
Kelly Brazil
d3c7cec333 add host parser 2023-09-30 15:32:27 -07:00
pettai
36fa08d711 Add ISC 'host' support (#450)
* Add ISC 'host' support

Add ISC 'host' command support

* Update host.py

remove leading tab from string

* Add integer conversion

Per request, fix integer conversion

* Cleanup

Cleanup strip()'s

* Add tests

Add two tests for the 'host' parser

* Update test_host.py

nit

---------

Co-authored-by: Kelly Brazil <kellyjonbrazil@gmail.com>
2023-09-30 15:26:03 -07:00
Kelly Brazil
a9958841e4 doc update 2023-09-30 15:23:29 -07:00
Kelly Brazil
504ad81a01 version bump 2023-09-30 15:21:29 -07:00
Kevin Lyter
8bf2f4f4d0 [xrandr] Fix 453 devices issue (#455)
* [xrandr] Fix bug 453, clean up data model

* Fix: 'devices' was originally not a list, just assigned each time it
was parsed. Made that a list and appended to it.
* Removed distinction between unassociated/associated devices
* Added test for @marcin-koziol's problem
* Put tests into separate test methods

* Formatting cleanup

* Backwards compatible type syntax

---------

Co-authored-by: Kelly Brazil <kellyjonbrazil@gmail.com>
2023-09-30 15:19:14 -07:00
Kelly Brazil
805397ea18 doc update 2023-09-30 15:15:29 -07:00
Samson Umezulike
1b3985c2d7 Adds graceful handling of superfluous bits in bit strings (#459)
Co-authored-by: Kelly Brazil <kellyjonbrazil@gmail.com>
2023-09-30 15:12:18 -07:00
Kelly Brazil
f602043642 fix for negative serial numbers 2023-09-30 15:06:28 -07:00
Samson Umezulike
1a1aa8fda3 Adds graceful handling of negative serial numbers in x509 certificates (#445)
Co-authored-by: Kelly Brazil <kellyjonbrazil@gmail.com>
2023-09-30 15:02:55 -07:00
Kelly Brazil
3249a017ae add dest-unreachable test 2023-09-15 12:09:38 -07:00
Kelly Brazil
84f0246b2d move int/float conversions to _process 2023-09-14 18:15:28 -07:00
Kelly Brazil
1c795982b0 add error and corrupted fields to ping-s 2023-09-14 13:03:45 -07:00
Kelly Brazil
c5164b4108 doc update 2023-09-10 15:20:11 -07:00
Kelly Brazil
dc3716ecb3 add errors and corrupted support 2023-09-10 15:18:32 -07:00
Kelly Brazil
c5165ccc21 version bump 2023-09-10 15:18:23 -07:00
José Miguel Guzmán
5b2035e0e6 Add support for corrupted and errors in linux ping. (#442)
* Add support for corrupted and errors in linux ping.

* Fix regular expressions

* Workaround to keep compatibility with current tests

---------

Co-authored-by: Jose Miguel Guzman <jmguzman@whitestack.com>
Co-authored-by: Kelly Brazil <kellyjonbrazil@gmail.com>
2023-08-25 10:31:17 -07:00
Kelly Brazil
5205154aaf add percent_wait to float conversion 2023-08-21 17:18:07 -07:00
Kelly Brazil
f500de3af6 version bump 2023-08-21 17:17:34 -07:00
Kian-Meng Ang
4b028b5080 Fix typos (#440)
Found via `codespell -S ./tests/fixtures -L chage,respons,astroid,unx,ist,technik,ans,buildd`
2023-07-31 08:45:03 -07:00
Kelly Brazil
4cd721be85 Dev v1.23.4 (#439)
* version bump

* fix regex for crlf line endings

* Completed Ip_route parser (#429)

* tests

* Merge pull request #398 from kellyjonbrazil/dev

Dev v1.23.2

* Merge pull request #398 from kellyjonbrazil/dev

Dev v1.23.2

---------

Co-authored-by: Kelly Brazil <kellyjonbrazil@gmail.com>
Co-authored-by: Jjack3032 <julian.jackson@parsons.us>

* formatting

* doc update

* use splitlines

* formatting

* formatting

* Parser for `find` linux command (#434)

* Added find parser and tests for Centos 7.7 and Ubuntu 18.04

* Added a test file, changed logic, and included a case for permission denied returned by find.

* Added a few more lines to the tests

* Changed logic for setting values to null and updated test cases.

* doc update

* doc update

* Added proc_net_tcp parser (#421)

Co-authored-by: Kelly Brazil <kellyjonbrazil@gmail.com>

* clean up net_tcp parser

* add resolve.conf test files

* doc update

* add resolve.conf parser

* doc update

* add sortlist functionality

* add resolve.conf parser tests

* doc update

---------

Co-authored-by: Julian5555 <58196809+Julian5555@users.noreply.github.com>
Co-authored-by: Jjack3032 <julian.jackson@parsons.us>
Co-authored-by: solomonleang <124934439+solomonleang@users.noreply.github.com>
Co-authored-by: AlvinSolomon <41175627+AlvinSolomon@users.noreply.github.com>
2023-07-30 10:08:39 -07:00
Kelly Brazil
d58ca402a7 Merge pull request #432 from kellyjonbrazil/revert-430-find_parser
Revert "Added find parser and tests for Centos 7.7 and Ubuntu 18.04"
2023-06-23 15:40:06 +00:00
Kelly Brazil
5386879040 Revert "Added find parser and tests for Centos 7.7 and Ubuntu 18.04 (#430)"
This reverts commit f19a1f23a9.
2023-06-23 08:39:12 -07:00
solomonleang
f19a1f23a9 Added find parser and tests for Centos 7.7 and Ubuntu 18.04 (#430)
* Added find parser and tests for Centos 7.7 and Ubuntu 18.04

* Added a test file, changed logic, and included a case for permission denied returned by find.

* Added a few more lines to the tests

* Changed logic for setting values to null and updated test cases.
2023-06-23 08:38:56 -07:00
Kelly Brazil
5023e5be4c Dev v1.23.3 (#426)
* make certificate search more robust to different line endings

* use license_files instead of license_file which is deprecated

* version bump

* parsing extra options -e, -o, -p

* fix for extra opts and different field length at option -[aeop]

* test integration for extra opts -e -o -p

* formatting and use ast.literal_eval instead of eval

* doc update

* doc update

* Add a parser to parse mounted encrypted veracrypt volumes (fixes #403)

* update compatibility warning message

* netstat windows parser

* tests

* Windows route parser

* tests

* id should be a string

* add veracrypt parser and docs

* formatting

* doc update

* lsattr parser

* Update test_lsattr.py

* changed keys to lowercase

* changed info

* support missing data for stat

* doc update

* doc update

* doc update

* ensure compatibility warning prints even with no data

* improve compatibility message

* add support for dig +nsid option

* New parser: srt (#415)

* srt parser

* changed the parser to support more complex cases

* doc updates

* Adding certificate request parser (#416)

* Adding certificate request parser

* Adding the CSR type for Windows-style CSR

---------

Co-authored-by: Stg22 <stephane.for.test@gmail.com>

* doc update

* add csr tests

* Last -x (#422)

* Refactored the parser

* last -x support

* doc update

* fix for ping on linux with missing hostname

* allow less strict email decoding with a warning.

* doc update

* use explicit ascii decode with backslashreplace

* doc update

* use jc warning function instead of print for warning message

* last -x shutdown fix (#423)

* inject quiet setting into asn1crypto library

* Parse appearance and modalias lines for mouse devices (fixes #419) (#425)

The bluetoothctl device parser is implemented so that it aborts the parsing
process immediately returning what it has collected so far. This is because
the parser should work in hydrid way to support outputs comming from bluetoothctl
devices and bluetoothctl info calls.

* doc update

* doc update

---------

Co-authored-by: gerd <gerd.augstein@gmail.com>
Co-authored-by: Jake Ob <iakopap@gmail.com>
Co-authored-by: Mevaser <mevaser.rotner@gmail.com>
Co-authored-by: M.R <69431152+YeahItsMeAgain@users.noreply.github.com>
Co-authored-by: Stg22 <46686290+Stg22@users.noreply.github.com>
Co-authored-by: Stg22 <stephane.for.test@gmail.com>
2023-06-21 15:48:23 -07:00
Kelly Brazil
5527d22459 Merge pull request #398 from kellyjonbrazil/dev
Dev v1.23.2
2023-04-30 10:40:21 -07:00
Kelly Brazil
9d567c2e70 python 3.6 fix 2023-04-30 10:13:00 -07:00
Kelly Brazil
39c03a15d5 doc update 2023-04-30 09:34:22 -07:00
Kelly Brazil
5e6d2562f9 add certbot command parser 2023-04-29 16:34:23 -07:00
Kelly Brazil
7009d5a014 fix for incorrect insertion and deletion parsing 2023-04-22 14:04:00 -07:00
Kelly Brazil
42f9ddabb9 change bluetoothctl compatibility 2023-04-21 14:22:23 -07:00
Kelly Brazil
30efb5afc0 fix for multiline description 2023-04-18 11:23:49 -07:00
Kelly Brazil
786dc76c09 fix for battery not charging message 2023-04-17 18:12:57 -07:00
Kelly Brazil
fc48874a5d pytest warning fixes 2023-04-17 15:15:40 -07:00
Kelly Brazil
67164e7b23 fix mypy issues 2023-04-16 18:50:40 -07:00
Kelly Brazil
3db9774ac6 doc update 2023-04-16 11:46:21 -07:00
Kelly Brazil
03aef93d9a Merge pull request #392 from tzeikob/bluetoothctl-parser
Add parser for the bluetoothctl utility
2023-04-16 11:39:54 -07:00
Jake Ob
9b7e3de3ed Fix broken unit tests for the bluetoothctl parser 2023-04-16 20:52:29 +03:00
Jake Ob
291b6b061a Parse numeric fields to their actual numeric value 2023-04-16 20:42:48 +03:00
Jake Ob
08496533e2 Process data only if data is given 2023-04-16 20:19:15 +03:00
Jake Ob
f8dceb5046 Fix minor typo in documentation 2023-04-16 20:15:24 +03:00
Jake Ob
240ed4047f Add parser for the bluetoothctl utility 2023-04-14 18:23:45 +03:00
Kelly Brazil
0bf6f7cd7c fix incorrect variable parsing when wildcard schedule 2023-04-02 13:18:16 -07:00
Kelly Brazil
313bd86e3e doc update 2023-04-02 12:57:56 -07:00
Kelly Brazil
56259d5605 fix for dashes in name 2023-04-02 12:57:50 -07:00
Kelly Brazil
8a4885c1fe Merge pull request #384 from six3six/patch-1
Fix iwconfig re_interface regex
2023-03-31 08:40:00 -07:00
Louis Desplanche
5b06f84917 Fix iwconfig re_interface regex
Small fix of re_interface in iwconfig.py.
When the network SSID contains a "-" character, the network interface was ignored.
2023-03-31 11:15:31 +02:00
Kelly Brazil
37a1428914 Merge pull request #381 from kellyjonbrazil/dev
Dev v1.23.1
2023-03-23 20:16:11 -04:00
Kelly Brazil
bff065daf3 doc update 2023-03-23 16:41:50 -07:00
Kelly Brazil
c60d899f31 ignore non-parser-plugin python files in plugin directory 2023-03-23 16:40:13 -07:00
Kelly Brazil
fb7c390506 add lsusb tests 2023-03-23 06:43:06 -07:00
Kelly Brazil
22afb69573 doc update 2023-03-22 16:47:45 -07:00
Kelly Brazil
be51304c9c add support for CDC MBIM and CDC MBIM Extended 2023-03-22 16:43:57 -07:00
Kelly Brazil
44f83d800f doc update 2023-03-19 12:49:25 -07:00
Kelly Brazil
bf07973d90 doc update 2023-03-19 12:39:28 -07:00
Kelly Brazil
11e94b686c mypy fix 2023-03-18 15:47:19 -07:00
Kelly Brazil
c68bf674a1 add py.typed file to wheel 2023-03-18 15:43:22 -07:00
Kelly Brazil
dbbc310082 Merge pull request #378 from jwilk-forks/proc-status-umask
Make umask optional in /proc/PID/status
2023-03-18 22:37:13 +00:00
Kelly Brazil
e861f4a597 Merge pull request #379 from jwilk-forks/spelling
Fix typo
2023-03-18 22:25:21 +00:00
Kelly Brazil
049e93707c doc update 2023-03-18 15:16:02 -07:00
Kelly Brazil
164294ecb7 add integer and float conversions 2023-03-18 15:14:59 -07:00
Jakub Wilk
22ef489795 fix typo 2023-03-18 11:07:15 +01:00
Jakub Wilk
8f9d650f6c make umask optional in /proc/PID/status
Zombie processes don't have the umask field available.
2023-03-18 10:28:05 +01:00
Kelly Brazil
7e134a63bd add support for the timesync-status option 2023-03-15 06:58:31 -07:00
Kelly Brazil
9b5c25cb5b fix for rtc configured as UTC 2023-03-13 19:47:38 -07:00
Kelly Brazil
894946b207 doc update 2023-03-05 10:07:23 -08:00
Kelly Brazil
92ad2068db version bump 2023-03-05 10:06:54 -08:00
Kelly Brazil
59662a1500 fix for lines that start with tab 2023-03-05 10:04:00 -08:00
Kelly Brazil
125b88a2ca Merge pull request #371 from kellyjonbrazil/master
sync to dev
2023-03-05 17:59:09 +00:00
Kelly Brazil
79fce8c769 doc update 2023-02-27 17:37:49 -08:00
Kelly Brazil
a4a53f8b3b Merge pull request #368 from kellyjonbrazil/dev
Dev v1.23.0
2023-02-27 15:13:57 -08:00
Kelly Brazil
743e1ae90f doc update 2023-02-27 15:01:00 -08:00
Kelly Brazil
dbcff80907 add zpool tests 2023-02-27 13:44:16 -08:00
Kelly Brazil
6e10965aed doc update 2023-02-21 17:19:17 -08:00
Kelly Brazil
0c8c4a9c53 formatting 2023-02-21 17:18:24 -08:00
Kelly Brazil
7632541a1e Merge pull request #365 from tzeikob/xrandr-edid-364
Modify the xrandr parser to extract display model info from EDID #364
2023-02-21 17:04:46 -08:00
Kelly Brazil
a213ad9a85 doc update 2023-02-21 15:50:09 -08:00
Kelly Brazil
15ac5a9004 fix for crontab files with only shortcut entries 2023-02-21 15:44:55 -08:00
Jake Ob
0658668eb0 Fix broken TypedDict compatibility with older versions of python
By missing to declare the Model as a generic dictionary type we cause
the script to fail because the TypedDict is not supported by older
versions of python (e.g. 3.6, 3.7).
2023-02-21 14:43:10 +02:00
Kelly Brazil
ccef69ac37 add ubuntu16 tests 2023-02-20 12:04:45 -08:00
Kelly Brazil
64676fda2e fix for older linux style output 2023-02-20 11:56:20 -08:00
Jake Ob
6ea2d776ae Add unit tests for xrandr --properties outputs 2023-02-17 16:25:41 +02:00
Jake Ob
98ced9616c Modify xrandr parser to extract model info from EDID (#364)
This change will make the xrandr parser to work also with outputs from
`xrandr --properties`, where the option `--properties` enables xrandr
to return the EDID data of each output device. The parser is expected
to remain backward compatible for regular xrandr outputs. The extra info
that will be added to every device object is:

```json
{
  "model_name": "str",
  "product_id": "str",
  "serial_number": "str"
}
```

Because the EDID data come encoded in a hexadecimal string, we have to
decode them by vendoring the pyedid (https://github.com/jojonas/pyedid) library.

Fixes: #364
2023-02-17 16:09:50 +02:00
Kelly Brazil
ae19183803 add test for is_current fix 2023-02-13 18:17:14 -08:00
Kelly Brazil
8536514baf add test for output with extra spaces that caused infinite loops 2023-02-13 18:09:10 -08:00
Kelly Brazil
81982a9f79 doc updagte 2023-02-12 18:30:31 -08:00
Kelly Brazil
1acbb2f096 Merge pull request #361 from tzeikob/reflect-360
Move the reflect value in its own key (xrandr) #360
2023-02-12 18:28:37 -08:00
Jake Ob
80fb4d40a5 Add unit test to assert devices in reflect mode 2023-02-09 21:22:34 +02:00
Kelly Brazil
c972dd1aac Merge pull request #362 from jpmat296/master
Improve accepted formats in proc_pid_smaps
2023-02-09 09:51:26 -08:00
Jean-Pierre Matsumoto
9dde65c25c Improve accepted formats in proc_pid_smaps 2023-02-07 14:57:15 +01:00
Jake Ob
7486b0c7cd Move the reflect value in its own key (xrandr)
Currently the reflect value of a device is included in the rotation
key as a combination of both rotation and reflection (e.g. normal X axis).
With this modification we move the reflection value to its own key in the
JSON document like so:

```json
{
  ...
  "rotation": "...",
  "reflection": "...,
  ...
}
```

Fixes: issue #360
2023-02-07 13:02:21 +02:00
Kelly Brazil
5c7a520a0b formatting 2023-02-05 13:04:32 -08:00
Kelly Brazil
193ddf71f8 fix typos 2023-02-05 09:52:59 -08:00
Kelly Brazil
c46fe9816c doc update 2023-02-05 09:49:32 -08:00
Kelly Brazil
aada5f0794 add int conversions 2023-02-04 19:04:43 -08:00
Kelly Brazil
5c7bf363a6 add schema and docs 2023-02-04 18:51:31 -08:00
Kelly Brazil
00b74be540 version bump 2023-02-04 17:37:56 -08:00
Kelly Brazil
96cb01f57a fix zpool-status for multi-line fields 2023-02-04 17:37:48 -08:00
Kelly Brazil
adf5f403ae regex fix for infinite loop? 2023-02-04 14:37:03 -08:00
Kelly Brazil
1c09c95c71 doc update 2023-02-04 11:26:41 -08:00
Kelly Brazil
64f442d743 fix for is_current? 2023-02-04 11:20:48 -08:00
Kelly Brazil
cd8d43446b add zpool-status parser 2023-02-02 15:57:15 -08:00
Kelly Brazil
7361eac1a4 formatting 2023-01-31 17:02:04 -08:00
Kelly Brazil
9d41f0a938 doc update 2023-01-31 17:00:42 -08:00
Kelly Brazil
00274c15df add process conversions 2023-01-31 16:52:56 -08:00
Kelly Brazil
d0b8a91f94 add zpool-iostat parser 2023-01-31 11:59:19 -08:00
Kelly Brazil
098e8dbef6 doc update 2023-01-31 11:59:06 -08:00
Kelly Brazil
ec29b8bbc6 add test for acpi fix for never fully discharge state 2023-01-31 09:51:23 -08:00
Kelly Brazil
1d8f83b8c6 add ver parser tests 2023-01-31 09:44:23 -08:00
Kelly Brazil
12c4419c6a doc update 2023-01-31 08:30:37 -08:00
Kelly Brazil
b134c53f33 add ssh_conf tests 2023-01-31 08:29:43 -08:00
Kelly Brazil
23ff19fdb5 add slicer tests 2023-01-31 06:48:37 -08:00
Kelly Brazil
7a43ba631b fix tests for slice info metadata 2023-01-30 08:59:54 -08:00
Kelly Brazil
2beb26f3e3 add slice info to Metadata output 2023-01-30 08:52:22 -08:00
Kelly Brazil
4b55f49e99 formatting 2023-01-30 08:11:21 -08:00
Kelly Brazil
c0239a771c clarify parser argument quote wrapping code 2023-01-27 16:28:55 -08:00
Kelly Brazil
bc4738e900 don't quote spaces in command arguments 2023-01-27 15:58:01 -08:00
Kelly Brazil
1c76caf59b formatting 2023-01-27 15:22:36 -08:00
Kelly Brazil
0679951d4a add <nobr> tag 2023-01-27 15:17:41 -08:00
Kelly Brazil
df7aee9e4b doc update 2023-01-27 14:47:08 -08:00
Kelly Brazil
a8d97a2521 add slice info to help and doc update 2023-01-27 11:41:21 -08:00
Kelly Brazil
9370b336d8 add slice to man page 2023-01-27 11:31:16 -08:00
Kelly Brazil
118f98222a change restapi link 2023-01-27 08:15:53 -08:00
Kelly Brazil
dc997821f4 ad jc-api demo link 2023-01-27 08:12:07 -08:00
Kelly Brazil
aef0fdb435 formatting 2023-01-27 08:02:07 -08:00
Kelly Brazil
21ee3c0e78 clarify slice usage 2023-01-27 08:01:32 -08:00
Kelly Brazil
d6c665f74b add both negative slice description 2023-01-26 17:06:07 -08:00
Kelly Brazil
79305a50d0 formatting 2023-01-26 16:59:15 -08:00
Kelly Brazil
e758aa41ef formatting 2023-01-26 16:49:27 -08:00
Kelly Brazil
910cb34b09 formatting 2023-01-26 16:49:09 -08:00
Kelly Brazil
976fea4eb2 Formatting 2023-01-26 16:46:54 -08:00
Kelly Brazil
81447ec9e6 formatting 2023-01-26 16:44:26 -08:00
Kelly Brazil
61b8e9f7b5 doc update 2023-01-26 16:24:32 -08:00
Kelly Brazil
8e86a04448 add slicing info 2023-01-26 16:18:54 -08:00
Kelly Brazil
f23715a783 formatting 2023-01-26 08:53:50 -08:00
Kelly Brazil
fa693a8bb1 formatting 2023-01-26 08:53:06 -08:00
Kelly Brazil
103168c25b add integer conversions 2023-01-26 08:48:59 -08:00
Kelly Brazil
ae7ed4eec5 doc update 2023-01-26 08:48:46 -08:00
Kelly Brazil
caa516db72 formatting 2023-01-25 16:59:49 -08:00
Kelly Brazil
f05c3d6113 docstring update 2023-01-25 16:57:15 -08:00
Kelly Brazil
5d872b1535 docstring update: add see also section. 2023-01-25 16:30:20 -08:00
Kelly Brazil
1559cd9f5c fixup patch numbers 2023-01-25 16:13:33 -08:00
Kelly Brazil
a5a87c7da1 add ver parser 2023-01-25 15:58:27 -08:00
Kelly Brazil
0e6cec62c1 remove unneeded slicer syntax check 2023-01-23 16:53:06 -08:00
Kelly Brazil
3bb1d89165 fix multiple slicer warnings 2023-01-23 16:50:41 -08:00
Kelly Brazil
f5f5f102fb full functioning slicer feature 2023-01-23 16:13:17 -08:00
Kelly Brazil
0c82fe7e4d add slicer functionality 2023-01-23 13:57:48 -08:00
Kelly Brazil
13ffe8a84d doc update 2023-01-22 12:44:16 -08:00
Kelly Brazil
4c6eebaa33 doc update 2023-01-22 10:37:27 -08:00
Kelly Brazil
f8fbb2dce2 use derivative of sshd_conf parser instead of paramiko 2023-01-22 10:36:29 -08:00
Kelly Brazil
dbfe682674 fix for never fully discharging state 2023-01-22 08:57:50 -08:00
Kelly Brazil
2a148d44a1 add host to objects 2023-01-21 17:00:22 -08:00
Kelly Brazil
0648d2e9e3 don't interpolate local host values 2023-01-21 16:37:27 -08:00
Kelly Brazil
ac9128fa0c add ssh-conf parser 2023-01-21 13:10:45 -08:00
Kelly Brazil
4d7a872670 version bump 2023-01-21 10:56:45 -08:00
Kelly Brazil
07f716b254 Merge pull request #355 from kellyjonbrazil/master
sync to dev
2023-01-21 10:53:03 -08:00
379 changed files with 17473 additions and 1179 deletions

View File

@@ -1,5 +1,77 @@
jc changelog
20231021 v1.23.5
- Add `host` command parser
- Add `nsd-control` command parser
- Add `lsb_release` command parser
- Add `/etc/os-release` file parser
- Enhance `env` command parser to support multi-line values
- Enhance `ping` and `ping-s` parsers to add error and corrupted support
- Enhance `xml` parser to include comments in the JSON output
- Fix `pidstat` command parser when using `-T ALL`
- Fix `x509-cert` parser to allow negative serial numbers
- Fix `x509-cert` parser for cases when bitstrings are larger than standard
- Fix `xrandr` command parser for associated device issues
- Fix error when pygments library is not installed
20230730 v1.23.4
- Add `/etc/resolve.conf` file parser
- Add `/proc/net/tcp` and `/proc/net/tcp6` file parser
- Add `find` command parser
- Add `ip route` command parser
- Fix `certbot` command parser to be more robust with different line endings
20230621 v1.23.3
- Add `lsattr` command parser
- Add `srt` file parser
- Add `veracrypt` command parser
- Add X509 Certificate Request file parser
- Enhance X509 Certificate parser to allow non-compliant email addresses with a warning
- Enhance `dig` command parser to support the `+nsid` option
- Enhance `last` and `lastb` command parser to support the `-x` option
- Enhance `route` command parser to add Windows support
- Enhnace `netstat` command parser to add Windows support
- Enhance `ss` command parser to support extended options
- Enhance the compatibility warning message
- Fix `bluetoothctl` command parser for some mouse devices
- Fix `ping` command parsers for output with missing hostname
- Fix `stat` command parser for older versions that may not contain all fields
- Fix deprecated option in `setup.cfg`
20230429 v1.23.2
- Add `bluetoothctl` command parser
- Add `certbot` command parser for `certificates` and `show_account` options
- Fix `acpi` command parser for "Not charging" battery status lines
- Fix `iwconfig` command parser for SSIDs with dashes in the name
- Fix `crontab` command parsers for incorrect variable parsing in some cases
- Fix `git-log` and `git-log-s` command parsers for incorrect insertion/deletion parsing
- Fix `ufw-appinfo` command parser for parsing errors on multiline description fields
- Fix pytest warnings
20230323 v1.23.1
- Fix `zpool-status` command parser for lines that start with tab
- Fix `timedatectl` command parser when RTC set to local
- Fix to ensure `py.typed` file is included in the package wheel
- Fix `lsusb` command parser to support CDC MBIM and CDC MBIM Extended fields
- Add support for the `timesync-status` for the `timedatectl` command parser
- Fix to ignore non-parser-plugins in the parser plugin directory
20230227 v1.23.0
- Add input slicing as a `jc` command-line option
- Add `ssh` configuration file parser
- Add `ver` Version string parser
- Add `zpool iostat` command parser
- Add `zpool status` command parser
- Fix `acpi` command parser for "will never fully discharge" battery state
- Fix `crontab` and `crontab-u` command and file parsers for cases where only
shortcut schedule items exist
- Fix `ifconfig` command parser for older-style linux output
- Fix `xrandr` command parser for proper `is_current` output
- Fix `xrandr` command parser for infinite loop with some device configurations
- Add `reflection` key to `xrandr` parser schema
- Add display model info from EDID to `xrandr` parser
- Add `MPX-specific VMA` support for VM Flags in `/proc/<pid>/smaps` parser
20230111 v1.22.5
- Add TOML file parser
- Add INI with duplicate key support file parser

View File

@@ -4551,6 +4551,57 @@ cat entrust.pem | jc --x509-cert -p
}
]
```
### X.509 PEM and DER certificate request files
```bash
cat myserver.csr | jc --x509-csr -p
```
```json
[
{
"certification_request_info": {
"version": "v1",
"subject": {
"common_name": "myserver.for.example"
},
"subject_pk_info": {
"algorithm": {
"algorithm": "ec",
"parameters": "secp256r1"
},
"public_key": "04:40:33:c0:91:8f:e9:46:ea:d0:dc:d0:f9:63:2c:a4:35:1f:0f:54:c8:a9:9b:e3:9e:d4:f3:64:b8:60:cc:7f:39:75:dd:a7:61:31:02:7c:9e:89:c6:db:45:15:f2:5f:b0:65:29:0b:42:d2:6e:c2:ea:a6:23:bd:fc:65:e5:7d:4e"
},
"attributes": [
{
"type": "extension_request",
"values": [
[
{
"extn_id": "extended_key_usage",
"critical": false,
"extn_value": [
"server_auth"
]
},
{
"extn_id": "subject_alt_name",
"critical": false,
"extn_value": [
"myserver.for.example"
]
}
]
]
}
]
},
"signature_algorithm": {
"algorithm": "sha384_ecdsa",
"parameters": null
},
"signature": "30:45:02:20:77:ac:5b:51:bf:c5:f5:43:02:52:ae:66:8a:fe:95:98:98:98:a9:45:34:31:08:ff:2c:cc:92:d9:1c:70:28:74:02:21:00:97:79:7b:e7:45:18:76:cf:d7:3b:79:34:56:d2:69:b5:73:41:9b:8a:b7:ad:ec:80:23:c1:2f:64:da:e5:28:19"
}
]
```
### XML files
```bash
cat cd_catalog.xml

365
README.md
View File

@@ -3,13 +3,15 @@
> Check out the `jc` Python [package documentation](https://github.com/kellyjonbrazil/jc/tree/master/docs) for developers
> Try the `jc` [web demo](https://jc-web.onrender.com/)
> Try the `jc` [web demo](https://jc-web.onrender.com/) and [REST API](https://github.com/kellyjonbrazil/jc-restapi)
> JC is [now available](https://galaxy.ansible.com/community/general) as an
> `jc` is [now available](https://galaxy.ansible.com/community/general) as an
Ansible filter plugin in the `community.general` collection. See this
[blog post](https://blog.kellybrazil.com/2020/08/30/parsing-command-output-in-ansible-with-jc/)
for an example.
> Looking for something like `jc` but lower-level? Check out [regex2json](https://gitlab.com/tozd/regex2json).
# JC
JSON Convert
@@ -44,8 +46,8 @@ $ jc dig example.com | jq -r '.[].answer[].data'
93.184.216.34
```
`jc` can also be used as a python library. In this case the output will be
a python dictionary, a list of dictionaries, or even a
`jc` can also be used as a python library. In this case the returned value
will be a python dictionary, a list of dictionaries, or even a
[lazy iterable of dictionaries](#using-streaming-parsers-as-python-modules)
instead of JSON:
```python
@@ -133,9 +135,9 @@ on Github.
`jc` accepts piped input from `STDIN` and outputs a JSON representation of the
previous command's output to `STDOUT`.
```bash
COMMAND | jc [OPTIONS] PARSER
cat FILE | jc [OPTIONS] PARSER
echo STRING | jc [OPTIONS] PARSER
COMMAND | jc [SLICE] [OPTIONS] PARSER
cat FILE | jc [SLICE] [OPTIONS] PARSER
echo STRING | jc [SLICE] [OPTIONS] PARSER
```
Alternatively, the "magic" syntax can be used by prepending `jc` to the command
@@ -143,8 +145,8 @@ to be converted or in front of the absolute path for Proc files. Options can be
passed to `jc` immediately before the command or Proc file path is given.
(Note: command aliases and shell builtins are not supported)
```bash
jc [OPTIONS] COMMAND
jc [OPTIONS] /proc/<path-to-procfile>
jc [SLICE] [OPTIONS] COMMAND
jc [SLICE] [OPTIONS] /proc/<path-to-procfile>
```
The JSON output can be compact (default) or pretty formatted with the `-p`
@@ -154,143 +156,160 @@ option.
| Argument | Command or Filetype | Documentation |
|-------------------|---------------------------------------------------------|----------------------------------------------------------------------------|
| ` --acpi` | `acpi` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/acpi) |
| ` --airport` | `airport -I` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/airport) |
| ` --airport-s` | `airport -s` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/airport_s) |
| ` --arp` | `arp` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/arp) |
| ` --asciitable` | ASCII and Unicode table parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/asciitable) |
| ` --asciitable-m` | multi-line ASCII and Unicode table parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/asciitable_m) |
| ` --blkid` | `blkid` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/blkid) |
| ` --cbt` | `cbt` (Google Bigtable) command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/cbt) |
| ` --cef` | CEF string parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/cef) |
| ` --cef-s` | CEF string streaming parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/cef_s) |
| ` --chage` | `chage --list` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/chage) |
| ` --cksum` | `cksum` and `sum` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/cksum) |
| ` --clf` | Common and Combined Log Format file parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/clf) |
| ` --clf-s` | Common and Combined Log Format file streaming parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/clf_s) |
| ` --crontab` | `crontab` command and file parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/crontab) |
| ` --crontab-u` | `crontab` file parser with user support | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/crontab_u) |
| ` --csv` | CSV file parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/csv) |
| ` --csv-s` | CSV file streaming parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/csv_s) |
| ` --date` | `date` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/date) |
| ` --datetime-iso` | ISO 8601 Datetime string parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/datetime_iso) |
| ` --df` | `df` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/df) |
| ` --dig` | `dig` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/dig) |
| ` --dir` | `dir` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/dir) |
| ` --dmidecode` | `dmidecode` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/dmidecode) |
| ` --dpkg-l` | `dpkg -l` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/dpkg_l) |
| ` --du` | `du` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/du) |
| `--acpi` | `acpi` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/acpi) |
| `--airport` | `airport -I` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/airport) |
| `--airport-s` | `airport -s` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/airport_s) |
| `--arp` | `arp` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/arp) |
| `--asciitable` | ASCII and Unicode table parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/asciitable) |
| `--asciitable-m` | multi-line ASCII and Unicode table parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/asciitable_m) |
| `--blkid` | `blkid` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/blkid) |
| `--bluetoothctl` | `bluetoothctl` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/bluetoothctl) |
| `--cbt` | `cbt` (Google Bigtable) command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/cbt) |
| `--cef` | CEF string parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/cef) |
| `--cef-s` | CEF string streaming parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/cef_s) |
| `--certbot` | `certbot` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/certbot) |
| `--chage` | `chage --list` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/chage) |
| `--cksum` | `cksum` and `sum` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/cksum) |
| `--clf` | Common and Combined Log Format file parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/clf) |
| `--clf-s` | Common and Combined Log Format file streaming parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/clf_s) |
| `--crontab` | `crontab` command and file parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/crontab) |
| `--crontab-u` | `crontab` file parser with user support | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/crontab_u) |
| `--csv` | CSV file parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/csv) |
| `--csv-s` | CSV file streaming parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/csv_s) |
| `--date` | `date` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/date) |
| `--datetime-iso` | ISO 8601 Datetime string parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/datetime_iso) |
| `--df` | `df` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/df) |
| `--dig` | `dig` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/dig) |
| `--dir` | `dir` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/dir) |
| `--dmidecode` | `dmidecode` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/dmidecode) |
| `--dpkg-l` | `dpkg -l` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/dpkg_l) |
| `--du` | `du` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/du) |
| `--email-address` | Email Address string parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/email_address) |
| ` --env` | `env` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/env) |
| ` --file` | `file` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/file) |
| ` --findmnt` | `findmnt` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/findmnt) |
| ` --finger` | `finger` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/finger) |
| ` --free` | `free` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/free) |
| ` --fstab` | `/etc/fstab` file parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/fstab) |
| ` --git-log` | `git log` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/git_log) |
| ` --git-log-s` | `git log` command streaming parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/git_log_s) |
| `--env` | `env` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/env) |
| `--file` | `file` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/file) |
| `--find` | `find` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/find) |
| `--findmnt` | `findmnt` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/findmnt) |
| `--finger` | `finger` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/finger) |
| `--free` | `free` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/free) |
| `--fstab` | `/etc/fstab` file parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/fstab) |
| `--git-log` | `git log` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/git_log) |
| `--git-log-s` | `git log` command streaming parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/git_log_s) |
| `--git-ls-remote` | `git ls-remote` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/git_ls_remote) |
| ` --gpg` | `gpg --with-colons` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/gpg) |
| ` --group` | `/etc/group` file parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/group) |
| ` --gshadow` | `/etc/gshadow` file parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/gshadow) |
| ` --hash` | `hash` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/hash) |
| ` --hashsum` | hashsum command parser (`md5sum`, `shasum`, etc.) | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/hashsum) |
| ` --hciconfig` | `hciconfig` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/hciconfig) |
| ` --history` | `history` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/history) |
| ` --hosts` | `/etc/hosts` file parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/hosts) |
| ` --id` | `id` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/id) |
| ` --ifconfig` | `ifconfig` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/ifconfig) |
| ` --ini` | INI file parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/ini) |
| ` --ini-dup` | INI with duplicate key file parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/ini_dup) |
| ` --iostat` | `iostat` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/iostat) |
| ` --iostat-s` | `iostat` command streaming parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/iostat_s) |
| ` --ip-address` | IPv4 and IPv6 Address string parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/ip_address) |
| ` --iptables` | `iptables` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/iptables) |
| ` --iw-scan` | `iw dev [device] scan` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/iw_scan) |
| ` --iwconfig` | `iwconfig` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/iwconfig) |
| ` --jar-manifest` | Java MANIFEST.MF file parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/jar_manifest) |
| ` --jobs` | `jobs` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/jobs) |
| ` --jwt` | JWT string parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/jwt) |
| ` --kv` | Key/Value file and string parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/kv) |
| ` --last` | `last` and `lastb` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/last) |
| ` --ls` | `ls` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/ls) |
| ` --ls-s` | `ls` command streaming parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/ls_s) |
| ` --lsblk` | `lsblk` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/lsblk) |
| ` --lsmod` | `lsmod` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/lsmod) |
| ` --lsof` | `lsof` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/lsof) |
| ` --lspci` | `lspci -mmv` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/lspci) |
| ` --lsusb` | `lsusb` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/lsusb) |
| ` --m3u` | M3U and M3U8 file parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/m3u) |
| ` --mdadm` | `mdadm` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/mdadm) |
| ` --mount` | `mount` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/mount) |
| ` --mpstat` | `mpstat` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/mpstat) |
| ` --mpstat-s` | `mpstat` command streaming parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/mpstat_s) |
| ` --netstat` | `netstat` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/netstat) |
| ` --nmcli` | `nmcli` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/nmcli) |
| ` --ntpq` | `ntpq -p` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/ntpq) |
| ` --openvpn` | openvpn-status.log file parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/openvpn) |
| ` --os-prober` | `os-prober` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/os_prober) |
| ` --passwd` | `/etc/passwd` file parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/passwd) |
| ` --pci-ids` | `pci.ids` file parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/pci_ids) |
| ` --pgpass` | PostgreSQL password file parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/pgpass) |
| ` --pidstat` | `pidstat -H` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/pidstat) |
| ` --pidstat-s` | `pidstat -H` command streaming parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/pidstat_s) |
| ` --ping` | `ping` and `ping6` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/ping) |
| ` --ping-s` | `ping` and `ping6` command streaming parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/ping_s) |
| ` --pip-list` | `pip list` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/pip_list) |
| ` --pip-show` | `pip show` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/pip_show) |
| ` --plist` | PLIST file parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/plist) |
| ` --postconf` | `postconf -M` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/postconf) |
| ` --proc` | `/proc/` file parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/proc) |
| ` --ps` | `ps` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/ps) |
| ` --route` | `route` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/route) |
| ` --rpm-qi` | `rpm -qi` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/rpm_qi) |
| ` --rsync` | `rsync` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/rsync) |
| ` --rsync-s` | `rsync` command streaming parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/rsync_s) |
| ` --semver` | Semantic Version string parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/semver) |
| ` --sfdisk` | `sfdisk` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/sfdisk) |
| ` --shadow` | `/etc/shadow` file parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/shadow) |
| ` --ss` | `ss` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/ss) |
| ` --sshd-conf` | sshd config file and `sshd -T` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/sshd_conf) |
| ` --stat` | `stat` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/stat) |
| ` --stat-s` | `stat` command streaming parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/stat_s) |
| ` --sysctl` | `sysctl` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/sysctl) |
| ` --syslog` | Syslog RFC 5424 string parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/syslog) |
| ` --syslog-s` | Syslog RFC 5424 string streaming parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/syslog_s) |
| ` --syslog-bsd` | Syslog RFC 3164 string parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/syslog_bsd) |
| ` --syslog-bsd-s` | Syslog RFC 3164 string streaming parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/syslog_bsd_s) |
| ` --systemctl` | `systemctl` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/systemctl) |
| ` --systemctl-lj` | `systemctl list-jobs` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/systemctl_lj) |
| ` --systemctl-ls` | `systemctl list-sockets` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/systemctl_ls) |
| `--gpg` | `gpg --with-colons` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/gpg) |
| `--group` | `/etc/group` file parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/group) |
| `--gshadow` | `/etc/gshadow` file parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/gshadow) |
| `--hash` | `hash` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/hash) |
| `--hashsum` | hashsum command parser (`md5sum`, `shasum`, etc.) | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/hashsum) |
| `--hciconfig` | `hciconfig` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/hciconfig) |
| `--history` | `history` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/history) |
| `--host` | `host` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/host) |
| `--hosts` | `/etc/hosts` file parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/hosts) |
| `--id` | `id` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/id) |
| `--ifconfig` | `ifconfig` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/ifconfig) |
| `--ini` | INI file parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/ini) |
| `--ini-dup` | INI with duplicate key file parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/ini_dup) |
| `--iostat` | `iostat` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/iostat) |
| `--iostat-s` | `iostat` command streaming parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/iostat_s) |
| `--ip-address` | IPv4 and IPv6 Address string parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/ip_address) |
| `--iptables` | `iptables` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/iptables) |
| `--ip-route` | `ip route` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/ip_route) |
| `--iw-scan` | `iw dev [device] scan` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/iw_scan) |
| `--iwconfig` | `iwconfig` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/iwconfig) |
| `--jar-manifest` | Java MANIFEST.MF file parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/jar_manifest) |
| `--jobs` | `jobs` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/jobs) |
| `--jwt` | JWT string parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/jwt) |
| `--kv` | Key/Value file and string parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/kv) |
| `--last` | `last` and `lastb` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/last) |
| `--ls` | `ls` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/ls) |
| `--ls-s` | `ls` command streaming parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/ls_s) |
| `--lsattr` | `lsattr` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/lsattr) |
| `--lsb-release` | `lsb_release` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/lsb_release) |
| `--lsblk` | `lsblk` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/lsblk) |
| `--lsmod` | `lsmod` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/lsmod) |
| `--lsof` | `lsof` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/lsof) |
| `--lspci` | `lspci -mmv` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/lspci) |
| `--lsusb` | `lsusb` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/lsusb) |
| `--m3u` | M3U and M3U8 file parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/m3u) |
| `--mdadm` | `mdadm` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/mdadm) |
| `--mount` | `mount` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/mount) |
| `--mpstat` | `mpstat` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/mpstat) |
| `--mpstat-s` | `mpstat` command streaming parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/mpstat_s) |
| `--netstat` | `netstat` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/netstat) |
| `--nmcli` | `nmcli` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/nmcli) |
| `--nsd-control` | `nsd-control` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/nsd_control) |
| `--ntpq` | `ntpq -p` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/ntpq) |
| `--openvpn` | openvpn-status.log file parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/openvpn) |
| `--os-prober` | `os-prober` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/os_prober) |
| `--os-release` | `/etc/os-release` file parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/os_release) |
| `--passwd` | `/etc/passwd` file parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/passwd) |
| `--pci-ids` | `pci.ids` file parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/pci_ids) |
| `--pgpass` | PostgreSQL password file parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/pgpass) |
| `--pidstat` | `pidstat -H` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/pidstat) |
| `--pidstat-s` | `pidstat -H` command streaming parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/pidstat_s) |
| `--ping` | `ping` and `ping6` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/ping) |
| `--ping-s` | `ping` and `ping6` command streaming parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/ping_s) |
| `--pip-list` | `pip list` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/pip_list) |
| `--pip-show` | `pip show` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/pip_show) |
| `--plist` | PLIST file parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/plist) |
| `--postconf` | `postconf -M` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/postconf) |
| `--proc` | `/proc/` file parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/proc) |
| `--ps` | `ps` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/ps) |
| `--resolve-conf` | `/etc/resolve.conf` file parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/resolve_conf) |
| `--route` | `route` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/route) |
| `--rpm-qi` | `rpm -qi` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/rpm_qi) |
| `--rsync` | `rsync` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/rsync) |
| `--rsync-s` | `rsync` command streaming parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/rsync_s) |
| `--semver` | Semantic Version string parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/semver) |
| `--sfdisk` | `sfdisk` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/sfdisk) |
| `--shadow` | `/etc/shadow` file parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/shadow) |
| `--srt` | SRT file parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/srt) |
| `--ss` | `ss` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/ss) |
| `--ssh-conf` | `ssh` config file and `ssh -G` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/ssh_conf) |
| `--sshd-conf` | `sshd` config file and `sshd -T` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/sshd_conf) |
| `--stat` | `stat` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/stat) |
| `--stat-s` | `stat` command streaming parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/stat_s) |
| `--sysctl` | `sysctl` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/sysctl) |
| `--syslog` | Syslog RFC 5424 string parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/syslog) |
| `--syslog-s` | Syslog RFC 5424 string streaming parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/syslog_s) |
| `--syslog-bsd` | Syslog RFC 3164 string parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/syslog_bsd) |
| `--syslog-bsd-s` | Syslog RFC 3164 string streaming parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/syslog_bsd_s) |
| `--systemctl` | `systemctl` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/systemctl) |
| `--systemctl-lj` | `systemctl list-jobs` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/systemctl_lj) |
| `--systemctl-ls` | `systemctl list-sockets` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/systemctl_ls) |
| `--systemctl-luf` | `systemctl list-unit-files` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/systemctl_luf) |
| ` --systeminfo` | `systeminfo` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/systeminfo) |
| ` --time` | `/usr/bin/time` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/time) |
| ` --timedatectl` | `timedatectl status` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/timedatectl) |
| ` --timestamp` | Unix Epoch Timestamp string parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/timestamp) |
| ` --toml` | TOML file parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/toml) |
| ` --top` | `top -b` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/top) |
| ` --top-s` | `top -b` command streaming parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/top_s) |
| ` --tracepath` | `tracepath` and `tracepath6` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/tracepath) |
| ` --traceroute` | `traceroute` and `traceroute6` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/traceroute) |
| ` --udevadm` | `udevadm info` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/udevadm) |
| ` --ufw` | `ufw status` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/ufw) |
| ` --ufw-appinfo` | `ufw app info [application]` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/ufw_appinfo) |
| ` --uname` | `uname -a` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/uname) |
| `--systeminfo` | `systeminfo` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/systeminfo) |
| `--time` | `/usr/bin/time` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/time) |
| `--timedatectl` | `timedatectl status` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/timedatectl) |
| `--timestamp` | Unix Epoch Timestamp string parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/timestamp) |
| `--toml` | TOML file parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/toml) |
| `--top` | `top -b` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/top) |
| `--top-s` | `top -b` command streaming parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/top_s) |
| `--tracepath` | `tracepath` and `tracepath6` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/tracepath) |
| `--traceroute` | `traceroute` and `traceroute6` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/traceroute) |
| `--udevadm` | `udevadm info` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/udevadm) |
| `--ufw` | `ufw status` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/ufw) |
| `--ufw-appinfo` | `ufw app info [application]` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/ufw_appinfo) |
| `--uname` | `uname -a` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/uname) |
| `--update-alt-gs` | `update-alternatives --get-selections` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/update_alt_gs) |
| ` --update-alt-q` | `update-alternatives --query` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/update_alt_q) |
| ` --upower` | `upower` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/upower) |
| ` --uptime` | `uptime` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/uptime) |
| ` --url` | URL string parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/url) |
| ` --vmstat` | `vmstat` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/vmstat) |
| ` --vmstat-s` | `vmstat` command streaming parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/vmstat_s) |
| ` --w` | `w` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/w) |
| ` --wc` | `wc` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/wc) |
| ` --who` | `who` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/who) |
| ` --x509-cert` | X.509 PEM and DER certificate file parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/x509_cert) |
| ` --xml` | XML file parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/xml) |
| ` --xrandr` | `xrandr` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/xrandr) |
| ` --yaml` | YAML file parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/yaml) |
| ` --zipinfo` | `zipinfo` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/zipinfo) |
| `--update-alt-q` | `update-alternatives --query` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/update_alt_q) |
| `--upower` | `upower` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/upower) |
| `--uptime` | `uptime` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/uptime) |
| `--url` | URL string parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/url) |
| `--ver` | Version string parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/ver) |
| `--veracrypt` | `veracrypt` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/veracrypt) |
| `--vmstat` | `vmstat` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/vmstat) |
| `--vmstat-s` | `vmstat` command streaming parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/vmstat_s) |
| `--w` | `w` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/w) |
| `--wc` | `wc` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/wc) |
| `--who` | `who` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/who) |
| `--x509-cert` | X.509 PEM and DER certificate file parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/x509_cert) |
| `--x509-csr` | X.509 PEM and DER certificate request file parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/x509_csr) |
| `--xml` | XML file parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/xml) |
| `--xrandr` | `xrandr` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/xrandr) |
| `--yaml` | YAML file parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/yaml) |
| `--zipinfo` | `zipinfo` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/zipinfo) |
| `--zpool-iostat` | `zpool iostat` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/zpool_iostat) |
| `--zpool-status` | `zpool status` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/zpool_status) |
### Options
@@ -311,6 +330,54 @@ option.
| `-B` | `--bash-comp` | Generate Bash shell completion script ([more info](https://github.com/kellyjonbrazil/jc/wiki/Shell-Completions)) |
| `-Z` | `--zsh-comp` | Generate Zsh shell completion script ([more info](https://github.com/kellyjonbrazil/jc/wiki/Shell-Completions)) |
### Slice
Line slicing is supported using the `START:STOP` syntax similar to Python
slicing. This allows you to skip lines at the beginning and/or end of the
`STDIN` input you would like `jc` to convert.
`START` and `STOP` can be positive or negative integers or blank and allow
you to specify how many lines to skip and how many lines to process.
Positive and blank slices are the most memory efficient. Any negative
integers in the slice will use more memory.
For example, to skip the first and last line of the following text, you
could express the slice in a couple ways:
```bash
$ cat table.txt
### We want to skip this header ###
col1 col2
foo 1
bar 2
### We want to skip this footer ###
$ cat table.txt | jc 1:-1 --asciitable
[{"col1":"foo","col2":"1"},{"col1":"bar","col2":"2"}]
$ cat table.txt | jc 1:4 --asciitable
[{"col1":"foo","col2":"1"},{"col1":"bar","col2":"2"}]
```
In this example `1:-1` and `1:4` line slices provide the same output.
When using positive integers the index location of `STOP` is non-inclusive.
Positive slices count from the first line of the input toward the end
starting at `0` as the first line. Negative slices count from the last line
toward the beginning starting at `-1` as the last line. This is also the way
[Python's slicing](https://stackoverflow.com/questions/509211/understanding-slicing)
feature works.
Here is a breakdown of line slice options:
| Slice Notation | Input Lines Processed |
|----------------|--------------------------------------------------------------|
| `START:STOP` | lines `START` through `STOP - 1` |
| `START:` | lines `START` through the rest of the output |
| `:STOP` | lines from the beginning through `STOP - 1` |
| `-START:STOP` | `START` lines from the end through `STOP - 1` |
| `START:-STOP` | lines `START` through `STOP` lines from the end |
| `-START:-STOP` | `START` lines from the end through `STOP` lines from the end |
| `-START:` | `START` lines from the end through the rest of the output |
| `:-STOP` | lines from the beginning through `STOP` lines from the end |
| `:` | all lines |
### Exit Codes
Any fatal errors within `jc` will generate an exit code of `100`, otherwise the
exit code will be `0`.
@@ -481,20 +548,22 @@ for item in result:
print(item["filename"])
```
### Custom Parsers
Custom local parser plugins may be placed in a `jc/jcparsers` folder in your
local **"App data directory"**:
### Parser Plugins
Parser plugins may be placed in a `jc/jcparsers` folder in your local
**"App data directory"**:
- Linux/unix: `$HOME/.local/share/jc/jcparsers`
- macOS: `$HOME/Library/Application Support/jc/jcparsers`
- Windows: `$LOCALAPPDATA\jc\jc\jcparsers`
Local parser plugins are standard python module files. Use the
Parser plugins are standard python module files. Use the
[`jc/parsers/foo.py`](https://github.com/kellyjonbrazil/jc/blob/master/jc/parsers/foo.py)
or [`jc/parsers/foo_s.py (streaming)`](https://github.com/kellyjonbrazil/jc/blob/master/jc/parsers/foo_s.py)
parser as a template and simply place a `.py` file in the `jcparsers` subfolder.
Any dependencies can be placed in the `jc` folder above `jcparsers` and can
be imported in the parser code.
Local plugin filenames must be valid python module names and therefore must
Parser plugin filenames must be valid python module names and therefore must
start with a letter and consist entirely of alphanumerics and underscores.
Local plugins may override default parsers.
@@ -552,7 +621,7 @@ they are run on an unsupported platform. To see all parser information,
including compatibility, run `jc -ap`.
You may still use a parser on an unsupported platform - for example, you may
want to parse a file with linux `lsof` output on an macOS or Windows laptop. In
want to parse a file with linux `lsof` output on a macOS or Windows laptop. In
that case you can suppress the warning message with the `-q` cli option or the
`quiet=True` function parameter in `parse()`:

View File

@@ -3,8 +3,8 @@ _jc()
local cur prev words cword jc_commands jc_parsers jc_options \
jc_about_options jc_about_mod_options jc_help_options jc_special_options
jc_commands=(acpi airport arp blkid cbt chage cksum crontab date df dig dmidecode dpkg du env file findmnt finger free git gpg hciconfig id ifconfig iostat iptables iw iwconfig jobs last lastb ls lsblk lsmod lsof lspci lsusb md5 md5sum mdadm mount mpstat netstat nmcli ntpq os-prober pidstat ping ping6 pip pip3 postconf printenv ps route rpm rsync sfdisk sha1sum sha224sum sha256sum sha384sum sha512sum shasum ss sshd stat sum sysctl systemctl systeminfo timedatectl top tracepath tracepath6 traceroute traceroute6 udevadm ufw uname update-alternatives upower uptime vdir vmstat w wc who xrandr zipinfo)
jc_parsers=(--acpi --airport --airport-s --arp --asciitable --asciitable-m --blkid --cbt --cef --cef-s --chage --cksum --clf --clf-s --crontab --crontab-u --csv --csv-s --date --datetime-iso --df --dig --dir --dmidecode --dpkg-l --du --email-address --env --file --findmnt --finger --free --fstab --git-log --git-log-s --git-ls-remote --gpg --group --gshadow --hash --hashsum --hciconfig --history --hosts --id --ifconfig --ini --ini-dup --iostat --iostat-s --ip-address --iptables --iw-scan --iwconfig --jar-manifest --jobs --jwt --kv --last --ls --ls-s --lsblk --lsmod --lsof --lspci --lsusb --m3u --mdadm --mount --mpstat --mpstat-s --netstat --nmcli --ntpq --openvpn --os-prober --passwd --pci-ids --pgpass --pidstat --pidstat-s --ping --ping-s --pip-list --pip-show --plist --postconf --proc --proc-buddyinfo --proc-consoles --proc-cpuinfo --proc-crypto --proc-devices --proc-diskstats --proc-filesystems --proc-interrupts --proc-iomem --proc-ioports --proc-loadavg --proc-locks --proc-meminfo --proc-modules --proc-mtrr --proc-pagetypeinfo --proc-partitions --proc-slabinfo --proc-softirqs --proc-stat --proc-swaps --proc-uptime --proc-version --proc-vmallocinfo --proc-vmstat --proc-zoneinfo --proc-driver-rtc --proc-net-arp --proc-net-dev --proc-net-dev-mcast --proc-net-if-inet6 --proc-net-igmp --proc-net-igmp6 --proc-net-ipv6-route --proc-net-netlink --proc-net-netstat --proc-net-packet --proc-net-protocols --proc-net-route --proc-net-unix --proc-pid-fdinfo --proc-pid-io --proc-pid-maps --proc-pid-mountinfo --proc-pid-numa-maps --proc-pid-smaps --proc-pid-stat --proc-pid-statm --proc-pid-status --ps --route --rpm-qi --rsync --rsync-s --semver --sfdisk --shadow --ss --sshd-conf --stat --stat-s --sysctl --syslog --syslog-s --syslog-bsd --syslog-bsd-s --systemctl --systemctl-lj --systemctl-ls --systemctl-luf --systeminfo --time --timedatectl --timestamp --toml --top --top-s --tracepath --traceroute --udevadm --ufw --ufw-appinfo --uname --update-alt-gs --update-alt-q --upower --uptime --url --vmstat --vmstat-s --w --wc --who --x509-cert --xml --xrandr --yaml --zipinfo)
jc_commands=(acpi airport arp blkid bluetoothctl cbt certbot chage cksum crontab date df dig dmidecode dpkg du env file findmnt finger free git gpg hciconfig host id ifconfig iostat ip iptables iw iwconfig jobs last lastb ls lsattr lsb_release lsblk lsmod lsof lspci lsusb md5 md5sum mdadm mount mpstat netstat nmcli nsd-control ntpq os-prober pidstat ping ping6 pip pip3 postconf printenv ps route rpm rsync sfdisk sha1sum sha224sum sha256sum sha384sum sha512sum shasum ss ssh sshd stat sum sysctl systemctl systeminfo timedatectl top tracepath tracepath6 traceroute traceroute6 udevadm ufw uname update-alternatives upower uptime vdir veracrypt vmstat w wc who xrandr zipinfo zpool)
jc_parsers=(--acpi --airport --airport-s --arp --asciitable --asciitable-m --blkid --bluetoothctl --cbt --cef --cef-s --certbot --chage --cksum --clf --clf-s --crontab --crontab-u --csv --csv-s --date --datetime-iso --df --dig --dir --dmidecode --dpkg-l --du --email-address --env --file --find --findmnt --finger --free --fstab --git-log --git-log-s --git-ls-remote --gpg --group --gshadow --hash --hashsum --hciconfig --history --host --hosts --id --ifconfig --ini --ini-dup --iostat --iostat-s --ip-address --iptables --ip-route --iw-scan --iwconfig --jar-manifest --jobs --jwt --kv --last --ls --ls-s --lsattr --lsb-release --lsblk --lsmod --lsof --lspci --lsusb --m3u --mdadm --mount --mpstat --mpstat-s --netstat --nmcli --nsd-control --ntpq --openvpn --os-prober --os-release --passwd --pci-ids --pgpass --pidstat --pidstat-s --ping --ping-s --pip-list --pip-show --plist --postconf --proc --proc-buddyinfo --proc-consoles --proc-cpuinfo --proc-crypto --proc-devices --proc-diskstats --proc-filesystems --proc-interrupts --proc-iomem --proc-ioports --proc-loadavg --proc-locks --proc-meminfo --proc-modules --proc-mtrr --proc-pagetypeinfo --proc-partitions --proc-slabinfo --proc-softirqs --proc-stat --proc-swaps --proc-uptime --proc-version --proc-vmallocinfo --proc-vmstat --proc-zoneinfo --proc-driver-rtc --proc-net-arp --proc-net-dev --proc-net-dev-mcast --proc-net-if-inet6 --proc-net-igmp --proc-net-igmp6 --proc-net-ipv6-route --proc-net-netlink --proc-net-netstat --proc-net-packet --proc-net-protocols --proc-net-route --proc-net-tcp --proc-net-unix --proc-pid-fdinfo --proc-pid-io --proc-pid-maps --proc-pid-mountinfo --proc-pid-numa-maps --proc-pid-smaps --proc-pid-stat --proc-pid-statm --proc-pid-status --ps --resolve-conf --route --rpm-qi --rsync --rsync-s --semver --sfdisk --shadow --srt --ss --ssh-conf --sshd-conf --stat --stat-s --sysctl --syslog --syslog-s --syslog-bsd --syslog-bsd-s --systemctl --systemctl-lj --systemctl-ls --systemctl-luf --systeminfo --time --timedatectl --timestamp --toml --top --top-s --tracepath --traceroute --udevadm --ufw --ufw-appinfo --uname --update-alt-gs --update-alt-q --upower --uptime --url --ver --veracrypt --vmstat --vmstat-s --w --wc --who --x509-cert --x509-csr --xml --xrandr --yaml --zipinfo --zpool-iostat --zpool-status)
jc_options=(--force-color -C --debug -d --monochrome -m --meta-out -M --pretty -p --quiet -q --raw -r --unbuffer -u --yaml-out -y)
jc_about_options=(--about -a)
jc_about_mod_options=(--pretty -p --yaml-out -y --monochrome -m --force-color -C)

View File

@@ -9,13 +9,15 @@ _jc() {
jc_help_options jc_help_options_describe \
jc_special_options jc_special_options_describe
jc_commands=(acpi airport arp blkid cbt chage cksum crontab date df dig dmidecode dpkg du env file findmnt finger free git gpg hciconfig id ifconfig iostat iptables iw iwconfig jobs last lastb ls lsblk lsmod lsof lspci lsusb md5 md5sum mdadm mount mpstat netstat nmcli ntpq os-prober pidstat ping ping6 pip pip3 postconf printenv ps route rpm rsync sfdisk sha1sum sha224sum sha256sum sha384sum sha512sum shasum ss sshd stat sum sysctl systemctl systeminfo timedatectl top tracepath tracepath6 traceroute traceroute6 udevadm ufw uname update-alternatives upower uptime vdir vmstat w wc who xrandr zipinfo)
jc_commands=(acpi airport arp blkid bluetoothctl cbt certbot chage cksum crontab date df dig dmidecode dpkg du env file findmnt finger free git gpg hciconfig host id ifconfig iostat ip iptables iw iwconfig jobs last lastb ls lsattr lsb_release lsblk lsmod lsof lspci lsusb md5 md5sum mdadm mount mpstat netstat nmcli nsd-control ntpq os-prober pidstat ping ping6 pip pip3 postconf printenv ps route rpm rsync sfdisk sha1sum sha224sum sha256sum sha384sum sha512sum shasum ss ssh sshd stat sum sysctl systemctl systeminfo timedatectl top tracepath tracepath6 traceroute traceroute6 udevadm ufw uname update-alternatives upower uptime vdir veracrypt vmstat w wc who xrandr zipinfo zpool)
jc_commands_describe=(
'acpi:run "acpi" command with magic syntax.'
'airport:run "airport" command with magic syntax.'
'arp:run "arp" command with magic syntax.'
'blkid:run "blkid" command with magic syntax.'
'bluetoothctl:run "bluetoothctl" command with magic syntax.'
'cbt:run "cbt" command with magic syntax.'
'certbot:run "certbot" command with magic syntax.'
'chage:run "chage" command with magic syntax.'
'cksum:run "cksum" command with magic syntax.'
'crontab:run "crontab" command with magic syntax.'
@@ -33,9 +35,11 @@ _jc() {
'git:run "git" command with magic syntax.'
'gpg:run "gpg" command with magic syntax.'
'hciconfig:run "hciconfig" command with magic syntax.'
'host:run "host" command with magic syntax.'
'id:run "id" command with magic syntax.'
'ifconfig:run "ifconfig" command with magic syntax.'
'iostat:run "iostat" command with magic syntax.'
'ip:run "ip" command with magic syntax.'
'iptables:run "iptables" command with magic syntax.'
'iw:run "iw" command with magic syntax.'
'iwconfig:run "iwconfig" command with magic syntax.'
@@ -43,6 +47,8 @@ _jc() {
'last:run "last" command with magic syntax.'
'lastb:run "lastb" command with magic syntax.'
'ls:run "ls" command with magic syntax.'
'lsattr:run "lsattr" command with magic syntax.'
'lsb_release:run "lsb_release" command with magic syntax.'
'lsblk:run "lsblk" command with magic syntax.'
'lsmod:run "lsmod" command with magic syntax.'
'lsof:run "lsof" command with magic syntax.'
@@ -55,6 +61,7 @@ _jc() {
'mpstat:run "mpstat" command with magic syntax.'
'netstat:run "netstat" command with magic syntax.'
'nmcli:run "nmcli" command with magic syntax.'
'nsd-control:run "nsd-control" command with magic syntax.'
'ntpq:run "ntpq" command with magic syntax.'
'os-prober:run "os-prober" command with magic syntax.'
'pidstat:run "pidstat" command with magic syntax.'
@@ -76,6 +83,7 @@ _jc() {
'sha512sum:run "sha512sum" command with magic syntax.'
'shasum:run "shasum" command with magic syntax.'
'ss:run "ss" command with magic syntax.'
'ssh:run "ssh" command with magic syntax.'
'sshd:run "sshd" command with magic syntax.'
'stat:run "stat" command with magic syntax.'
'sum:run "sum" command with magic syntax.'
@@ -95,14 +103,16 @@ _jc() {
'upower:run "upower" command with magic syntax.'
'uptime:run "uptime" command with magic syntax.'
'vdir:run "vdir" command with magic syntax.'
'veracrypt:run "veracrypt" command with magic syntax.'
'vmstat:run "vmstat" command with magic syntax.'
'w:run "w" command with magic syntax.'
'wc:run "wc" command with magic syntax.'
'who:run "who" command with magic syntax.'
'xrandr:run "xrandr" command with magic syntax.'
'zipinfo:run "zipinfo" command with magic syntax.'
'zpool:run "zpool" command with magic syntax.'
)
jc_parsers=(--acpi --airport --airport-s --arp --asciitable --asciitable-m --blkid --cbt --cef --cef-s --chage --cksum --clf --clf-s --crontab --crontab-u --csv --csv-s --date --datetime-iso --df --dig --dir --dmidecode --dpkg-l --du --email-address --env --file --findmnt --finger --free --fstab --git-log --git-log-s --git-ls-remote --gpg --group --gshadow --hash --hashsum --hciconfig --history --hosts --id --ifconfig --ini --ini-dup --iostat --iostat-s --ip-address --iptables --iw-scan --iwconfig --jar-manifest --jobs --jwt --kv --last --ls --ls-s --lsblk --lsmod --lsof --lspci --lsusb --m3u --mdadm --mount --mpstat --mpstat-s --netstat --nmcli --ntpq --openvpn --os-prober --passwd --pci-ids --pgpass --pidstat --pidstat-s --ping --ping-s --pip-list --pip-show --plist --postconf --proc --proc-buddyinfo --proc-consoles --proc-cpuinfo --proc-crypto --proc-devices --proc-diskstats --proc-filesystems --proc-interrupts --proc-iomem --proc-ioports --proc-loadavg --proc-locks --proc-meminfo --proc-modules --proc-mtrr --proc-pagetypeinfo --proc-partitions --proc-slabinfo --proc-softirqs --proc-stat --proc-swaps --proc-uptime --proc-version --proc-vmallocinfo --proc-vmstat --proc-zoneinfo --proc-driver-rtc --proc-net-arp --proc-net-dev --proc-net-dev-mcast --proc-net-if-inet6 --proc-net-igmp --proc-net-igmp6 --proc-net-ipv6-route --proc-net-netlink --proc-net-netstat --proc-net-packet --proc-net-protocols --proc-net-route --proc-net-unix --proc-pid-fdinfo --proc-pid-io --proc-pid-maps --proc-pid-mountinfo --proc-pid-numa-maps --proc-pid-smaps --proc-pid-stat --proc-pid-statm --proc-pid-status --ps --route --rpm-qi --rsync --rsync-s --semver --sfdisk --shadow --ss --sshd-conf --stat --stat-s --sysctl --syslog --syslog-s --syslog-bsd --syslog-bsd-s --systemctl --systemctl-lj --systemctl-ls --systemctl-luf --systeminfo --time --timedatectl --timestamp --toml --top --top-s --tracepath --traceroute --udevadm --ufw --ufw-appinfo --uname --update-alt-gs --update-alt-q --upower --uptime --url --vmstat --vmstat-s --w --wc --who --x509-cert --xml --xrandr --yaml --zipinfo)
jc_parsers=(--acpi --airport --airport-s --arp --asciitable --asciitable-m --blkid --bluetoothctl --cbt --cef --cef-s --certbot --chage --cksum --clf --clf-s --crontab --crontab-u --csv --csv-s --date --datetime-iso --df --dig --dir --dmidecode --dpkg-l --du --email-address --env --file --find --findmnt --finger --free --fstab --git-log --git-log-s --git-ls-remote --gpg --group --gshadow --hash --hashsum --hciconfig --history --host --hosts --id --ifconfig --ini --ini-dup --iostat --iostat-s --ip-address --iptables --ip-route --iw-scan --iwconfig --jar-manifest --jobs --jwt --kv --last --ls --ls-s --lsattr --lsb-release --lsblk --lsmod --lsof --lspci --lsusb --m3u --mdadm --mount --mpstat --mpstat-s --netstat --nmcli --nsd-control --ntpq --openvpn --os-prober --os-release --passwd --pci-ids --pgpass --pidstat --pidstat-s --ping --ping-s --pip-list --pip-show --plist --postconf --proc --proc-buddyinfo --proc-consoles --proc-cpuinfo --proc-crypto --proc-devices --proc-diskstats --proc-filesystems --proc-interrupts --proc-iomem --proc-ioports --proc-loadavg --proc-locks --proc-meminfo --proc-modules --proc-mtrr --proc-pagetypeinfo --proc-partitions --proc-slabinfo --proc-softirqs --proc-stat --proc-swaps --proc-uptime --proc-version --proc-vmallocinfo --proc-vmstat --proc-zoneinfo --proc-driver-rtc --proc-net-arp --proc-net-dev --proc-net-dev-mcast --proc-net-if-inet6 --proc-net-igmp --proc-net-igmp6 --proc-net-ipv6-route --proc-net-netlink --proc-net-netstat --proc-net-packet --proc-net-protocols --proc-net-route --proc-net-tcp --proc-net-unix --proc-pid-fdinfo --proc-pid-io --proc-pid-maps --proc-pid-mountinfo --proc-pid-numa-maps --proc-pid-smaps --proc-pid-stat --proc-pid-statm --proc-pid-status --ps --resolve-conf --route --rpm-qi --rsync --rsync-s --semver --sfdisk --shadow --srt --ss --ssh-conf --sshd-conf --stat --stat-s --sysctl --syslog --syslog-s --syslog-bsd --syslog-bsd-s --systemctl --systemctl-lj --systemctl-ls --systemctl-luf --systeminfo --time --timedatectl --timestamp --toml --top --top-s --tracepath --traceroute --udevadm --ufw --ufw-appinfo --uname --update-alt-gs --update-alt-q --upower --uptime --url --ver --veracrypt --vmstat --vmstat-s --w --wc --who --x509-cert --x509-csr --xml --xrandr --yaml --zipinfo --zpool-iostat --zpool-status)
jc_parsers_describe=(
'--acpi:`acpi` command parser'
'--airport:`airport -I` command parser'
@@ -111,9 +121,11 @@ _jc() {
'--asciitable:ASCII and Unicode table parser'
'--asciitable-m:multi-line ASCII and Unicode table parser'
'--blkid:`blkid` command parser'
'--bluetoothctl:`bluetoothctl` command parser'
'--cbt:`cbt` (Google Bigtable) command parser'
'--cef:CEF string parser'
'--cef-s:CEF string streaming parser'
'--certbot:`certbot` command parser'
'--chage:`chage --list` command parser'
'--cksum:`cksum` and `sum` command parser'
'--clf:Common and Combined Log Format file parser'
@@ -133,6 +145,7 @@ _jc() {
'--email-address:Email Address string parser'
'--env:`env` command parser'
'--file:`file` command parser'
'--find:`find` command parser'
'--findmnt:`findmnt` command parser'
'--finger:`finger` command parser'
'--free:`free` command parser'
@@ -147,6 +160,7 @@ _jc() {
'--hashsum:hashsum command parser (`md5sum`, `shasum`, etc.)'
'--hciconfig:`hciconfig` command parser'
'--history:`history` command parser'
'--host:`host` command parser'
'--hosts:`/etc/hosts` file parser'
'--id:`id` command parser'
'--ifconfig:`ifconfig` command parser'
@@ -156,6 +170,7 @@ _jc() {
'--iostat-s:`iostat` command streaming parser'
'--ip-address:IPv4 and IPv6 Address string parser'
'--iptables:`iptables` command parser'
'--ip-route:`ip route` command parser'
'--iw-scan:`iw dev [device] scan` command parser'
'--iwconfig:`iwconfig` command parser'
'--jar-manifest:Java MANIFEST.MF file parser'
@@ -165,6 +180,8 @@ _jc() {
'--last:`last` and `lastb` command parser'
'--ls:`ls` command parser'
'--ls-s:`ls` command streaming parser'
'--lsattr:`lsattr` command parser'
'--lsb-release:`lsb_release` command parser'
'--lsblk:`lsblk` command parser'
'--lsmod:`lsmod` command parser'
'--lsof:`lsof` command parser'
@@ -177,9 +194,11 @@ _jc() {
'--mpstat-s:`mpstat` command streaming parser'
'--netstat:`netstat` command parser'
'--nmcli:`nmcli` command parser'
'--nsd-control:`nsd-control` command parser'
'--ntpq:`ntpq -p` command parser'
'--openvpn:openvpn-status.log file parser'
'--os-prober:`os-prober` command parser'
'--os-release:`/etc/os-release` file parser'
'--passwd:`/etc/passwd` file parser'
'--pci-ids:`pci.ids` file parser'
'--pgpass:PostgreSQL password file parser'
@@ -231,6 +250,7 @@ _jc() {
'--proc-net-packet:`/proc/net/packet` file parser'
'--proc-net-protocols:`/proc/net/protocols` file parser'
'--proc-net-route:`/proc/net/route` file parser'
'--proc-net-tcp:`/proc/net/tcp` and `/proc/net/tcp6` file parser'
'--proc-net-unix:`/proc/net/unix` file parser'
'--proc-pid-fdinfo:`/proc/<pid>/fdinfo/<fd>` file parser'
'--proc-pid-io:`/proc/<pid>/io` file parser'
@@ -242,6 +262,7 @@ _jc() {
'--proc-pid-statm:`/proc/<pid>/statm` file parser'
'--proc-pid-status:`/proc/<pid>/status` file parser'
'--ps:`ps` command parser'
'--resolve-conf:`/etc/resolve.conf` file parser'
'--route:`route` command parser'
'--rpm-qi:`rpm -qi` command parser'
'--rsync:`rsync` command parser'
@@ -249,8 +270,10 @@ _jc() {
'--semver:Semantic Version string parser'
'--sfdisk:`sfdisk` command parser'
'--shadow:`/etc/shadow` file parser'
'--srt:SRT file parser'
'--ss:`ss` command parser'
'--sshd-conf:sshd config file and `sshd -T` command parser'
'--ssh-conf:`ssh` config file and `ssh -G` command parser'
'--sshd-conf:`sshd` config file and `sshd -T` command parser'
'--stat:`stat` command parser'
'--stat-s:`stat` command streaming parser'
'--sysctl:`sysctl` command parser'
@@ -280,16 +303,21 @@ _jc() {
'--upower:`upower` command parser'
'--uptime:`uptime` command parser'
'--url:URL string parser'
'--ver:Version string parser'
'--veracrypt:`veracrypt` command parser'
'--vmstat:`vmstat` command parser'
'--vmstat-s:`vmstat` command streaming parser'
'--w:`w` command parser'
'--wc:`wc` command parser'
'--who:`who` command parser'
'--x509-cert:X.509 PEM and DER certificate file parser'
'--x509-csr:X.509 PEM and DER certificate request file parser'
'--xml:XML file parser'
'--xrandr:`xrandr` command parser'
'--yaml:YAML file parser'
'--zipinfo:`zipinfo` command parser'
'--zpool-iostat:`zpool iostat` command parser'
'--zpool-status:`zpool status` command parser'
)
jc_options=(--force-color -C --debug -d --monochrome -m --meta-out -M --pretty -p --quiet -q --raw -r --unbuffer -u --yaml-out -y)
jc_options_describe=(

View File

@@ -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]]
```

View File

@@ -250,4 +250,4 @@ Returns:
### Parser Information
Compatibility: linux
Version 1.4 by Kelly Brazil (kellyjonbrazil@gmail.com)
Version 1.6 by Kelly Brazil (kellyjonbrazil@gmail.com)

View File

@@ -0,0 +1,133 @@
[Home](https://kellyjonbrazil.github.io/jc/)
<a id="jc.parsers.bluetoothctl"></a>
# jc.parsers.bluetoothctl
jc - JSON Convert `bluetoothctl` command output parser
Supports the following `bluetoothctl` subcommands:
- `bluetoothctl list`
- `bluetoothctl show`
- `bluetoothctl show <ctrl>`
- `bluetoothctl devices`
- `bluetoothctl info <dev>`
Usage (cli):
$ bluetoothctl info <dev> | jc --bluetoothctl
or
$ jc bluetoothctl info <dev>
Usage (module):
import jc
result = jc.parse('bluetoothctl', bluetoothctl_command_output)
Schema:
Because bluetoothctl is handling two main entities, controllers and devices,
the schema is shared between them. Most of the fields are common between
a controller and a device but there might be fields corresponding to one entity.
Controller:
[
{
"name": string,
"is_default": boolean,
"is_public": boolean,
"is_random": boolean,
"address": string,
"alias": string,
"class": string,
"powered": string,
"discoverable": string,
"discoverable_timeout": string,
"pairable": string,
"modalias": string,
"discovering": string,
"uuids": array
}
]
Device:
[
{
"name": string,
"is_public": boolean,
"is_random": boolean,
"address": string,
"alias": string,
"appearance": string,
"class": string,
"icon": string,
"paired": string,
"bonded": string,
"trusted": string,
"blocked": string,
"connected": string,
"legacy_pairing": string,
"rssi": int,
"txpower": int,
"uuids": array,
"modalias": string
}
]
Examples:
$ bluetoothctl info EB:06:EF:62:B3:19 | jc --bluetoothctl -p
[
{
"address": "22:06:33:62:B3:19",
"is_public": true,
"name": "TaoTronics TT-BH336",
"alias": "TaoTronics TT-BH336",
"class": "0x00240455",
"icon": "audio-headset",
"paired": "no",
"bonded": "no",
"trusted": "no",
"blocked": "no",
"connected": "no",
"legacy_pairing": "no",
"uuids": [
"Advanced Audio Distribu.. (0000120d-0000-1000-8000-00805f9b34fb)",
"Audio Sink (0000130b-0000-1000-8000-00805f9b34fb)",
"A/V Remote Control (0000140e-0000-1000-8000-00805f9b34fb)",
"A/V Remote Control Cont.. (0000150f-0000-1000-8000-00805f9b34fb)",
"Handsfree (0000161e-0000-1000-8000-00805f9b34fb)",
"Headset (00001708-0000-1000-8000-00805f9b34fb)",
"Headset HS (00001831-0000-1000-8000-00805f9b34fb)"
],
"rssi": -52,
"txpower": 4
}
]
<a id="jc.parsers.bluetoothctl.parse"></a>
### parse
```python
def parse(data: str,
raw: bool = False,
quiet: bool = False) -> List[JSONDictType]
```
Main text parsing function
Parameters:
data: (string) text data to parse
raw: (boolean) unprocessed output if True
quiet: (boolean) suppress warning messages if True
Returns:
List of Dictionaries. Raw or processed structured data.
### Parser Information
Compatibility: linux
Version 1.1 by Jake Ob (iakopap at gmail.com)

161
docs/parsers/certbot.md Normal file
View File

@@ -0,0 +1,161 @@
[Home](https://kellyjonbrazil.github.io/jc/)
<a id="jc.parsers.certbot"></a>
# jc.parsers.certbot
jc - JSON Convert `certbot` command output parser
Supports the following `certbot` commands:
- `certbot show_account`
- `certbot certificates`
Verbose options are not supported.
Usage (cli):
$ certbot show_account | jc --certbot
$ certbot certificates | jc --certbot
or
$ jc certbot show_account
$ jc certbot certificates
Usage (module):
import jc
result = jc.parse('certbot', certbot_command_output)
Schema:
{
"certificates": [
{
"name": string,
"serial_number": string,
"key_type": string,
"domains": [
string
],
"expiration_date": string,
"expiration_date_epoch": integer,
"expiration_date_epoch_utc": integer,
"expiration_date_iso": string,
"validity": string,
"certificate_path": string,
"private_key_path": string
}
],
"account": {
"server": string,
"url": string,
"email": string
}
}
Examples:
$ certbot certificates | jc --certbot -p
{
"certificates": [
{
"name": "example.com",
"serial_number": "3f7axxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"key_type": "RSA",
"domains": [
"example.com",
"www.example.com"
],
"expiration_date": "2023-05-11 01:33:10+00:00",
"validity": "63 days",
"certificate_path": "/etc/letsencrypt/live/example.com/chain.pem",
"private_key_path": "/etc/letsencrypt/live/example.com/priv.pem",
"expiration_date_epoch": 1683793990,
"expiration_date_epoch_utc": 1683768790,
"expiration_date_iso": "2023-05-11T01:33:10+00:00"
},
{
"name": "example.org",
"serial_number": "3bcyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy",
"key_type": "RSA",
"domains": [
"example.org",
"www.example.org"
],
"expiration_date": "2023-06-12 01:35:30+00:00",
"validity": "63 days",
"certificate_path": "/etc/letsencrypt/live/example.org/chain.pem",
"private_key_path": "/etc/letsencrypt/live/example.org/key.pem",
"expiration_date_epoch": 1686558930,
"expiration_date_epoch_utc": 1686533730,
"expiration_date_iso": "2023-06-12T01:35:30+00:00"
}
]
}
$ certbot certificates | jc --certbot -p -r
{
"certificates": [
{
"name": "example.com",
"serial_number": "3f7axxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"key_type": "RSA",
"domains": [
"example.com",
"www.example.com"
],
"expiration_date": "2023-05-11 01:33:10+00:00",
"validity": "63 days",
"certificate_path": "/etc/letsencrypt/live/example.com/chain.pem",
"private_key_path": "/etc/letsencrypt/live/example.com/priv.pem"
},
{
"name": "example.org",
"serial_number": "3bcyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy",
"key_type": "RSA",
"domains": [
"example.org",
"www.example.org"
],
"expiration_date": "2023-06-12 01:35:30+00:00",
"validity": "63 days",
"certificate_path": "/etc/letsencrypt/live/example.org/chain.pem",
"private_key_path": "/etc/letsencrypt/live/example.org/key.pem"
}
]
}
$ certbot show_account | jc --certbot -p
{
"account": {
"server": "https://acme-staging-v02.api.letsencrypt.org/directory",
"url": "https://acme-staging-v02.api.letsencrypt.org/acme/acct/123",
"email": "some@example.com"
}
}
<a id="jc.parsers.certbot.parse"></a>
### parse
```python
def parse(data: str, raw: bool = False, quiet: bool = False) -> JSONDictType
```
Main text parsing function
Parameters:
data: (string) text data to parse
raw: (boolean) unprocessed output if True
quiet: (boolean) suppress warning messages if True
Returns:
Dictionary. Raw or processed structured data.
### Parser Information
Compatibility: linux, darwin, cygwin, win32, aix, freebsd
Version 1.2 by Kelly Brazil (kellyjonbrazil@gmail.com)

View File

@@ -196,4 +196,4 @@ Returns:
### Parser Information
Compatibility: linux, darwin, aix, freebsd
Version 1.6 by Kelly Brazil (kellyjonbrazil@gmail.com)
Version 1.8 by Kelly Brazil (kellyjonbrazil@gmail.com)

View File

@@ -193,4 +193,4 @@ Returns:
### Parser Information
Compatibility: linux, darwin, aix, freebsd
Version 1.7 by Kelly Brazil (kellyjonbrazil@gmail.com)
Version 1.9 by Kelly Brazil (kellyjonbrazil@gmail.com)

View File

@@ -9,6 +9,7 @@ Options supported:
- `+noall +answer` options are supported in cases where only the answer
information is desired.
- `+axfr` option is supported on its own
- `+nsid` option is supported
The `when_epoch` calculated timestamp field is naive. (i.e. based on the
local time of the system the parser is run on)
@@ -345,4 +346,4 @@ Returns:
### Parser Information
Compatibility: linux, aix, freebsd, darwin, win32, cygwin
Version 2.4 by Kelly Brazil (kellyjonbrazil@gmail.com)
Version 2.5 by Kelly Brazil (kellyjonbrazil@gmail.com)

View File

@@ -90,10 +90,10 @@ Parameters:
Returns:
Dictionary of raw structured data or
List of Dictionaries of processed structured data
Dictionary of raw structured data or (default)
List of Dictionaries of processed structured data (raw)
### Parser Information
Compatibility: linux, darwin, cygwin, win32, aix, freebsd
Version 1.4 by Kelly Brazil (kellyjonbrazil@gmail.com)
Version 1.5 by Kelly Brazil (kellyjonbrazil@gmail.com)

82
docs/parsers/find.md Normal file
View File

@@ -0,0 +1,82 @@
[Home](https://kellyjonbrazil.github.io/jc/)
<a id="jc.parsers.find"></a>
# jc.parsers.find
jc - JSON Convert `find` command output parser
This parser returns a list of objects by default and a list of strings if
the `--raw` option is used.
Usage (cli):
$ find | jc --find
Usage (module):
import jc
result = jc.parse('find', find_command_output)
Schema:
[
{
"path": string,
"node": string,
"error": string
}
]
Examples:
$ find | jc --find -p
[
{
"path": "./directory"
"node": "filename"
},
{
"path": "./anotherdirectory"
"node": "anotherfile"
},
{
"path": null
"node": null
"error": "find: './inaccessible': Permission denied"
}
...
]
$ find | jc --find -p -r
[
"./templates/readme_template",
"./templates/manpage_template",
"./.github/workflows/pythonapp.yml",
...
]
<a id="jc.parsers.find.parse"></a>
### parse
```python
def parse(data, raw=False, quiet=False)
```
Main text parsing function
Parameters:
data: (string) text data to parse
raw: (boolean) unprocessed output if True
quiet: (boolean) suppress warning messages if True
Returns:
List of raw strings or
List of Dictionaries of processed structured data
### Parser Information
Compatibility: linux
Version 1.0 by Solomon Leang (solomonleang@gmail.com)

View File

@@ -172,4 +172,4 @@ Returns:
### Parser Information
Compatibility: linux, darwin, cygwin, win32, aix, freebsd
Version 1.3 by Kelly Brazil (kellyjonbrazil@gmail.com)
Version 1.4 by Kelly Brazil (kellyjonbrazil@gmail.com)

View File

@@ -108,4 +108,4 @@ Returns:
### Parser Information
Compatibility: linux, darwin, cygwin, win32, aix, freebsd
Version 1.3 by Kelly Brazil (kellyjonbrazil@gmail.com)
Version 1.4 by Kelly Brazil (kellyjonbrazil@gmail.com)

113
docs/parsers/host.md Normal file
View File

@@ -0,0 +1,113 @@
[Home](https://kellyjonbrazil.github.io/jc/)
<a id="jc.parsers.host"></a>
# jc.parsers.host
jc - JSON Convert `host` command output parser
Supports parsing of the most commonly used RR types (A, AAAA, MX, TXT)
Usage (cli):
$ host google.com | jc --host
or
$ jc host google.com
Usage (module):
import jc
result = jc.parse('host', host_command_output)
Schema:
[
{
"hostname": string,
"address": [
string
],
"v6-address": [
string
],
"mail": [
string
]
}
]
[
{
"nameserver": string,
"zone": string,
"mname": string,
"rname": string,
"serial": integer,
"refresh": integer,
"retry": integer,
"expire": integer,
"minimum": integer
}
]
Examples:
$ host google.com | jc --host
[
{
"hostname": "google.com",
"address": [
"142.251.39.110"
],
"v6-address": [
"2a00:1450:400e:811::200e"
],
"mail": [
"smtp.google.com."
]
}
]
$ jc host -C sunet.se
[
{
"nameserver": "2001:6b0:7::2",
"zone": "sunet.se",
"mname": "sunic.sunet.se.",
"rname": "hostmaster.sunet.se.",
"serial": "2023090401",
"refresh": "28800",
"retry": "7200",
"expire": "604800",
"minimum": "300"
},
{
...
}
]
<a id="jc.parsers.host.parse"></a>
### parse
```python
def parse(data: str, raw: bool = False, quiet: bool = False)
```
Main text parsing function
Parameters:
data: (string) text data to parse
raw: (boolean) unprocessed output if True
quiet: (boolean) suppress warning messages if True
Returns:
List of Dictionaries. Raw or processed structured data.
### Parser Information
Compatibility: linux, darwin, cygwin, win32, aix, freebsd
Version 1.0 by Pettai (pettai@sunet.se)

View File

@@ -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)

View File

@@ -22,12 +22,12 @@ contained in lists/arrays.
Usage (cli):
$ cat foo.ini | jc --ini
$ cat foo.ini | jc --ini-dup
Usage (module):
import jc
result = jc.parse('ini', ini_file_output)
result = jc.parse('ini_dup', ini_file_output)
Schema:
@@ -67,7 +67,7 @@ Examples:
fruit = peach
color = green
$ cat example.ini | jc --ini -p
$ cat example.ini | jc --ini-dup -p
{
"foo": [
"fiz"
@@ -118,4 +118,4 @@ Returns:
### Parser Information
Compatibility: linux, darwin, cygwin, win32, aix, freebsd
Version 1.0 by Kelly Brazil (kellyjonbrazil@gmail.com)
Version 1.1 by Kelly Brazil (kellyjonbrazil@gmail.com)

74
docs/parsers/ip_route.md Normal file
View File

@@ -0,0 +1,74 @@
[Home](https://kellyjonbrazil.github.io/jc/)
<a id="jc.parsers.ip_route"></a>
# jc.parsers.ip\_route
jc - JSON Convert `ip route` command output parser
Usage (cli):
$ ip route | jc --ip-route
or
$ jc ip-route
Usage (module):
import jc
result = jc.parse('ip_route', ip_route_command_output)
Schema:
[
{
"ip": string,
"via": string,
"dev": string,
"metric": integer,
"proto": string,
"scope": string,
"src": string,
"via": string,
"status": string
}
]
Examples:
$ ip route | jc --ip-route -p
[
{
"ip": "10.0.2.0/24",
"dev": "enp0s3",
"proto": "kernel",
"scope": "link",
"src": "10.0.2.15",
"metric": 100
}
]
<a id="jc.parsers.ip_route.parse"></a>
### parse
```python
def parse(data, raw=False, quiet=False)
```
Main text parsing function
Parameters:
data: (string) text data to parse
raw: (boolean) unprocessed output if True
quiet: (boolean) suppress warning messages if True
Returns:
List of Json objects if data is processed and Raw data if raw = true.
### Parser Information
Compatibility: linux
Version 1.0 by Julian Jackson (jackson.julian55@yahoo.com)

View File

@@ -108,4 +108,4 @@ Returns:
### Parser Information
Compatibility: linux
Version 1.0 by Thomas Vincent (vrince@gmail.com)
Version 1.1 by Thomas Vincent (vrince@gmail.com)

View File

@@ -5,7 +5,7 @@
jc - JSON Convert `last` and `lastb` command output parser
Supports `-w` and `-F` options.
Supports `-w`, `-F`, and `-x` options.
Calculated epoch time fields are naive (i.e. based on the local time of the
system the parser is run on) since there is no timezone information in the
@@ -127,4 +127,4 @@ Returns:
### Parser Information
Compatibility: linux, darwin, aix, freebsd
Version 1.8 by Kelly Brazil (kellyjonbrazil@gmail.com)
Version 1.9 by Kelly Brazil (kellyjonbrazil@gmail.com)

89
docs/parsers/lsattr.md Normal file
View File

@@ -0,0 +1,89 @@
[Home](https://kellyjonbrazil.github.io/jc/)
<a id="jc.parsers.lsattr"></a>
# jc.parsers.lsattr
jc - JSON Convert `lsattr` command output parser
Usage (cli):
$ lsattr | jc --lsattr
or
$ jc lsattr
Usage (module):
import jc
result = jc.parse('lsattr', lsattr_command_output)
Schema:
Information from https://github.com/mirror/busybox/blob/2d4a3d9e6c1493a9520b907e07a41aca90cdfd94/e2fsprogs/e2fs_lib.c#L40
used to define field names
[
{
"file": string,
"compressed_file": Optional[boolean],
"compressed_dirty_file": Optional[boolean],
"compression_raw_access": Optional[boolean],
"secure_deletion": Optional[boolean],
"undelete": Optional[boolean],
"synchronous_updates": Optional[boolean],
"synchronous_directory_updates": Optional[boolean],
"immutable": Optional[boolean],
"append_only": Optional[boolean],
"no_dump": Optional[boolean],
"no_atime": Optional[boolean],
"compression_requested": Optional[boolean],
"encrypted": Optional[boolean],
"journaled_data": Optional[boolean],
"indexed_directory": Optional[boolean],
"no_tailmerging": Optional[boolean],
"top_of_directory_hierarchies": Optional[boolean],
"extents": Optional[boolean],
"no_cow": Optional[boolean],
"casefold": Optional[boolean],
"inline_data": Optional[boolean],
"project_hierarchy": Optional[boolean],
"verity": Optional[boolean],
}
]
Examples:
$ sudo lsattr /etc/passwd | jc --lsattr
[
{
"file": "/etc/passwd",
"extents": true
}
]
<a id="jc.parsers.lsattr.parse"></a>
### parse
```python
def parse(data: str,
raw: bool = False,
quiet: bool = False) -> List[JSONDictType]
```
Main text parsing function
Parameters:
data: (string) text data to parse
quiet: (boolean) suppress warning messages if True
Returns:
List of Dictionaries. Raw or processed structured data.
### Parser Information
Compatibility: linux
Version 1.0 by Mark Rotner (rotner.mr@gmail.com)

View File

@@ -0,0 +1,61 @@
[Home](https://kellyjonbrazil.github.io/jc/)
<a id="jc.parsers.lsb_release"></a>
# jc.parsers.lsb\_release
jc - JSON Convert `lsb_release` command parser
This parser is an alias to the Key/Value parser (`--kv`).
Usage (cli):
$ lsb_release -a | jc --lsb-release
or
$ jc lsb_release -a
Usage (module):
import jc
result = jc.parse('lsb_release', lsb_release_command_output)
Schema:
{
"<key>": string
}
Examples:
$ lsb_release -a | jc --lsb-release -p
{
"Distributor ID": "Ubuntu",
"Description": "Ubuntu 16.04.6 LTS",
"Release": "16.04",
"Codename": "xenial"
}
<a id="jc.parsers.lsb_release.parse"></a>
### parse
```python
def parse(data: str, raw: bool = False, quiet: bool = False) -> JSONDictType
```
Main text parsing function
Parameters:
data: (string) text data to parse
raw: (boolean) unprocessed output if True
quiet: (boolean) suppress warning messages if True
Returns:
Dictionary. Raw or processed structured data.
### Parser Information
Compatibility: linux, darwin, cygwin, win32, aix, freebsd
Version 1.0 by Kelly Brazil (kellyjonbrazil@gmail.com)

View File

@@ -102,6 +102,24 @@ Schema:
]
}
},
"cdc_mbim": {
"<item>": {
"value": string,
"description": string,
"attributes": [
string
]
}
},
"cdc_mbim_extended": {
"<item>": {
"value": string,
"description": string,
"attributes": [
string
]
}
},
"videocontrol_descriptors": [
{
"<item>": {
@@ -312,4 +330,4 @@ Returns:
### Parser Information
Compatibility: linux
Version 1.3 by Kelly Brazil (kellyjonbrazil@gmail.com)
Version 1.4 by Kelly Brazil (kellyjonbrazil@gmail.com)

View File

@@ -376,6 +376,6 @@ Returns:
List of Dictionaries. Raw or processed structured data.
### Parser Information
Compatibility: linux, darwin, freebsd
Compatibility: linux, darwin, freebsd, win32
Version 1.13 by Kelly Brazil (kellyjonbrazil@gmail.com)
Version 1.15 by Kelly Brazil (kellyjonbrazil@gmail.com)

View File

@@ -0,0 +1,90 @@
[Home](https://kellyjonbrazil.github.io/jc/)
<a id="jc.parsers.nsd_control"></a>
# jc.parsers.nsd\_control
jc - JSON Convert `nsd-control` command output parser
Usage (cli):
$ nsd-control | jc --nsd-control
or
$ jc nsd-control
Usage (module):
import jc
result = jc.parse('nsd_control', nsd_control_command_output)
Schema:
[
{
"version": string,
"verbosity": integer,
"ratelimit": integer
}
]
[
{
"zone": string
"status": {
"state": string,
"served-serial": string,
"commit-serial": string,
"wait": string
}
}
]
Examples:
$ nsd-control | jc --nsd-control status
[
{
"version": "4.6.2",
"verbosity": "2",
"ratelimit": "0"
}
]
$ nsd-control | jc --nsd-control zonestatus sunet.se
[
{
"zone": "sunet.se",
"status": {
"state": "ok",
"served-serial": "2023090704 since 2023-09-07T16:34:27",
"commit-serial": "2023090704 since 2023-09-07T16:34:27",
"wait": "28684 sec between attempts"
}
}
]
<a id="jc.parsers.nsd_control.parse"></a>
### parse
```python
def parse(data: str, raw: bool = False, quiet: bool = False)
```
Main text parsing function
Parameters:
data: (string) text data to parse
raw: (boolean) unprocessed output if True
quiet: (boolean) suppress warning messages if True
Returns:
List of Dictionaries. Raw or processed structured data.
### Parser Information
Compatibility: linux, darwin, cygwin, win32, aix, freebsd
Version 1.0 by Pettai (pettai@sunet.se)

View File

@@ -0,0 +1,86 @@
[Home](https://kellyjonbrazil.github.io/jc/)
<a id="jc.parsers.os_release"></a>
# jc.parsers.os\_release
jc - JSON Convert `/etc/os-release` file parser
This parser is an alias to the Key/Value parser (`--kv`).
Usage (cli):
$ cat /etc/os-release | jc --os-release
Usage (module):
import jc
result = jc.parse('os_release', os_release_output)
Schema:
{
"<key>": string
}
Examples:
$ cat /etc/os-release | jc --os-release -p
{
"NAME": "CentOS Linux",
"VERSION": "7 (Core)",
"ID": "centos",
"ID_LIKE": "rhel fedora",
"VERSION_ID": "7",
"PRETTY_NAME": "CentOS Linux 7 (Core)",
"ANSI_COLOR": "0;31",
"CPE_NAME": "cpe:/o:centos:centos:7",
"HOME_URL": "https://www.centos.org/",
"BUG_REPORT_URL": "https://bugs.centos.org/",
"CENTOS_MANTISBT_PROJECT": "CentOS-7",
"CENTOS_MANTISBT_PROJECT_VERSION": "7",
"REDHAT_SUPPORT_PRODUCT": "centos",
"REDHAT_SUPPORT_PRODUCT_VERSION": "7"
}
$ cat /etc/os-release | jc --os-release -p -r
{
"NAME": "\\"CentOS Linux\\"",
"VERSION": "\\"7 (Core)\\"",
"ID": "\\"centos\\"",
"ID_LIKE": "\\"rhel fedora\\"",
"VERSION_ID": "\\"7\\"",
"PRETTY_NAME": "\\"CentOS Linux 7 (Core)\\"",
"ANSI_COLOR": "\\"0;31\\"",
"CPE_NAME": "\\"cpe:/o:centos:centos:7\\"",
"HOME_URL": "\\"https://www.centos.org/\\"",
"BUG_REPORT_URL": "\\"https://bugs.centos.org/\\"",
"CENTOS_MANTISBT_PROJECT": "\\"CentOS-7\\"",
"CENTOS_MANTISBT_PROJECT_VERSION": "\\"7\\"",
"REDHAT_SUPPORT_PRODUCT": "\\"centos\\"",
"REDHAT_SUPPORT_PRODUCT_VERSION": "\\"7\\""
}
<a id="jc.parsers.os_release.parse"></a>
### parse
```python
def parse(data: str, raw: bool = False, quiet: bool = False) -> JSONDictType
```
Main text parsing function
Parameters:
data: (string) text data to parse
raw: (boolean) unprocessed output if True
quiet: (boolean) suppress warning messages if True
Returns:
Dictionary. Raw or processed structured data.
### Parser Information
Compatibility: linux, darwin, cygwin, win32, aix, freebsd
Version 1.0 by Kelly Brazil (kellyjonbrazil@gmail.com)

View File

@@ -45,6 +45,9 @@ Schema:
"kb_ccwr_s": float,
"cswch_s": float,
"nvcswch_s": float,
"usr_ms": integer,
"system_ms": integer,
"guest_ms": integer,
"command": string
}
]
@@ -148,4 +151,4 @@ Returns:
### Parser Information
Compatibility: linux
Version 1.1 by Kelly Brazil (kellyjonbrazil@gmail.com)
Version 1.3 by Kelly Brazil (kellyjonbrazil@gmail.com)

View File

@@ -39,6 +39,7 @@ Schema:
"percent_usr": float,
"percent_system": float,
"percent_guest": float,
"percent_wait": float,
"percent_cpu": float,
"cpu": integer,
"minflt_s": float,
@@ -53,6 +54,9 @@ Schema:
"kb_ccwr_s": float,
"cswch_s": float,
"nvcswch_s": float,
"usr_ms": integer,
"system_ms": integer,
"guest_ms": integer,
"command": string,
# below object only exists if using -qq or ignore_exceptions=True
@@ -107,4 +111,4 @@ Returns:
### Parser Information
Compatibility: linux
Version 1.1 by Kelly Brazil (kellyjonbrazil@gmail.com)
Version 1.2 by Kelly Brazil (kellyjonbrazil@gmail.com)

View File

@@ -35,6 +35,8 @@ Schema:
"packets_received": integer,
"packet_loss_percent": float,
"duplicates": integer,
"errors": integer,
"corrupted": integer,
"round_trip_ms_min": float,
"round_trip_ms_avg": float,
"round_trip_ms_max": float,
@@ -185,4 +187,4 @@ Returns:
### Parser Information
Compatibility: linux, darwin, freebsd
Version 1.8 by Kelly Brazil (kellyjonbrazil@gmail.com)
Version 1.10 by Kelly Brazil (kellyjonbrazil@gmail.com)

View File

@@ -36,7 +36,7 @@ Schema:
"source_ip": string,
"destination_ip": string,
"sent_bytes": integer,
"pattern": string, # (null if not set)
"pattern": string, # null if not set
"destination": string,
"timestamp": float,
"response_bytes": integer,
@@ -49,10 +49,12 @@ Schema:
"packets_received": integer,
"packet_loss_percent": float,
"duplicates": integer,
"round_trip_ms_min": float,
"round_trip_ms_avg": float,
"round_trip_ms_max": float,
"round_trip_ms_stddev": float,
"errors": integer, # null if not set
"corrupted": integer, # null if not set
"round_trip_ms_min": float, # null if not set
"round_trip_ms_avg": float, # null if not set
"round_trip_ms_max": float, # null if not set
"round_trip_ms_stddev": float, # null if not set
# below object only exists if using -qq or ignore_exceptions=True
"_jc_meta": {
@@ -106,4 +108,4 @@ Returns:
### Parser Information
Compatibility: linux, darwin, freebsd
Version 1.2 by Kelly Brazil (kellyjonbrazil@gmail.com)
Version 1.4 by Kelly Brazil (kellyjonbrazil@gmail.com)

View File

@@ -0,0 +1,186 @@
[Home](https://kellyjonbrazil.github.io/jc/)
<a id="jc.parsers.proc_net_tcp"></a>
# jc.parsers.proc\_net\_tcp
jc - JSON Convert `/proc/net/tcp` and `proc/net/tcp6` file parser
IPv4 and IPv6 addresses are converted to standard notation unless the raw
(--raw) option is used.
Usage (cli):
$ cat /proc/net/tcp | jc --proc
or
$ jc /proc/net/tcp
or
$ cat /proc/net/tcp | jc --proc-net-tcp
Usage (module):
import jc
result = jc.parse('proc', proc_net_tcp_file)
or
import jc
result = jc.parse('proc_net_tcp', proc_net_tcp_file)
Schema:
Field names and types gathered from the following:
https://www.kernel.org/doc/Documentation/networking/proc_net_tcp.txt
https://github.com/torvalds/linux/blob/master/net/ipv4/tcp_ipv4.c
https://github.com/torvalds/linux/blob/master/net/ipv6/tcp_ipv6.c
[
{
"entry": integer,
"local_address": string,
"local_port": integer,
"remote_address": string,
"remote_port": integer,
"state": string,
"tx_queue": string,
"rx_queue": string,
"timer_active": integer,
"jiffies_until_timer_expires": string,
"unrecovered_rto_timeouts": string,
"uid": integer,
"unanswered_0_window_probes": integer,
"inode": integer,
"sock_ref_count": integer,
"sock_mem_loc": string,
"retransmit_timeout": integer,
"soft_clock_tick": integer,
"ack_quick_pingpong": integer,
"sending_congestion_window": integer,
"slow_start_size_threshold": integer
}
]
Examples:
$ cat /proc/net/tcp | jc --proc -p
[
{
"entry": "0",
"local_address": "10.0.0.28",
"local_port": 42082,
"remote_address": "64.12.0.108",
"remote_port": 80,
"state": "04",
"tx_queue": "00000001",
"rx_queue": "00000000",
"timer_active": 1,
"jiffies_until_timer_expires": "00000015",
"unrecovered_rto_timeouts": "00000000",
"uid": 0,
"unanswered_0_window_probes": 0,
"inode": 0,
"sock_ref_count": 3,
"sock_mem_loc": "ffff8c7a0de930c0",
"retransmit_timeout": 21,
"soft_clock_tick": 4,
"ack_quick_pingpong": 30,
"sending_congestion_window": 10,
"slow_start_size_threshold": -1
},
{
"entry": "1",
"local_address": "10.0.0.28",
"local_port": 38864,
"remote_address": "104.244.42.65",
"remote_port": 80,
"state": "06",
"tx_queue": "00000000",
"rx_queue": "00000000",
"timer_active": 3,
"jiffies_until_timer_expires": "000007C5",
"unrecovered_rto_timeouts": "00000000",
"uid": 0,
"unanswered_0_window_probes": 0,
"inode": 0,
"sock_ref_count": 3,
"sock_mem_loc": "ffff8c7a12d31aa0"
},
...
]
$ cat /proc/net/tcp | jc --proc -p -r
[
{
"entry": "1",
"local_address": "1C00000A",
"local_port": "A462",
"remote_address": "6C000C40",
"remote_port": "0050",
"state": "04",
"tx_queue": "00000001",
"rx_queue": "00000000",
"timer_active": "01",
"jiffies_until_timer_expires": "00000015",
"unrecovered_rto_timeouts": "00000000",
"uid": "0",
"unanswered_0_window_probes": "0",
"inode": "0",
"sock_ref_count": "3",
"sock_mem_loc": "ffff8c7a0de930c0",
"retransmit_timeout": "21",
"soft_clock_tick": "4",
"ack_quick_pingpong": "30",
"sending_congestion_window": "10",
"slow_start_size_threshold": "-1"
},
{
"entry": "2",
"local_address": "1C00000A",
"local_port": "97D0",
"remote_address": "412AF468",
"remote_port": "0050",
"state": "06",
"tx_queue": "00000000",
"rx_queue": "00000000",
"timer_active": "03",
"jiffies_until_timer_expires": "000007C5",
"unrecovered_rto_timeouts": "00000000",
"uid": "0",
"unanswered_0_window_probes": "0",
"inode": "0",
"sock_ref_count": "3",
"sock_mem_loc": "ffff8c7a12d31aa0"
},
...
]
<a id="jc.parsers.proc_net_tcp.parse"></a>
### parse
```python
def parse(data: str, raw: bool = False, quiet: bool = False) -> List[Dict]
```
Main text parsing function
Parameters:
data: (string) text data to parse
raw: (boolean) unprocessed output if True
quiet: (boolean) suppress warning messages if True
Returns:
List of Dictionaries. Raw or processed structured data.
### Parser Information
Compatibility: linux
Version 1.0 by Alvin Solomon (alvinms01@gmail.com)

View File

@@ -114,7 +114,8 @@ Examples:
"mw",
"me",
"dw",
"sd"
"sd",
"mp"
],
"VmFlags_pretty": [
"readable",

View File

@@ -0,0 +1,83 @@
[Home](https://kellyjonbrazil.github.io/jc/)
<a id="jc.parsers.resolve_conf"></a>
# jc.parsers.resolve\_conf
jc - JSON Convert `/etc/resolve.conf` file parser
This parser may be more forgiving than the system parser. For example, if
multiple `search` lists are defined, this parser will append all entries to
the `search` field, while the system parser may only use the list from the
last defined instance.
Usage (cli):
$ cat /etc/resolve.conf | jc --resolve-conf
Usage (module):
import jc
result = jc.parse('resolve_conf', resolve_conf_output)
Schema:
{
"domain": string,
"search": [
string
],
"nameservers": [
string
],
"options": [
string
],
"sortlist": [
string
]
}
Examples:
$ cat /etc/resolve.conf | jc --resolve-conf -p
{
"search": [
"eng.myprime.com",
"dev.eng.myprime.com",
"labs.myprime.com",
"qa.myprime.com"
],
"nameservers": [
"10.136.17.15"
],
"options": [
"rotate",
"ndots:1"
]
}
<a id="jc.parsers.resolve_conf.parse"></a>
### parse
```python
def parse(data: str, raw: bool = False, quiet: bool = False) -> JSONDictType
```
Main text parsing function
Parameters:
data: (string) text data to parse
raw: (boolean) unprocessed output if True
quiet: (boolean) suppress warning messages if True
Returns:
Dictionary. Raw or processed structured data.
### Parser Information
Compatibility: linux, darwin, cygwin, win32, aix, freebsd
Version 1.0 by Kelly Brazil (kellyjonbrazil@gmail.com)

View File

@@ -22,6 +22,13 @@ Schema:
[
{
"interfaces": [
{
"id": string,
"mac": string,
"name": string,
}
]
"destination": string,
"gateway": string,
"genmask": string,
@@ -129,6 +136,6 @@ Returns:
List of Dictionaries. Raw or processed structured data.
### Parser Information
Compatibility: linux
Compatibility: linux, win32
Version 1.8 by Kelly Brazil (kellyjonbrazil@gmail.com)
Version 1.9 by Kelly Brazil (kellyjonbrazil@gmail.com)

View File

@@ -7,6 +7,8 @@ jc - JSON Convert Semantic Version string parser
This parser conforms to the specification at https://semver.org/
See Also: `ver` parser.
Usage (cli):
$ echo 1.2.3-rc.1+44837 | jc --semver

136
docs/parsers/srt.md Normal file
View File

@@ -0,0 +1,136 @@
[Home](https://kellyjonbrazil.github.io/jc/)
<a id="jc.parsers.srt"></a>
# jc.parsers.srt
jc - JSON Convert `SRT` file parser
Usage (cli):
$ cat foo.srt | jc --srt
Usage (module):
import jc
result = jc.parse('srt', srt_file_output)
Schema:
[
{
"index": int,
"start": {
"hours": int,
"minutes": int,
"seconds": int,
"milliseconds": int,
"timestamp": string
},
"end": {
"hours": int,
"minutes": int,
"seconds": int,
"milliseconds": int,
"timestamp": string
},
"content": string
}
]
Examples:
$ cat attack_of_the_clones.srt
1
00:02:16,612 --> 00:02:19,376
Senator, we're making
our final approach into Coruscant.
2
00:02:19,482 --> 00:02:21,609
Very good, Lieutenant.
...
$ cat attack_of_the_clones.srt | jc --srt
[
{
"index": 1,
"start": {
"hours": 0,
"minutes": 2,
"seconds": 16,
"milliseconds": 612,
"timestamp": "00:02:16,612"
},
"end": {
"hours": 0,
"minutes": 2,
"seconds": 19,
"milliseconds": 376,
"timestamp": "00:02:19,376"
},
"content": "Senator, we're making\nour final approach into Coruscant."
},
{
"index": 2,
"start": {
"hours": 0,
"minutes": 2,
"seconds": 19,
"milliseconds": 482,
"timestamp": "00:02:19,482"
},
"end": {
"hours": 0,
"minutes": 2,
"seconds": 21,
"milliseconds": 609,
"timestamp": "00:02:21,609"
},
"content": "Very good, Lieutenant."
},
...
]
<a id="jc.parsers.srt.parse_timestamp"></a>
### parse\_timestamp
```python
def parse_timestamp(timestamp: str) -> Dict
```
timestamp: "hours:minutes:seconds,milliseconds" --->
{
"hours": "hours",
"minutes": "minutes",
"seconds": "seconds",
"milliseconds": "milliseconds",
"timestamp": "hours:minutes:seconds,milliseconds"
}
<a id="jc.parsers.srt.parse"></a>
### parse
```python
def parse(data: str,
raw: bool = False,
quiet: bool = False) -> List[JSONDictType]
```
Main text parsing function
Parameters:
data: (string) text data to parse
raw: (boolean) unprocessed output if True
quiet: (boolean) suppress warning messages if True
Returns:
Dictionary. Raw or processed structured data.
### Parser Information
Compatibility: linux, darwin, cygwin, win32, aix, freebsd
Version 1.0 by Mark Rotner (rotner.mr@gmail.com)

View File

@@ -5,9 +5,6 @@
jc - JSON Convert `ss` command output parser
Extended information options like `-e` and `-p` are not supported and may
cause parsing irregularities.
Usage (cli):
$ ss | jc --ss
@@ -28,21 +25,29 @@ field names
[
{
"netid": string,
"state": string,
"recv_q": integer,
"send_q": integer,
"local_address": string,
"local_port": string,
"local_port_num": integer,
"peer_address": string,
"peer_port": string,
"peer_port_num": integer,
"interface": string,
"link_layer" string,
"channel": string,
"path": string,
"pid": integer
"netid": string,
"state": string,
"recv_q": integer,
"send_q": integer,
"local_address": string,
"local_port": string,
"local_port_num": integer,
"peer_address": string,
"peer_port": string,
"peer_port_num": integer,
"interface": string,
"link_layer" string,
"channel": string,
"path": string,
"pid": integer,
"opts": {
"process_id": {
"<process_id>": {
"user": string,
"file_descriptor": string
}
}
}
}
]
@@ -303,4 +308,4 @@ Returns:
### Parser Information
Compatibility: linux
Version 1.6 by Kelly Brazil (kellyjonbrazil@gmail.com)
Version 1.7 by Kelly Brazil (kellyjonbrazil@gmail.com)

548
docs/parsers/ssh_conf.md Normal file
View 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)

View File

@@ -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)

View File

@@ -193,4 +193,4 @@ Returns:
### Parser Information
Compatibility: linux, darwin, freebsd
Version 1.12 by Kelly Brazil (kellyjonbrazil@gmail.com)
Version 1.13 by Kelly Brazil (kellyjonbrazil@gmail.com)

View File

@@ -5,6 +5,8 @@
jc - JSON Convert `timedatectl` command output parser
Also supports the `timesync-status` option.
The `epoch_utc` calculated timestamp field is timezone-aware and is only
available if the `universal_time` field is available.
@@ -34,7 +36,24 @@ Schema:
"system_clock_synchronized": boolean,
"systemd-timesyncd.service_active": boolean,
"rtc_in_local_tz": boolean,
"dst_active": boolean
"dst_active": boolean,
"server": string,
"poll_interval": string,
"leap": string,
"version": integer,
"stratum": integer,
"reference": string,
"precision": string,
"root_distance": string,
"offset": float,
"offset_unit": string,
"delay": float,
"delay_unit": string,
"jitter": float,
"jitter_unit": string,
"packet_count": integer,
"frequency": float,
"frequency_unit": string
}
Examples:
@@ -87,4 +106,4 @@ Returns:
### Parser Information
Compatibility: linux
Version 1.7 by Kelly Brazil (kellyjonbrazil@gmail.com)
Version 1.8 by Kelly Brazil (kellyjonbrazil@gmail.com)

View File

@@ -161,4 +161,4 @@ Returns:
### Parser Information
Compatibility: linux
Version 1.2 by Kelly Brazil (kellyjonbrazil@gmail.com)
Version 1.3 by Kelly Brazil (kellyjonbrazil@gmail.com)

View File

@@ -42,8 +42,7 @@ Parameters:
underscore '_'. You should also ensure headers are
lowercase by using .lower().
Also, ensure there are no blank lines (list items)
in the data.
Also, ensure there are no blank rows in the data.
Returns:

112
docs/parsers/ver.md Normal file
View File

@@ -0,0 +1,112 @@
[Home](https://kellyjonbrazil.github.io/jc/)
<a id="jc.parsers.ver"></a>
# jc.parsers.ver
jc - JSON Convert Version string output parser
Best-effort attempt to parse various styles of version numbers. This parser
is based off of the version parser included in the CPython distutils
library.
If the version string conforms to some de facto-standard versioning rules
followed by many developers a `strict` key will be present in the output
with a value of `true` along with the named parsed components.
All other version strings will have a `strict` value of `false` and a
`components` key will contain a list of detected parts of the version
string.
See Also: `semver` parser.
Usage (cli):
$ echo 1.2a1 | jc --ver
Usage (module):
import jc
result = jc.parse('ver', version_string_output)
Schema:
{
"major": integer,
"minor": integer,
"patch": integer,
"prerelease": string,
"prerelease_num": integer,
"components": [
integer/string
],
"strict": boolean
}
Examples:
$ echo 1.2a1 | jc --ver -p
{
"major": 1,
"minor": 2,
"patch": 0,
"prerelease": "a",
"prerelease_num": 1,
"strict": true
}
$ echo 1.2a1 | jc --ver -p -r
{
"major": "1",
"minor": "2",
"patch": "0",
"prerelease": "a",
"prerelease_num": "1",
"strict": true
}
$ echo 1.2beta3 | jc --ver -p
{
"components": [
1,
2,
"beta",
3
],
"strict": false
}
$ echo 1.2beta3 | jc --ver -p -r
{
"components": [
"1",
"2",
"beta",
"3"
],
"strict": false
}
<a id="jc.parsers.ver.parse"></a>
### parse
```python
def parse(data: str, raw: bool = False, quiet: bool = False) -> JSONDictType
```
Main text parsing function
Parameters:
data: (string) text data to parse
raw: (boolean) unprocessed output if True
quiet: (boolean) suppress warning messages if True
Returns:
List of Dictionaries. Raw or processed structured data.
### Parser Information
Compatibility: linux, darwin, cygwin, win32, aix, freebsd
Version 1.0 by Kelly Brazil (kellyjonbrazil@gmail.com)

108
docs/parsers/veracrypt.md Normal file
View File

@@ -0,0 +1,108 @@
[Home](https://kellyjonbrazil.github.io/jc/)
<a id="jc.parsers.veracrypt"></a>
# jc.parsers.veracrypt
jc - JSON Convert `veracrypt` command output parser
Supports the following `veracrypt` subcommands:
- `veracrypt --text --list`
- `veracrypt --text --list --verbose`
- `veracrypt --text --volume-properties <volume>`
Usage (cli):
$ veracrypt --text --list | jc --veracrypt
or
$ jc veracrypt --text --list
Usage (module):
import jc
result = jc.parse('veracrypt', veracrypt_command_output)
Schema:
Volume:
[
{
"slot": integer,
"path": string,
"device": string,
"mountpoint": string,
"size": string,
"type": string,
"readonly": string,
"hidden_protected": string,
"encryption_algo": string,
"pk_size": string,
"sk_size": string,
"block_size": string,
"mode": string,
"prf": string,
"format_version": integer,
"backup_header": string
}
]
Examples:
$ veracrypt --text --list | jc --veracrypt -p
[
{
"slot": 1,
"path": "/dev/sdb1",
"device": "/dev/mapper/veracrypt1",
"mountpoint": "/home/bob/mount/encrypt/sdb1"
}
]
$ veracrypt --text --list --verbose | jc --veracrypt -p
[
{
"slot": 1,
"path": "/dev/sdb1",
"device": "/dev/mapper/veracrypt1",
"mountpoint": "/home/bob/mount/encrypt/sdb1",
"size": "522 MiB",
"type": "Normal",
"readonly": "No",
"hidden_protected": "No",
"encryption_algo": "AES",
"pk_size": "256 bits",
"sk_size": "256 bits",
"block_size": "128 bits",
"mode": "XTS",
"prf": "HMAC-SHA-512",
"format_version": 2,
"backup_header": "Yes"
}
]
<a id="jc.parsers.veracrypt.parse"></a>
### parse
```python
def parse(data: str,
raw: bool = False,
quiet: bool = False) -> List[JSONDictType]
```
Main text parsing function
Parameters:
data: (string) text data to parse
raw: (boolean) unprocessed output if True
quiet: (boolean) suppress warning messages if True
Returns:
List of Dictionaries. Raw or processed structured data.
### Parser Information
Compatibility: linux
Version 1.0 by Jake Ob (iakopap at gmail.com)

View File

@@ -433,4 +433,4 @@ Returns:
### Parser Information
Compatibility: linux, darwin, cygwin, win32, aix, freebsd
Version 1.1 by Kelly Brazil (kellyjonbrazil@gmail.com)
Version 1.3 by Kelly Brazil (kellyjonbrazil@gmail.com)

282
docs/parsers/x509_csr.md Normal file
View File

@@ -0,0 +1,282 @@
[Home](https://kellyjonbrazil.github.io/jc/)
<a id="jc.parsers.x509_csr"></a>
# jc.parsers.x509\_csr
jc - JSON Convert X.509 Certificate Request format file parser
This parser will convert DER and PEM encoded X.509 certificate request files.
Usage (cli):
$ cat certificateRequest.pem | jc --x509-csr
Usage (module):
import jc
result = jc.parse('x509_csr', x509_csr_file_output)
Schema:
[
{
"certification_request_info": {
"version": string,
"serial_number": string, # [0]
"serial_number_str": string,
"signature": {
"algorithm": string,
"parameters": string/null,
},
"issuer": {
"country_name": string,
"state_or_province_name" string,
"locality_name": string,
"organization_name": array/string,
"organizational_unit_name": array/string,
"common_name": string,
"email_address": string,
"serial_number": string, # [0]
"serial_number_str": string
},
"validity": {
"not_before": integer, # [1]
"not_after": integer, # [1]
"not_before_iso": string,
"not_after_iso": string
},
"subject": {
"country_name": string,
"state_or_province_name": string,
"locality_name": string,
"organization_name": array/string,
"organizational_unit_name": array/string,
"common_name": string,
"email_address": string,
"serial_number": string, # [0]
"serial_number_str": string
},
"subject_public_key_info": {
"algorithm": {
"algorithm": string,
"parameters": string/null,
},
"public_key": {
"modulus": string, # [0]
"public_exponent": integer
}
},
"issuer_unique_id": string/null,
"subject_unique_id": string/null,
"extensions": [
{
"extn_id": string,
"critical": boolean,
"extn_value": array/object/string/integer # [2]
}
]
},
"signature_algorithm": {
"algorithm": string,
"parameters": string/null
},
"signature_value": string # [0]
}
]
[0] in colon-delimited hex notation
[1] time-zone-aware (UTC) epoch timestamp
[2] See below for well-known Extension schemas:
Basic Constraints:
{
"extn_id": "basic_constraints",
"critical": boolean,
"extn_value": {
"ca": boolean,
"path_len_constraint": string/null
}
}
Key Usage:
{
"extn_id": "key_usage",
"critical": boolean,
"extn_value": [
string
]
}
Key Identifier:
{
"extn_id": "key_identifier",
"critical": boolean,
"extn_value": string # [0]
}
Authority Key Identifier:
{
"extn_id": "authority_key_identifier",
"critical": boolean,
"extn_value": {
"key_identifier": string, # [0]
"authority_cert_issuer": string/null,
"authority_cert_serial_number": string/null
}
}
Subject Alternative Name:
{
"extn_id": "subject_alt_name",
"critical": boolean,
"extn_value": [
string
]
}
Certificate Policies:
{
"extn_id": "certificate_policies",
"critical": boolean,
"extn_value": [
{
"policy_identifier": string,
"policy_qualifiers": [ array or null
{
"policy_qualifier_id": string,
"qualifier": string
}
]
}
]
}
Signed Certificate Timestamp List:
{
"extn_id": "signed_certificate_timestamp_list",
"critical": boolean,
"extn_value": string # [0]
}
Examples:
$ cat server.csr| jc --x509-csr -p
[
{
"certification_request_info": {
"version": "v1",
"subject": {
"common_name": "myserver.for.example"
},
"subject_pk_info": {
"algorithm": {
"algorithm": "ec",
"parameters": "secp256r1"
},
"public_key": "04:40:33:c0:91:8f:e9:46:ea:d0:dc:d0:f9:63:2..."
},
"attributes": [
{
"type": "extension_request",
"values": [
[
{
"extn_id": "extended_key_usage",
"critical": false,
"extn_value": [
"server_auth"
]
},
{
"extn_id": "subject_alt_name",
"critical": false,
"extn_value": [
"myserver.for.example"
]
}
]
]
}
]
},
"signature_algorithm": {
"algorithm": "sha384_ecdsa",
"parameters": null
},
"signature": "30:45:02:20:77:ac:5b:51:bf:c5:f5:43:02:52:ae:66:..."
}
]
$ openssl req -in server.csr | jc --x509-csr -p
[
{
"certification_request_info": {
"version": "v1",
"subject": {
"common_name": "myserver.for.example"
},
"subject_pk_info": {
"algorithm": {
"algorithm": "ec",
"parameters": "secp256r1"
},
"public_key": "04:40:33:c0:91:8f:e9:46:ea:d0:dc:d0:f9:63:2..."
},
"attributes": [
{
"type": "extension_request",
"values": [
[
{
"extn_id": "extended_key_usage",
"critical": false,
"extn_value": [
"server_auth"
]
},
{
"extn_id": "subject_alt_name",
"critical": false,
"extn_value": [
"myserver.for.example"
]
}
]
]
}
]
},
"signature_algorithm": {
"algorithm": "sha384_ecdsa",
"parameters": null
},
"signature": "30:45:02:20:77:ac:5b:51:bf:c5:f5:43:02:52:ae:66:..."
}
]
<a id="jc.parsers.x509_csr.parse"></a>
### parse
```python
def parse(data: Union[str, bytes],
raw: bool = False,
quiet: bool = False) -> List[Dict]
```
Main text parsing function
Parameters:
data: (string or bytes) text or binary data to parse
raw: (boolean) unprocessed output if True
quiet: (boolean) suppress warning messages if True
Returns:
List of Dictionaries. Raw or processed structured data.
### Parser Information
Compatibility: linux, darwin, cygwin, win32, aix, freebsd
Version 1.0 by Kelly Brazil (kellyjonbrazil@gmail.com)

View File

@@ -98,4 +98,4 @@ Returns:
### Parser Information
Compatibility: linux, darwin, cygwin, win32, aix, freebsd
Version 1.7 by Kelly Brazil (kellyjonbrazil@gmail.com)
Version 1.8 by Kelly Brazil (kellyjonbrazil@gmail.com)

View File

@@ -8,6 +8,7 @@ jc - JSON Convert `xrandr` command output parser
Usage (cli):
$ xrandr | jc --xrandr
$ xrandr --properties | jc --xrandr
or
@@ -30,8 +31,8 @@ Schema:
"current_height": integer,
"maximum_width": integer,
"maximum_height": integer,
"associated_device": {
"associated_modes": [
"devices": {
"modes": [
{
"resolution_width": integer,
"resolution_height": integer,
@@ -49,33 +50,19 @@ Schema:
"is_connected": boolean,
"is_primary": boolean,
"device_name": string,
"model_name": string,
"product_id" string,
"serial_number": string,
"resolution_width": integer,
"resolution_height": integer,
"offset_width": integer,
"offset_height": integer,
"dimension_width": integer,
"dimension_height": integer,
"rotation": string
"rotation": string,
"reflection": string
}
],
"unassociated_devices": [
{
"associated_modes": [
{
"resolution_width": integer,
"resolution_height": integer,
"is_high_resolution": boolean,
"frequencies": [
{
"frequency": float,
"is_current": boolean,
"is_preferred": boolean
}
]
}
]
}
]
}
Examples:
@@ -91,8 +78,8 @@ Examples:
"current_height": 1080,
"maximum_width": 32767,
"maximum_height": 32767,
"associated_device": {
"associated_modes": [
"devices": {
"modes": [
{
"resolution_width": 1920,
"resolution_height": 1080,
@@ -132,11 +119,73 @@ Examples:
"offset_height": 0,
"dimension_width": 310,
"dimension_height": 170,
"rotation": "normal"
"rotation": "normal",
"reflection": "normal"
}
}
],
"unassociated_devices": []
]
}
$ xrandr --properties | jc --xrandr -p
{
"screens": [
{
"screen_number": 0,
"minimum_width": 8,
"minimum_height": 8,
"current_width": 1920,
"current_height": 1080,
"maximum_width": 32767,
"maximum_height": 32767,
"devices": {
"modes": [
{
"resolution_width": 1920,
"resolution_height": 1080,
"is_high_resolution": false,
"frequencies": [
{
"frequency": 60.03,
"is_current": true,
"is_preferred": true
},
{
"frequency": 59.93,
"is_current": false,
"is_preferred": false
}
]
},
{
"resolution_width": 1680,
"resolution_height": 1050,
"is_high_resolution": false,
"frequencies": [
{
"frequency": 59.88,
"is_current": false,
"is_preferred": false
}
]
}
],
"is_connected": true,
"is_primary": true,
"device_name": "eDP1",
"model_name": "ASUS VW193S",
"product_id": "54297",
"serial_number": "78L8021107",
"resolution_width": 1920,
"resolution_height": 1080,
"offset_width": 0,
"offset_height": 0,
"dimension_width": 310,
"dimension_height": 170,
"rotation": "normal",
"reflection": "normal"
}
}
]
}
<a id="jc.parsers.xrandr.parse"></a>
@@ -162,4 +211,4 @@ Returns:
### Parser Information
Compatibility: linux, darwin, cygwin, aix, freebsd
Version 1.1 by Kevin Lyter (lyter_git at sent.com)
Version 1.3 by Kevin Lyter (code (at) lyterk.com)

View 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)

View File

@@ -0,0 +1,163 @@
[Home](https://kellyjonbrazil.github.io/jc/)
<a id="jc.parsers.zpool_status"></a>
# jc.parsers.zpool\_status
jc - JSON Convert `zpool status` command output parser
Works with or without the `-v` option.
Usage (cli):
$ zpool status | jc --zpool-status
or
$ jc zpool status
Usage (module):
import jc
result = jc.parse('zpool_status', zpool_status_command_output)
Schema:
[
{
"pool": string,
"state": string,
"status": string,
"action": string,
"see": string,
"scan": string,
"scrub": string,
"config": [
{
"name": string,
"state": string,
"read": integer,
"write": integer,
"checksum": integer,
"errors": string,
}
],
"errors": string
}
]
Examples:
$ zpool status -v | jc --zpool-status -p
[
{
"pool": "tank",
"state": "DEGRADED",
"status": "One or more devices could not be opened. Suffic...",
"action": "Attach the missing device and online it using 'zpool...",
"see": "http://www.sun.com/msg/ZFS-8000-2Q",
"scrub": "none requested",
"config": [
{
"name": "tank",
"state": "DEGRADED",
"read": 0,
"write": 0,
"checksum": 0
},
{
"name": "mirror-0",
"state": "DEGRADED",
"read": 0,
"write": 0,
"checksum": 0
},
{
"name": "c1t0d0",
"state": "ONLINE",
"read": 0,
"write": 0,
"checksum": 0
},
{
"name": "c1t1d0",
"state": "UNAVAIL",
"read": 0,
"write": 0,
"checksum": 0,
"errors": "cannot open"
}
],
"errors": "No known data errors"
}
]
$ zpool status -v | jc --zpool-status -p -r
[
{
"pool": "tank",
"state": "DEGRADED",
"status": "One or more devices could not be opened. Sufficient...",
"action": "Attach the missing device and online it using 'zpool...",
"see": "http://www.sun.com/msg/ZFS-8000-2Q",
"scrub": "none requested",
"config": [
{
"name": "tank",
"state": "DEGRADED",
"read": "0",
"write": "0",
"checksum": "0"
},
{
"name": "mirror-0",
"state": "DEGRADED",
"read": "0",
"write": "0",
"checksum": "0"
},
{
"name": "c1t0d0",
"state": "ONLINE",
"read": "0",
"write": "0",
"checksum": "0"
},
{
"name": "c1t1d0",
"state": "UNAVAIL",
"read": "0",
"write": "0",
"checksum": "0",
"errors": "cannot open"
}
],
"errors": "No known data errors"
}
]
<a id="jc.parsers.zpool_status.parse"></a>
### parse
```python
def parse(data: str,
raw: bool = False,
quiet: bool = False) -> List[JSONDictType]
```
Main text parsing function
Parameters:
data: (string) text data to parse
raw: (boolean) unprocessed output if True
quiet: (boolean) suppress warning messages if True
Returns:
List of Dictionaries. Raw or processed structured data.
### Parser Information
Compatibility: linux, darwin, freebsd
Version 1.1 by Kelly Brazil (kellyjonbrazil@gmail.com)

View File

@@ -124,6 +124,14 @@ Get a list of streaming parser module names to be used in
`parse()`, `parser_info()`, and `get_help()`. This list is a subset of
`parser_mod_list()`.
"""
from .lib import (__version__, parse, parser_mod_list, plugin_parser_mod_list,
standard_parser_mod_list, streaming_parser_mod_list,
parser_info, all_parser_info, get_help)
from .lib import (
__version__ as __version__,
parse as parse,
parser_mod_list as parser_mod_list,
plugin_parser_mod_list as plugin_parser_mod_list,
standard_parser_mod_list as standard_parser_mod_list,
streaming_parser_mod_list as streaming_parser_mod_list,
parser_info as parser_info,
all_parser_info as all_parser_info,
get_help as get_help
)

255
jc/cli.py
View File

@@ -5,11 +5,13 @@ JC cli module
import io
import sys
import os
import re
from itertools import islice
from datetime import datetime, timezone
import textwrap
import shlex
import subprocess
from typing import List, Dict, Union, Optional, TextIO
from typing import List, Dict, Iterable, Union, Optional, TextIO
from types import ModuleType
from .lib import (
__version__, parser_info, all_parser_info, parsers, _get_parser, _parser_is_streaming,
@@ -40,6 +42,10 @@ except Exception:
JC_CLEAN_EXIT: int = 0
JC_ERROR_EXIT: int = 100
MAX_EXIT: int = 255
SLICER_PATTERN: str = r'-?[0-9]*\:-?[0-9]*$'
SLICER_RE = re.compile(SLICER_PATTERN)
NEWLINES_PATTERN: str = r'(\r\n|\r|\n)'
NEWLINES_RE = re.compile(NEWLINES_PATTERN)
class info():
@@ -69,11 +75,11 @@ class JcCli():
'help_me', 'pretty', 'quiet', 'ignore_exceptions', 'raw', 'meta_out', 'unbuffer',
'version_info', 'yaml_output', 'bash_comp', 'zsh_comp', 'magic_found_parser',
'magic_options', 'magic_run_command', 'magic_run_command_str', 'magic_stdout',
'magic_stderr', 'magic_returncode'
'magic_stderr', 'magic_returncode', 'slice_str', 'slice_start', 'slice_end'
)
def __init__(self) -> None:
self.data_in: Optional[Union[str, bytes, TextIO]] = None
self.data_in: Optional[Union[str, bytes, TextIO, Iterable[str]]] = None
self.data_out: Optional[Union[List[JSONDictType], JSONDictType]] = None
self.options: List[str] = []
self.args: List[str] = []
@@ -89,6 +95,11 @@ class JcCli():
self.json_indent: Optional[int] = None
self.run_timestamp: Optional[datetime] = None
# slicer
self.slice_str: str = ''
self.slice_start: Optional[int] = None
self.slice_end: Optional[int] = None
# cli options
self.about: bool = False
self.debug: bool = False
@@ -134,33 +145,34 @@ class JcCli():
JC_COLORS=blue,brightblack,magenta,green
JC_COLORS=default,default,default,default
"""
input_error = False
env_colors = os.getenv('JC_COLORS')
if PYGMENTS_INSTALLED:
input_error = False
env_colors = os.getenv('JC_COLORS')
if env_colors:
color_list = env_colors.split(',')
else:
color_list = ['default', 'default', 'default', 'default']
if env_colors:
color_list = env_colors.split(',')
else:
color_list = ['default', 'default', 'default', 'default']
if len(color_list) != 4:
input_error = True
for color in color_list:
if color != 'default' and color not in PYGMENT_COLOR:
if len(color_list) != 4:
input_error = True
# if there is an issue with the env variable, just set all colors to default and move on
if input_error:
utils.warning_message(['Could not parse JC_COLORS environment variable'])
color_list = ['default', 'default', 'default', 'default']
for color in color_list:
if color != 'default' and color not in PYGMENT_COLOR:
input_error = True
# Try the color set in the JC_COLORS env variable first. If it is set to default, then fall back to default colors
self.custom_colors = {
Name.Tag: f'bold {PYGMENT_COLOR[color_list[0]]}' if color_list[0] != 'default' else f"bold {PYGMENT_COLOR['blue']}", # key names
Keyword: PYGMENT_COLOR[color_list[1]] if color_list[1] != 'default' else PYGMENT_COLOR['brightblack'], # true, false, null
Number: PYGMENT_COLOR[color_list[2]] if color_list[2] != 'default' else PYGMENT_COLOR['magenta'], # numbers
String: PYGMENT_COLOR[color_list[3]] if color_list[3] != 'default' else PYGMENT_COLOR['green'] # strings
}
# if there is an issue with the env variable, just set all colors to default and move on
if input_error:
utils.warning_message(['Could not parse JC_COLORS environment variable'])
color_list = ['default', 'default', 'default', 'default']
# Try the color set in the JC_COLORS env variable first. If it is set to default, then fall back to default colors
self.custom_colors = {
Name.Tag: f'bold {PYGMENT_COLOR[color_list[0]]}' if color_list[0] != 'default' else f"bold {PYGMENT_COLOR['blue']}", # key names
Keyword: PYGMENT_COLOR[color_list[1]] if color_list[1] != 'default' else PYGMENT_COLOR['brightblack'], # true, false, null
Number: PYGMENT_COLOR[color_list[2]] if color_list[2] != 'default' else PYGMENT_COLOR['magenta'], # numbers
String: PYGMENT_COLOR[color_list[3]] if color_list[3] != 'default' else PYGMENT_COLOR['green'] # strings
}
def set_mono(self) -> None:
"""
@@ -204,14 +216,14 @@ class JcCli():
category_text: str = ''
padding_char: str = ' '
all_parsers = all_parser_info(show_hidden=True, show_deprecated=False)
generic = [{'arg': x['argument'], 'desc': x['description']} for x in all_parsers if 'generic' in x['tags']]
standard = [{'arg': x['argument'], 'desc': x['description']} for x in all_parsers if 'standard' in x['tags']]
command = [{'arg': x['argument'], 'desc': x['description']} for x in all_parsers if 'command' in x['tags']]
generic = [{'arg': x['argument'], 'desc': x['description']} for x in all_parsers if 'generic' in x.get('tags', [])]
standard = [{'arg': x['argument'], 'desc': x['description']} for x in all_parsers if 'standard' in x.get('tags', [])]
command = [{'arg': x['argument'], 'desc': x['description']} for x in all_parsers if 'command' in x.get('tags', [])]
file_str_bin = [
{'arg': x['argument'], 'desc': x['description']} for x in all_parsers
if 'file' in x['tags'] or
'string' in x['tags'] or
'binary' in x['tags']
if 'file' in x.get('tags', []) or
'string' in x.get('tags', []) or
'binary' in x.get('tags', [])
]
streaming = [{'arg': x['argument'], 'desc': x['description']} for x in all_parsers if x.get('streaming')]
categories: Dict = {
@@ -432,6 +444,17 @@ class JcCli():
self.magic_options = []
return
# slicer found
if ':' in arg:
if SLICER_RE.match(arg):
self.slice_str = arg
args_given.pop(0)
continue
else:
utils.warning_message(['Invalid slice syntax.'])
args_given.pop(0)
continue
# option found - populate option list
if arg.startswith('-'):
self.magic_options.extend(args_given.pop(0)[1:])
@@ -574,59 +597,6 @@ class JcCli():
utils.error_message(['Missing piped data. Use "jc -h" for help.'])
self.exit_error()
def streaming_parse_and_print(self) -> None:
"""only supports UTF-8 string data for now"""
self.data_in = sys.stdin
if self.parser_module:
result = self.parser_module.parse(
self.data_in,
raw=self.raw,
quiet=self.quiet,
ignore_exceptions=self.ignore_exceptions
)
for line in result:
self.data_out = line
if self.meta_out:
self.run_timestamp = datetime.now(timezone.utc)
self.add_metadata_to_output()
self.safe_print_out()
def standard_parse_and_print(self) -> None:
"""supports binary and UTF-8 string data"""
self.data_in = self.magic_stdout or sys.stdin.buffer.read()
# convert to UTF-8, if possible. Otherwise, leave as bytes
try:
if isinstance(self.data_in, bytes):
self.data_in = self.data_in.decode('utf-8')
except UnicodeDecodeError:
pass
if self.parser_module:
self.data_out = self.parser_module.parse(
self.data_in,
raw=self.raw,
quiet=self.quiet
)
if self.meta_out:
self.run_timestamp = datetime.now(timezone.utc)
self.add_metadata_to_output()
self.safe_print_out()
def exit_clean(self) -> None:
exit_code: int = self.magic_returncode + JC_CLEAN_EXIT
exit_code = min(exit_code, MAX_EXIT)
sys.exit(exit_code)
def exit_error(self) -> None:
exit_code: int = self.magic_returncode + JC_ERROR_EXIT
exit_code = min(exit_code, MAX_EXIT)
sys.exit(exit_code)
def add_metadata_to_output(self) -> None:
"""
This function mutates data_out in place. If the _jc_meta field
@@ -641,7 +611,9 @@ class JcCli():
if self.run_timestamp:
meta_obj: JSONDictType = {
'parser': self.parser_name,
'timestamp': self.run_timestamp.timestamp()
'timestamp': self.run_timestamp.timestamp(),
'slice_start': self.slice_start,
'slice_end': self.slice_end
}
if self.magic_run_command:
@@ -669,6 +641,116 @@ class JcCli():
utils.error_message(['Parser returned an unsupported object type.'])
self.exit_error()
@staticmethod
def lazy_splitlines(text: str) -> Iterable[str]:
start = 0
for m in NEWLINES_RE.finditer(text):
begin, end = m.span()
if begin != start:
yield text[start:begin]
start = end
if text[start:]:
yield text[start:]
def slicer(self) -> None:
"""Slice input data lazily, if possible. Updates self.data_in"""
if self.slice_str:
slice_start_str, slice_end_str = self.slice_str.split(':', maxsplit=1)
if slice_start_str:
self.slice_start = int(slice_start_str)
if slice_end_str:
self.slice_end = int(slice_end_str)
if not self.slice_start is None or not self.slice_end is None:
# standard parsers UTF-8 input
if isinstance(self.data_in, str):
data_in_iter = self.lazy_splitlines(self.data_in)
# positive slices
if (self.slice_start is None or self.slice_start >= 0) \
and (self.slice_end is None or self.slice_end >= 0):
self.data_in = '\n'.join(islice(data_in_iter, self.slice_start, self.slice_end))
# negative slices found (non-lazy, uses more memory)
else:
self.data_in = '\n'.join(list(data_in_iter)[self.slice_start:self.slice_end])
# standard parsers bytes input
elif isinstance(self.data_in, bytes):
utils.warning_message(['Cannot slice bytes data.'])
# streaming parsers UTF-8 input
else:
# positive slices
if (self.slice_start is None or self.slice_start >= 0) \
and (self.slice_end is None or self.slice_end >= 0) \
and self.data_in:
self.data_in = islice(self.data_in, self.slice_start, self.slice_end)
# negative slices found (non-lazy, uses more memory)
elif self.data_in:
self.data_in = list(self.data_in)[self.slice_start:self.slice_end]
def streaming_parse_and_print(self) -> None:
"""only supports UTF-8 string data for now"""
self.data_in = sys.stdin
self.slicer()
if self.parser_module:
result = self.parser_module.parse(
self.data_in,
raw=self.raw,
quiet=self.quiet,
ignore_exceptions=self.ignore_exceptions
)
for line in result:
self.data_out = line
if self.meta_out:
self.run_timestamp = datetime.now(timezone.utc)
self.add_metadata_to_output()
self.safe_print_out()
def standard_parse_and_print(self) -> None:
"""supports binary and UTF-8 string data"""
self.data_in = self.magic_stdout or sys.stdin.buffer.read()
# convert to UTF-8, if possible. Otherwise, leave as bytes
try:
if isinstance(self.data_in, bytes):
self.data_in = self.data_in.decode('utf-8')
except UnicodeDecodeError:
pass
self.slicer()
if self.parser_module:
self.data_out = self.parser_module.parse(
self.data_in,
raw=self.raw,
quiet=self.quiet
)
if self.meta_out:
self.run_timestamp = datetime.now(timezone.utc)
self.add_metadata_to_output()
self.safe_print_out()
def exit_clean(self) -> None:
exit_code: int = self.magic_returncode + JC_CLEAN_EXIT
exit_code = min(exit_code, MAX_EXIT)
sys.exit(exit_code)
def exit_error(self) -> None:
exit_code: int = self.magic_returncode + JC_ERROR_EXIT
exit_code = min(exit_code, MAX_EXIT)
sys.exit(exit_code)
def _run(self) -> None:
# enable colors for Windows cmd.exe terminal
if sys.platform.startswith('win32'):
@@ -684,6 +766,9 @@ class JcCli():
# find options if magic_parser did not find a command
if not self.magic_found_parser:
for opt in self.args:
if SLICER_RE.match(opt):
self.slice_str = opt
if opt in long_options_map:
self.options.extend(long_options_map[opt][0])

View File

@@ -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

View File

@@ -3,13 +3,13 @@ import sys
import os
import re
import importlib
from typing import List, Iterable, Union, Iterator
from typing import List, Iterable, Optional, Union, Iterator
from types import ModuleType
from .jc_types import ParserInfoType, JSONDictType
from jc import appdirs
__version__ = '1.22.5'
__version__ = '1.23.5'
parsers: List[str] = [
'acpi',
@@ -19,9 +19,11 @@ parsers: List[str] = [
'asciitable',
'asciitable-m',
'blkid',
'bluetoothctl',
'cbt',
'cef',
'cef-s',
'certbot',
'chage',
'cksum',
'clf',
@@ -41,6 +43,7 @@ parsers: List[str] = [
'email-address',
'env',
'file',
'find',
'findmnt',
'finger',
'free',
@@ -55,6 +58,7 @@ parsers: List[str] = [
'hashsum',
'hciconfig',
'history',
'host',
'hosts',
'id',
'ifconfig',
@@ -64,6 +68,7 @@ parsers: List[str] = [
'iostat-s',
'ip-address',
'iptables',
'ip-route',
'iso-datetime',
'iw-scan',
'iwconfig',
@@ -74,6 +79,8 @@ parsers: List[str] = [
'last',
'ls',
'ls-s',
'lsattr',
'lsb-release',
'lsblk',
'lsmod',
'lsof',
@@ -86,9 +93,11 @@ parsers: List[str] = [
'mpstat-s',
'netstat',
'nmcli',
'nsd-control',
'ntpq',
'openvpn',
'os-prober',
'os-release',
'passwd',
'pci-ids',
'pgpass',
@@ -140,6 +149,7 @@ parsers: List[str] = [
'proc-net-packet',
'proc-net-protocols',
'proc-net-route',
'proc-net-tcp',
'proc-net-unix',
'proc-pid-fdinfo',
'proc-pid-io',
@@ -151,6 +161,7 @@ parsers: List[str] = [
'proc-pid-statm',
'proc-pid-status',
'ps',
'resolve-conf',
'route',
'rpm-qi',
'rsync',
@@ -158,7 +169,9 @@ parsers: List[str] = [
'semver',
'sfdisk',
'shadow',
'srt',
'ss',
'ssh-conf',
'sshd-conf',
'stat',
'stat-s',
@@ -189,16 +202,21 @@ parsers: List[str] = [
'upower',
'uptime',
'url',
'ver',
'veracrypt',
'vmstat',
'vmstat-s',
'w',
'wc',
'who',
'x509-cert',
'x509-csr',
'xml',
'xrandr',
'yaml',
'zipinfo'
'zipinfo',
'zpool-iostat',
'zpool-status'
]
def _cliname_to_modname(parser_cli_name: str) -> str:
@@ -209,6 +227,19 @@ def _modname_to_cliname(parser_mod_name: str) -> str:
"""Return module's cli name (underscores converted to dashes)"""
return parser_mod_name.replace('_', '-')
def _is_valid_parser_plugin(name: str, local_parsers_dir: str) -> bool:
if re.match(r'\w+\.py$', name) and os.path.isfile(os.path.join(local_parsers_dir, name)):
try:
parser_mod_name = _cliname_to_modname(name)[0:-3]
modpath = 'jcparsers.'
plugin = importlib.import_module(f'{modpath}{parser_mod_name}')
if hasattr(plugin, 'info') and hasattr(plugin, 'parse'):
del plugin
return True
except Exception:
return False
return False
# Create the local_parsers list. This is a list of custom or
# override parsers from <user_data_dir>/jc/jcparsers/*.py.
# Once this list is created, extend the parsers list with it.
@@ -218,7 +249,7 @@ local_parsers_dir = os.path.join(data_dir, 'jcparsers')
if os.path.isdir(local_parsers_dir):
sys.path.append(data_dir)
for name in os.listdir(local_parsers_dir):
if re.match(r'\w+\.py$', name) and os.path.isfile(os.path.join(local_parsers_dir, name)):
if _is_valid_parser_plugin(name, local_parsers_dir):
plugin_name = name[0:-3]
local_parsers.append(_modname_to_cliname(plugin_name))
if plugin_name not in parsers:
@@ -279,7 +310,7 @@ def parse(
data: Union[str, bytes, Iterable[str]],
quiet: bool = False,
raw: bool = False,
ignore_exceptions: bool = None,
ignore_exceptions: Optional[bool] = None,
**kwargs
) -> Union[JSONDictType, List[JSONDictType], Iterator[JSONDictType]]:
"""

View File

@@ -227,7 +227,7 @@ import jc.utils
class info():
"""Provides parser metadata (version, author, etc.)"""
version = '1.4'
version = '1.6'
description = '`acpi` command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
@@ -333,10 +333,19 @@ def parse(data, raw=False, quiet=False):
if obj_type == 'Battery':
output_line['type'] = obj_type
output_line['id'] = obj_id
if 'Charging' in line or 'Discharging' in line or 'Full' in line:
if 'Not charging' in line:
output_line['state'] = 'Not charging'
output_line['charge_percent'] = line.split()[-1].rstrip('%,')
if 'Charging' in line \
or 'Discharging' in line \
or 'Full' in line:
output_line['state'] = line.split()[2][:-1]
output_line['charge_percent'] = line.split()[3].rstrip('%,')
if 'rate information unavailable' not in line:
if 'will never fully discharge' in line:
pass
elif 'rate information unavailable' not in line:
if 'Charging' in line:
output_line['until_charged'] = line.split()[4]
if 'Discharging' in line:

View File

@@ -2333,8 +2333,8 @@ class BitString(_IntegerBitString, Constructable, Castable, Primitive, ValueMap)
if self._map:
self._native = set()
for index, bit in enumerate(bits):
if bit:
name = self._map.get(index, index)
if bit and index in self._map:
name = self._map.get(index)
self._native.add(name)
else:
self._native = bits

View File

@@ -0,0 +1 @@
quiet = False

View File

@@ -251,7 +251,18 @@ class EmailAddress(IA5String):
self._unicode = contents.decode('cp1252')
else:
mailbox, hostname = contents.rsplit(b'@', 1)
self._unicode = mailbox.decode('cp1252') + '@' + hostname.decode('idna')
# fix to allow incorrectly encoded email addresses to succeed with warning
try:
self._unicode = mailbox.decode('cp1252') + '@' + hostname.decode('idna')
except UnicodeDecodeError:
ascii_mailbox = mailbox.decode('ascii', errors='backslashreplace')
ascii_hostname = hostname.decode('ascii', errors='backslashreplace')
from jc.utils import warning_message
import jc.parsers.asn1crypto.jc_global as jc_global
if not jc_global.quiet:
warning_message([f'Invalid email address found: {ascii_mailbox}@{ascii_hostname}'])
self._unicode = ascii_mailbox + '@' + ascii_hostname
return self._unicode
def __ne__(self, other):

421
jc/parsers/bluetoothctl.py Normal file
View File

@@ -0,0 +1,421 @@
"""jc - JSON Convert `bluetoothctl` command output parser
Supports the following `bluetoothctl` subcommands:
- `bluetoothctl list`
- `bluetoothctl show`
- `bluetoothctl show <ctrl>`
- `bluetoothctl devices`
- `bluetoothctl info <dev>`
Usage (cli):
$ bluetoothctl info <dev> | jc --bluetoothctl
or
$ jc bluetoothctl info <dev>
Usage (module):
import jc
result = jc.parse('bluetoothctl', bluetoothctl_command_output)
Schema:
Because bluetoothctl is handling two main entities, controllers and devices,
the schema is shared between them. Most of the fields are common between
a controller and a device but there might be fields corresponding to one entity.
Controller:
[
{
"name": string,
"is_default": boolean,
"is_public": boolean,
"is_random": boolean,
"address": string,
"alias": string,
"class": string,
"powered": string,
"discoverable": string,
"discoverable_timeout": string,
"pairable": string,
"modalias": string,
"discovering": string,
"uuids": array
}
]
Device:
[
{
"name": string,
"is_public": boolean,
"is_random": boolean,
"address": string,
"alias": string,
"appearance": string,
"class": string,
"icon": string,
"paired": string,
"bonded": string,
"trusted": string,
"blocked": string,
"connected": string,
"legacy_pairing": string,
"rssi": int,
"txpower": int,
"uuids": array,
"modalias": string
}
]
Examples:
$ bluetoothctl info EB:06:EF:62:B3:19 | jc --bluetoothctl -p
[
{
"address": "22:06:33:62:B3:19",
"is_public": true,
"name": "TaoTronics TT-BH336",
"alias": "TaoTronics TT-BH336",
"class": "0x00240455",
"icon": "audio-headset",
"paired": "no",
"bonded": "no",
"trusted": "no",
"blocked": "no",
"connected": "no",
"legacy_pairing": "no",
"uuids": [
"Advanced Audio Distribu.. (0000120d-0000-1000-8000-00805f9b34fb)",
"Audio Sink (0000130b-0000-1000-8000-00805f9b34fb)",
"A/V Remote Control (0000140e-0000-1000-8000-00805f9b34fb)",
"A/V Remote Control Cont.. (0000150f-0000-1000-8000-00805f9b34fb)",
"Handsfree (0000161e-0000-1000-8000-00805f9b34fb)",
"Headset (00001708-0000-1000-8000-00805f9b34fb)",
"Headset HS (00001831-0000-1000-8000-00805f9b34fb)"
],
"rssi": -52,
"txpower": 4
}
]
"""
import re
from typing import List, Dict, Optional, Any
from jc.jc_types import JSONDictType
import jc.utils
class info():
"""Provides parser metadata (version, author, etc.)"""
version = '1.1'
description = '`bluetoothctl` command parser'
author = 'Jake Ob'
author_email = 'iakopap at gmail.com'
compatible = ['linux']
magic_commands = ['bluetoothctl']
tags = ['command']
__version__ = info.version
try:
from typing import TypedDict
Controller = TypedDict(
"Controller",
{
"name": str,
"is_default": bool,
"is_public": bool,
"is_random": bool,
"address": str,
"alias": str,
"class": str,
"powered": str,
"discoverable": str,
"discoverable_timeout": str,
"pairable": str,
"modalias": str,
"discovering": str,
"uuids": List[str],
},
)
Device = TypedDict(
"Device",
{
"name": str,
"is_public": bool,
"is_random": bool,
"address": str,
"alias": str,
"appearance": str,
"class": str,
"icon": str,
"paired": str,
"bonded": str,
"trusted": str,
"blocked": str,
"connected": str,
"legacy_pairing": str,
"rssi": int,
"txpower": int,
"uuids": List[str],
"modalias": str
},
)
except ImportError:
Controller = Dict[str, Any] # type: ignore
Device = Dict[str, Any] # type: ignore
_controller_head_pattern = r"Controller (?P<address>([0-9A-F]{2}:){5}[0-9A-F]{2}) (?P<name>.+)"
_controller_line_pattern = (
r"(\s*Name:\s*(?P<name>.+)"
+ r"|\s*Alias:\s*(?P<alias>.+)"
+ r"|\s*Class:\s*(?P<class>.+)"
+ r"|\s*Powered:\s*(?P<powered>.+)"
+ r"|\s*Discoverable:\s*(?P<discoverable>.+)"
+ r"|\s*DiscoverableTimeout:\s*(?P<discoverable_timeout>.+)"
+ r"|\s*Pairable:\s*(?P<pairable>.+)"
+ r"|\s*Modalias:\s*(?P<modalias>.+)"
+ r"|\s*Discovering:\s*(?P<discovering>.+)"
+ r"|\s*UUID:\s*(?P<uuid>.+))"
)
def _parse_controller(next_lines: List[str]) -> Optional[Controller]:
next_line = next_lines.pop()
result = re.match(_controller_head_pattern, next_line)
if not result:
next_lines.append(next_line)
return None
matches = result.groupdict()
name = matches["name"]
if name.endswith("not available"):
return None
controller: Controller = {
"name": '',
"is_default": False,
"is_public": False,
"is_random": False,
"address": matches["address"],
"alias": '',
"class": '',
"powered": '',
"discoverable": '',
"discoverable_timeout": '',
"pairable": '',
"modalias": '',
"discovering": '',
"uuids": [],
}
if name.endswith("[default]"):
controller["is_default"] = True
name = name.replace("[default]", "")
elif name.endswith("(public)"):
controller["is_public"] = True
name = name.replace("(public)", "")
elif name.endswith("(random)"):
controller["is_random"] = True
name = name.replace("(random)", "")
controller["name"] = name.strip()
while next_lines:
next_line = next_lines.pop()
result = re.match(_controller_line_pattern, next_line)
if not result:
next_lines.append(next_line)
return controller
matches = result.groupdict()
if matches["name"]:
controller["name"] = matches["name"]
elif matches["alias"]:
controller["alias"] = matches["alias"]
elif matches["class"]:
controller["class"] = matches["class"]
elif matches["powered"]:
controller["powered"] = matches["powered"]
elif matches["discoverable"]:
controller["discoverable"] = matches["discoverable"]
elif matches["discoverable_timeout"]:
controller["discoverable_timeout"] = matches["discoverable_timeout"]
elif matches["pairable"]:
controller["pairable"] = matches["pairable"]
elif matches["modalias"]:
controller["modalias"] = matches["modalias"]
elif matches["discovering"]:
controller["discovering"] = matches["discovering"]
elif matches["uuid"]:
if not "uuids" in controller:
controller["uuids"] = []
controller["uuids"].append(matches["uuid"])
return controller
_device_head_pattern = r"Device (?P<address>([0-9A-F]{2}:){5}[0-9A-F]{2}) (?P<name>.+)"
_device_line_pattern = (
r"(\s*Name:\s*(?P<name>.+)"
+ r"|\s*Alias:\s*(?P<alias>.+)"
+ r"|\s*Appearance:\s*(?P<appearance>.+)"
+ r"|\s*Class:\s*(?P<class>.+)"
+ r"|\s*Icon:\s*(?P<icon>.+)"
+ r"|\s*Paired:\s*(?P<paired>.+)"
+ r"|\s*Bonded:\s*(?P<bonded>.+)"
+ r"|\s*Trusted:\s*(?P<trusted>.+)"
+ r"|\s*Blocked:\s*(?P<blocked>.+)"
+ r"|\s*Connected:\s*(?P<connected>.+)"
+ r"|\s*LegacyPairing:\s*(?P<legacy_pairing>.+)"
+ r"|\s*Modalias:\s*(?P<modalias>.+)"
+ r"|\s*RSSI:\s*(?P<rssi>.+)"
+ r"|\s*TxPower:\s*(?P<txpower>.+)"
+ r"|\s*UUID:\s*(?P<uuid>.+))"
)
def _parse_device(next_lines: List[str], quiet: bool) -> Optional[Device]:
next_line = next_lines.pop()
result = re.match(_device_head_pattern, next_line)
if not result:
next_lines.append(next_line)
return None
matches = result.groupdict()
name = matches["name"]
if name.endswith("not available"):
return None
device: Device = {
"name": '',
"is_public": False,
"is_random": False,
"address": matches["address"],
"alias": '',
"appearance": '',
"class": '',
"icon": '',
"paired": '',
"bonded": '',
"trusted": '',
"blocked": '',
"connected": '',
"legacy_pairing": '',
"rssi": 0,
"txpower": 0,
"uuids": [],
"modalias": ''
}
if name.endswith("(public)"):
device["is_public"] = True
name = name.replace("(public)", "")
elif name.endswith("(random)"):
device["is_random"] = True
name = name.replace("(random)", "")
device["name"] = name.strip()
while next_lines:
next_line = next_lines.pop()
result = re.match(_device_line_pattern, next_line)
if not result:
next_lines.append(next_line)
return device
matches = result.groupdict()
if matches["name"]:
device["name"] = matches["name"]
elif matches["alias"]:
device["alias"] = matches["alias"]
elif matches["appearance"]:
device["appearance"] = matches["appearance"]
elif matches["class"]:
device["class"] = matches["class"]
elif matches["icon"]:
device["icon"] = matches["icon"]
elif matches["paired"]:
device["paired"] = matches["paired"]
elif matches["bonded"]:
device["bonded"] = matches["bonded"]
elif matches["trusted"]:
device["trusted"] = matches["trusted"]
elif matches["blocked"]:
device["blocked"] = matches["blocked"]
elif matches["connected"]:
device["connected"] = matches["connected"]
elif matches["legacy_pairing"]:
device["legacy_pairing"] = matches["legacy_pairing"]
elif matches["rssi"]:
rssi = matches["rssi"]
try:
device["rssi"] = int(rssi)
except ValueError:
if not quiet:
jc.utils.warning_message([f"{next_line} : rssi - {rssi} is not int-able"])
elif matches["txpower"]:
txpower = matches["txpower"]
try:
device["txpower"] = int(txpower)
except ValueError:
if not quiet:
jc.utils.warning_message([f"{next_line} : txpower - {txpower} is not int-able"])
elif matches["uuid"]:
if not "uuids" in device:
device["uuids"] = []
device["uuids"].append(matches["uuid"])
elif matches["modalias"]:
device["modalias"] = matches["modalias"]
return device
def parse(data: str, raw: bool = False, quiet: bool = False) -> List[JSONDictType]:
"""
Main text parsing function
Parameters:
data: (string) text data to parse
raw: (boolean) unprocessed output if True
quiet: (boolean) suppress warning messages if True
Returns:
List of Dictionaries. Raw or processed structured data.
"""
jc.utils.compatibility(__name__, info.compatible, quiet)
jc.utils.input_type_check(data)
result: List = []
if jc.utils.has_data(data):
linedata = data.splitlines()
linedata.reverse()
while linedata:
element = None
if data.startswith("Controller"):
element = _parse_controller(linedata)
elif data.startswith("Device"):
element = _parse_device(linedata, quiet) # type: ignore
if element:
result.append(element)
else:
break
return result

263
jc/parsers/certbot.py Normal file
View File

@@ -0,0 +1,263 @@
"""jc - JSON Convert `certbot` command output parser
Supports the following `certbot` commands:
- `certbot show_account`
- `certbot certificates`
Verbose options are not supported.
Usage (cli):
$ certbot show_account | jc --certbot
$ certbot certificates | jc --certbot
or
$ jc certbot show_account
$ jc certbot certificates
Usage (module):
import jc
result = jc.parse('certbot', certbot_command_output)
Schema:
{
"certificates": [
{
"name": string,
"serial_number": string,
"key_type": string,
"domains": [
string
],
"expiration_date": string,
"expiration_date_epoch": integer,
"expiration_date_epoch_utc": integer,
"expiration_date_iso": string,
"validity": string,
"certificate_path": string,
"private_key_path": string
}
],
"account": {
"server": string,
"url": string,
"email": string
}
}
Examples:
$ certbot certificates | jc --certbot -p
{
"certificates": [
{
"name": "example.com",
"serial_number": "3f7axxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"key_type": "RSA",
"domains": [
"example.com",
"www.example.com"
],
"expiration_date": "2023-05-11 01:33:10+00:00",
"validity": "63 days",
"certificate_path": "/etc/letsencrypt/live/example.com/chain.pem",
"private_key_path": "/etc/letsencrypt/live/example.com/priv.pem",
"expiration_date_epoch": 1683793990,
"expiration_date_epoch_utc": 1683768790,
"expiration_date_iso": "2023-05-11T01:33:10+00:00"
},
{
"name": "example.org",
"serial_number": "3bcyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy",
"key_type": "RSA",
"domains": [
"example.org",
"www.example.org"
],
"expiration_date": "2023-06-12 01:35:30+00:00",
"validity": "63 days",
"certificate_path": "/etc/letsencrypt/live/example.org/chain.pem",
"private_key_path": "/etc/letsencrypt/live/example.org/key.pem",
"expiration_date_epoch": 1686558930,
"expiration_date_epoch_utc": 1686533730,
"expiration_date_iso": "2023-06-12T01:35:30+00:00"
}
]
}
$ certbot certificates | jc --certbot -p -r
{
"certificates": [
{
"name": "example.com",
"serial_number": "3f7axxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"key_type": "RSA",
"domains": [
"example.com",
"www.example.com"
],
"expiration_date": "2023-05-11 01:33:10+00:00",
"validity": "63 days",
"certificate_path": "/etc/letsencrypt/live/example.com/chain.pem",
"private_key_path": "/etc/letsencrypt/live/example.com/priv.pem"
},
{
"name": "example.org",
"serial_number": "3bcyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy",
"key_type": "RSA",
"domains": [
"example.org",
"www.example.org"
],
"expiration_date": "2023-06-12 01:35:30+00:00",
"validity": "63 days",
"certificate_path": "/etc/letsencrypt/live/example.org/chain.pem",
"private_key_path": "/etc/letsencrypt/live/example.org/key.pem"
}
]
}
$ certbot show_account | jc --certbot -p
{
"account": {
"server": "https://acme-staging-v02.api.letsencrypt.org/directory",
"url": "https://acme-staging-v02.api.letsencrypt.org/acme/acct/123",
"email": "some@example.com"
}
}
"""
import re
from typing import List, Dict
from jc.jc_types import JSONDictType
import jc.utils
class info():
"""Provides parser metadata (version, author, etc.)"""
version = '1.2'
description = '`certbot` command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
compatible = ['linux', 'darwin', 'cygwin', 'win32', 'aix', 'freebsd']
tags = ['command']
magic_commands = ['certbot']
__version__ = info.version
def _process(proc_data: JSONDictType) -> JSONDictType:
"""
Final processing to conform to the schema.
Parameters:
proc_data: (List of Dictionaries) raw structured data to process
Returns:
Dictionary. Structured to conform to the schema.
"""
if 'certificates' in proc_data:
for cert in proc_data['certificates']:
if 'expiration_date' in cert:
dt = jc.utils.timestamp(cert['expiration_date'], format_hint=(1760,))
cert['expiration_date_epoch'] = dt.naive
cert['expiration_date_epoch_utc'] = dt.utc
cert['expiration_date_iso'] = dt.iso
return proc_data
def parse(
data: str,
raw: bool = False,
quiet: bool = False
) -> JSONDictType:
"""
Main text parsing function
Parameters:
data: (string) text data to parse
raw: (boolean) unprocessed output if True
quiet: (boolean) suppress warning messages if True
Returns:
Dictionary. Raw or processed structured data.
"""
jc.utils.compatibility(__name__, info.compatible, quiet)
jc.utils.input_type_check(data)
raw_output: Dict = {}
cert_list: List = []
cert_dict: Dict = {}
acct_dict: Dict = {}
cmd_option = ''
if jc.utils.has_data(data):
cert_pattern = re.compile(r'^Found the following certs:\r?$', re.MULTILINE)
if re.search(cert_pattern, data):
cmd_option = 'certificates'
else:
cmd_option = 'account'
for line in filter(None, data.splitlines()):
if cmd_option == 'certificates':
if line.startswith(' Certificate Name:'):
if cert_dict:
cert_list.append(cert_dict)
cert_dict = {}
cert_dict['name'] = line.split()[-1]
if line.startswith(' Serial Number:'):
cert_dict['serial_number'] = line.split()[-1]
if line.startswith(' Key Type:'):
cert_dict['key_type'] = line.split(': ', maxsplit=1)[1]
if line.startswith(' Domains:'):
splitline = line.split(': ', maxsplit=1)[1]
cert_dict['domains'] = splitline.split()
if line.startswith(' Expiry Date:'):
splitline = line.split(': ', maxsplit=1)[1]
cert_datetime = splitline.split('(')[0]
validity = splitline.split('(')[1]
cert_dict['expiration_date'] = cert_datetime.strip()
cert_dict['validity'] = validity[:-1].replace('VALID: ', '')
if line.startswith(' Certificate Path:'):
cert_dict['certificate_path'] = line.split(': ', maxsplit=1)[1]
if line.startswith(' Private Key Path:'):
cert_dict['private_key_path'] = line.split(': ', maxsplit=1)[1]
if cmd_option == 'account':
if line.startswith('Account details for server'):
acct_dict['server'] = line.split()[-1][:-1]
if line.startswith(' Account URL:'):
acct_dict['url'] = line.split()[-1]
if line.startswith(' Email contact:'):
acct_dict['email'] = line.split()[-1]
if acct_dict:
raw_output['account'] = acct_dict
if cert_dict:
cert_list.append(cert_dict)
if cert_list:
raw_output['certificates'] = cert_list
return raw_output if raw else _process(raw_output)

View File

@@ -174,7 +174,7 @@ import jc.parsers.universal
class info():
"""Provides parser metadata (version, author, etc.)"""
version = '1.6'
version = '1.8'
description = '`crontab` command and file parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
@@ -198,7 +198,7 @@ def _process(proc_data):
Dictionary. Structured data to conform to the schema.
"""
# put itmes in lists
# put items in lists
try:
for entry in proc_data['schedule']:
entry['minute'] = entry['minute'].split(',')
@@ -245,7 +245,11 @@ def parse(data, raw=False, quiet=False):
# Pop any variable assignment lines
cron_var = []
for i, line in reversed(list(enumerate(cleandata))):
if '=' in line and not line.strip()[0].isdigit() and not line.strip()[0] == '@':
if '=' in line \
and not line.strip()[0].isdigit() \
and not line.strip()[0] == '@' \
and not line.strip()[0] == '*':
var_line = cleandata.pop(i)
var_name = var_line.split('=', maxsplit=1)[0].strip()
var_value = var_line.split('=', maxsplit=1)[1].strip()
@@ -273,6 +277,9 @@ def parse(data, raw=False, quiet=False):
raw_output['schedule'] = cron_list
# Add shortcut entries back in
if 'schedule' not in raw_output:
raw_output['schedule'] = []
for item in shortcut_list:
raw_output['schedule'].append(item)

View File

@@ -171,7 +171,7 @@ import jc.parsers.universal
class info():
"""Provides parser metadata (version, author, etc.)"""
version = '1.7'
version = '1.9'
description = '`crontab` file parser with user support'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
@@ -194,7 +194,7 @@ def _process(proc_data):
Dictionary. Structured data to conform to the schema.
"""
# put itmes in lists
# put items in lists
try:
for entry in proc_data['schedule']:
entry['minute'] = entry['minute'].split(',')
@@ -241,7 +241,11 @@ def parse(data, raw=False, quiet=False):
# Pop any variable assignment lines
cron_var = []
for i, line in reversed(list(enumerate(cleandata))):
if '=' in line and not line.strip()[0].isdigit() and not line.strip()[0] == '@':
if '=' in line \
and not line.strip()[0].isdigit() \
and not line.strip()[0] == '@' \
and not line.strip()[0] == '*':
var_line = cleandata.pop(i)
var_name = var_line.split('=', maxsplit=1)[0].strip()
var_value = var_line.split('=', maxsplit=1)[1].strip()
@@ -271,6 +275,9 @@ def parse(data, raw=False, quiet=False):
raw_output['schedule'] = cron_list
# Add shortcut entries back in
if 'schedule' not in raw_output:
raw_output['schedule'] = []
for item in shortcut_list:
raw_output['schedule'].append(item)

View File

@@ -4,6 +4,7 @@ Options supported:
- `+noall +answer` options are supported in cases where only the answer
information is desired.
- `+axfr` option is supported on its own
- `+nsid` option is supported
The `when_epoch` calculated timestamp field is naive. (i.e. based on the
local time of the system the parser is run on)
@@ -322,7 +323,7 @@ import jc.utils
class info():
"""Provides parser metadata (version, author, etc.)"""
version = '2.4'
version = '2.5'
description = '`dig` command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
@@ -427,6 +428,7 @@ def _parse_flags_line(flagsline):
def _parse_opt_pseudosection(optline):
# ;; OPT PSEUDOSECTION:
# ; EDNS: version: 0, flags:; udp: 4096
# ; NSID: 67 70 64 6e 73 2d 73 66 6f ("gpdns-sfo")
# ; COOKIE: 1cbc06703eaef210
if optline.startswith('; EDNS:'):
optline_list = optline.replace(',', ' ').split(';')
@@ -443,11 +445,18 @@ def _parse_opt_pseudosection(optline):
}
}
elif optline.startswith('; COOKIE:'):
if optline.startswith('; COOKIE:'):
return {
'cookie': optline.split()[2]
}
if optline.startswith('; NSID:'):
return {
'nsid': optline.split('("')[-1].rstrip('")')
}
return {}
def _parse_question(question):
# ;www.cnn.com. IN A

View File

@@ -67,12 +67,13 @@ Examples:
"_": "/usr/bin/env"
}
"""
import re
import jc.utils
class info():
"""Provides parser metadata (version, author, etc.)"""
version = '1.4'
version = '1.5'
description = '`env` command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
@@ -83,6 +84,7 @@ class info():
__version__ = info.version
VAR_DEF_PATTERN = re.compile(r'^[a-zA-Z_][a-zA-Z0-9_]*=\S*.*$')
def _process(proc_data):
"""
@@ -96,8 +98,6 @@ def _process(proc_data):
List of Dictionaries. Structured data to conform to the schema.
"""
# rebuild output for added semantic information
processed = []
for k, v in proc_data.items():
proc_line = {}
@@ -120,24 +120,29 @@ def parse(data, raw=False, quiet=False):
Returns:
Dictionary of raw structured data or
List of Dictionaries of processed structured data
Dictionary of raw structured data or (default)
List of Dictionaries of processed structured data (raw)
"""
jc.utils.compatibility(__name__, info.compatible, quiet)
jc.utils.input_type_check(data)
raw_output = {}
# Clear any blank lines
cleandata = list(filter(None, data.splitlines()))
key = ''
value = None
if jc.utils.has_data(data):
for line in data.splitlines():
if VAR_DEF_PATTERN.match(line):
if not value is None:
raw_output[key] = value
key, value = line.split('=', maxsplit=1)
continue
for entry in cleandata:
parsed_line = entry.split('=', maxsplit=1)
raw_output[parsed_line[0]] = parsed_line[1]
if not value is None:
value = value + '\n' + line
if not value is None:
raw_output[key] = value
return raw_output if raw else _process(raw_output)
if raw:
return raw_output
else:
return _process(raw_output)

137
jc/parsers/find.py Normal file
View File

@@ -0,0 +1,137 @@
"""jc - JSON Convert `find` command output parser
This parser returns a list of objects by default and a list of strings if
the `--raw` option is used.
Usage (cli):
$ find | jc --find
Usage (module):
import jc
result = jc.parse('find', find_command_output)
Schema:
[
{
"path": string,
"node": string,
"error": string
}
]
Examples:
$ find | jc --find -p
[
{
"path": "./directory"
"node": "filename"
},
{
"path": "./anotherdirectory"
"node": "anotherfile"
},
{
"path": null
"node": null
"error": "find: './inaccessible': Permission denied"
}
...
]
$ find | jc --find -p -r
[
"./templates/readme_template",
"./templates/manpage_template",
"./.github/workflows/pythonapp.yml",
...
]
"""
import jc.utils
class info():
"""Provides parser metadata (version, author, etc.)"""
version = '1.0'
description = '`find` command parser'
author = 'Solomon Leang'
author_email = 'solomonleang@gmail.com'
compatible = ['linux']
tags = ['command']
__version__ = info.version
def _process(proc_data):
"""
Final processing to conform to the schema.
Parameters:
proc_data: (List of Strings) raw structured data to process
Returns:
List of Dictionaries. Structured data to conform to the schema.
"""
processed = []
for index in proc_data:
path, node, error = "", "", ""
if index == ".":
node = "."
elif index.startswith('find: '):
error = index
else:
try:
path, node = index.rsplit('/', maxsplit=1)
except ValueError:
pass
proc_line = {
'path': path if path else None,
'node': node if node else None
}
if error:
proc_line.update(
{'error': error}
)
processed.append(proc_line)
return processed
def parse(data, raw=False, quiet=False):
"""
Main text parsing function
Parameters:
data: (string) text data to parse
raw: (boolean) unprocessed output if True
quiet: (boolean) suppress warning messages if True
Returns:
List of raw strings or
List of Dictionaries of processed structured data
"""
jc.utils.compatibility(__name__, info.compatible, quiet)
jc.utils.input_type_check(data)
raw_output = []
if jc.utils.has_data(data):
raw_output = data.splitlines()
if raw:
return raw_output
else:
return _process(raw_output)

View File

@@ -149,11 +149,11 @@ from typing import List, Dict
import jc.utils
hash_pattern = re.compile(r'(?:[0-9]|[a-f]){40}')
changes_pattern = re.compile(r'\s(?P<files>\d+)\s+(files? changed),\s+(?P<insertions>\d+)\s(insertions?\(\+\))?(,\s+)?(?P<deletions>\d+)?(\s+deletions?\(\-\))?')
changes_pattern = re.compile(r'\s(?P<files>\d+)\s+(files? changed)(?:,\s+(?P<insertions>\d+)\s+(insertions?\(\+\)))?(?:,\s+(?P<deletions>\d+)\s+(deletions?\(\-\)))?')
class info():
"""Provides parser metadata (version, author, etc.)"""
version = '1.3'
version = '1.4'
description = '`git log` command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'

View File

@@ -83,12 +83,12 @@ from jc.exceptions import ParseError
hash_pattern = re.compile(r'(?:[0-9]|[a-f]){40}')
changes_pattern = re.compile(r'\s(?P<files>\d+)\s+(files? changed),\s+(?P<insertions>\d+)\s(insertions?\(\+\))?(,\s+)?(?P<deletions>\d+)?(\s+deletions?\(\-\))?')
changes_pattern = re.compile(r'\s(?P<files>\d+)\s+(files? changed)(?:,\s+(?P<insertions>\d+)\s+(insertions?\(\+\)))?(?:,\s+(?P<deletions>\d+)\s+(deletions?\(\-\)))?')
class info():
"""Provides parser metadata (version, author, etc.)"""
version = '1.3'
version = '1.4'
description = '`git log` command streaming parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'

243
jc/parsers/host.py Normal file
View File

@@ -0,0 +1,243 @@
"""jc - JSON Convert `host` command output parser
Supports parsing of the most commonly used RR types (A, AAAA, MX, TXT)
Usage (cli):
$ host google.com | jc --host
or
$ jc host google.com
Usage (module):
import jc
result = jc.parse('host', host_command_output)
Schema:
[
{
"hostname": string,
"address": [
string
],
"v6-address": [
string
],
"mail": [
string
]
}
]
[
{
"nameserver": string,
"zone": string,
"mname": string,
"rname": string,
"serial": integer,
"refresh": integer,
"retry": integer,
"expire": integer,
"minimum": integer
}
]
Examples:
$ host google.com | jc --host
[
{
"hostname": "google.com",
"address": [
"142.251.39.110"
],
"v6-address": [
"2a00:1450:400e:811::200e"
],
"mail": [
"smtp.google.com."
]
}
]
$ jc host -C sunet.se
[
{
"nameserver": "2001:6b0:7::2",
"zone": "sunet.se",
"mname": "sunic.sunet.se.",
"rname": "hostmaster.sunet.se.",
"serial": "2023090401",
"refresh": "28800",
"retry": "7200",
"expire": "604800",
"minimum": "300"
},
{
...
}
]
"""
from typing import Dict, List
import jc.utils
class info():
"""Provides parser metadata (version, author, etc.)"""
version = '1.0'
description = '`host` command parser'
author = 'Pettai'
author_email = 'pettai@sunet.se'
compatible = ['linux', 'darwin', 'cygwin', 'win32', 'aix', 'freebsd']
tags = ['command']
magic_commands = ['host']
__version__ = info.version
def _process(proc_data):
"""
Final processing to conform to the schema.
Parameters:
proc_data: (List of Dictionaries) raw structured data to process
Returns:
List of Dictionaries. Structured to conform to the schema.
"""
int_list = {'serial', 'refresh', 'retry', 'expire', 'minimum'}
for entry in proc_data:
for key in entry:
if key in int_list:
entry[key] = jc.utils.convert_to_int(entry[key])
return proc_data
def parse(data: str, raw: bool = False, quiet: bool = False):
"""
Main text parsing function
Parameters:
data: (string) text data to parse
raw: (boolean) unprocessed output if True
quiet: (boolean) suppress warning messages if True
Returns:
List of Dictionaries. Raw or processed structured data.
"""
jc.utils.compatibility(__name__, info.compatible, quiet)
jc.utils.input_type_check(data)
raw_output: List[Dict] = []
warned = False
if jc.utils.has_data(data):
addresses = []
v6addresses = []
mail = []
text = []
rrdata = {}
soaparse = False
for line in filter(None, data.splitlines()):
line = line.strip()
# default
if ' has address ' in line:
linedata = line.split(' ', maxsplit=3)
hostname = linedata[0]
address = linedata[3]
addresses.append(address)
rrdata.update({'hostname': hostname})
rrdata.update({'address': addresses})
continue
if ' has IPv6 address ' in line:
linedata = line.split(' ', maxsplit=4)
hostname = linedata[0]
v6address = linedata[4]
v6addresses.append(v6address)
rrdata.update({'hostname': hostname})
rrdata.update({'v6-address': v6addresses})
continue
if ' mail is handled by ' in line:
linedata = line.split(' ', maxsplit=6)
hostname = linedata[0]
mx = linedata[6]
mail.append(mx)
rrdata.update({'hostname': hostname})
rrdata.update({'mail': mail})
continue
# TXT parsing
if ' descriptive text ' in line:
linedata = line.split('descriptive text "', maxsplit=1)
hostname = linedata[0]
txt = linedata[1].strip('"')
text.append(txt)
rrdata.update({'hostname': hostname})
rrdata.update({'text': text})
continue
# -C / SOA parsing
if line.startswith('Nameserver '):
soaparse = True
rrdata = {}
linedata = line.split(' ', maxsplit=1)
nameserverip = linedata[1].rstrip(':')
rrdata.update({'nameserver': nameserverip})
continue
if ' has SOA record ' in line:
linedata = line.split(' ', maxsplit=10)
zone = linedata[0]
mname = linedata[4]
rname = linedata[5]
serial = linedata[6]
refresh = linedata[7]
retry = linedata[8]
expire = linedata[9]
minimum = linedata[10]
try:
rrdata.update(
{
'zone': zone,
'mname': mname,
'rname': rname,
'serial': serial,
'refresh': refresh,
'retry': retry,
'expire': expire,
'minimum': minimum
},
)
raw_output.append(rrdata)
except IndexError:
if not warned:
jc.utils.warning_message(['Unknown format detected.'])
warned = True
if not soaparse:
raw_output.append(rrdata)
return raw_output if raw else _process(raw_output)

View File

@@ -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'''

View File

@@ -17,12 +17,12 @@ contained in lists/arrays.
Usage (cli):
$ cat foo.ini | jc --ini
$ cat foo.ini | jc --ini-dup
Usage (module):
import jc
result = jc.parse('ini', ini_file_output)
result = jc.parse('ini_dup', ini_file_output)
Schema:
@@ -62,7 +62,7 @@ Examples:
fruit = peach
color = green
$ cat example.ini | jc --ini -p
$ cat example.ini | jc --ini-dup -p
{
"foo": [
"fiz"
@@ -97,7 +97,7 @@ import uuid
class info():
"""Provides parser metadata (version, author, etc.)"""
version = '1.0'
version = '1.1'
description = 'INI with duplicate key file parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'

145
jc/parsers/ip_route.py Normal file
View File

@@ -0,0 +1,145 @@
"""jc - JSON Convert `ip route` command output parser
Usage (cli):
$ ip route | jc --ip-route
or
$ jc ip-route
Usage (module):
import jc
result = jc.parse('ip_route', ip_route_command_output)
Schema:
[
{
"ip": string,
"via": string,
"dev": string,
"metric": integer,
"proto": string,
"scope": string,
"src": string,
"via": string,
"status": string
}
]
Examples:
$ ip route | jc --ip-route -p
[
{
"ip": "10.0.2.0/24",
"dev": "enp0s3",
"proto": "kernel",
"scope": "link",
"src": "10.0.2.15",
"metric": 100
}
]
"""
from typing import Dict
import jc.utils
class info:
"""Provides parser metadata (version, author, etc.)"""
version = '1.0'
description = '`ip route` command parser'
author = 'Julian Jackson'
author_email = 'jackson.julian55@yahoo.com'
compatible = ['linux']
magic_commands = ['ip route']
tags = ['command']
__version__ = info.version
def parse(data, raw=False, quiet=False):
"""
Main text parsing function
Parameters:
data: (string) text data to parse
raw: (boolean) unprocessed output if True
quiet: (boolean) suppress warning messages if True
Returns:
List of Json objects if data is processed and Raw data if raw = true.
"""
structure = {}
items = []
lines = data.splitlines()
index = 0
place = 0
inc = 0
for line in lines:
temp = line.split()
for word in temp:
if word == 'via':
y = {'via': temp[place + 1]}
place += 1
structure.update(y)
elif word == 'dev':
y = {'dev': temp[place + 1]}
place += 1
structure.update(y)
elif word == 'metric':
if raw:
y = {'metric': temp[place + 1]}
else:
y = {'metric': jc.utils.convert_to_int(temp[place+1])}
place += 1
structure.update(y)
elif word == 'proto':
y = {'proto': temp[place + 1]}
place += 1
structure.update(y)
elif word == 'scope':
y = {'scope': temp[place + 1]}
place += 1
structure.update(y)
elif word == 'src':
y = {'src': temp[place + 1]}
place += 1
structure.update(y)
elif word == 'status':
y = {'status': temp[place + 1]}
place += 1
structure.update(y)
elif word == 'default':
y = {'ip': 'default'}
place += 1
structure.update(y)
elif word == 'linkdown':
y = {'status': 'linkdown'}
place += 1
structure.update(y)
else:
y = {'ip': temp[0]}
place += 1
structure.update(y)
if y.get("ip") != "":
items.append(structure)
structure = {}
place = 0
index += 1
inc += 1
jc.utils.compatibility(__name__, info.compatible, quiet)
jc.utils.input_type_check(data)
if not jc.utils.has_data(data):
return []
return items

View File

@@ -85,7 +85,7 @@ import jc.utils
class info():
"""Provides parser metadata (version, author, etc.)"""
version = '1.0'
version = '1.1'
description = '`iwconfig` command parser'
author = 'Thomas Vincent'
author_email = 'vrince@gmail.com'
@@ -146,7 +146,7 @@ def parse(
raw_output: List[Dict] = []
re_interface = re.compile(r'^(?P<name>[a-zA-Z0-9:._-]+)\s+(?P<protocol>([a-zA-Z0-9]+\s)*[a-zA-Z0-9.]+)\s+ESSID:\"(?P<essid>[a-zA-Z0-9:._\s]+)\"')
re_interface = re.compile(r'^(?P<name>[a-zA-Z0-9:._\-]+)\s+(?P<protocol>([a-zA-Z0-9]+\s)*[a-zA-Z0-9.]+)\s+ESSID:\"(?P<essid>[a-zA-Z0-9:._\s\-]+)\"')
re_mode = re.compile(r'Mode:(?P<mode>\w+)')
re_frequency = re.compile(r'Frequency:(?P<frequency>[0-9.]+)\s(?P<frequency_unit>\w+)')
re_access_point = re.compile(r'Access Point:\s*(?P<access_point>[0-9A-F:]+)')

View File

@@ -1,6 +1,6 @@
"""jc - JSON Convert `last` and `lastb` command output parser
Supports `-w` and `-F` options.
Supports `-w`, `-F`, and `-x` options.
Calculated epoch time fields are naive (i.e. based on the local time of the
system the parser is run on) since there is no timezone information in the
@@ -103,10 +103,15 @@ Examples:
import re
import jc.utils
DATE_RE = re.compile(r'[MTWFS][ouerha][nedritnu] [JFMASOND][aepuco][nbrynlgptvc]')
LAST_F_DATE_RE = re.compile(r'\d\d:\d\d:\d\d \d\d\d\d')
LOGIN_LOGOUT_EPOCH_RE = re.compile(r'.*\d\d:\d\d:\d\d \d\d\d\d.*')
LOGOUT_IGNORED_EVENTS = ['down', 'crash']
class info():
"""Provides parser metadata (version, author, etc.)"""
version = '1.8'
version = '1.9'
description = '`last` and `lastb` command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
@@ -138,9 +143,6 @@ def _process(proc_data):
if 'tty' in entry and entry['tty'] == '~':
entry['tty'] = None
if 'tty' in entry and entry['tty'] == 'system_boot':
entry['tty'] = 'system boot'
if 'hostname' in entry and entry['hostname'] == '-':
entry['hostname'] = None
@@ -153,11 +155,11 @@ def _process(proc_data):
if 'logout' in entry and entry['logout'] == 'gone_-_no_logout':
entry['logout'] = 'gone - no logout'
if 'login' in entry and re.match(r'.*\d\d:\d\d:\d\d \d\d\d\d.*', entry['login']):
if 'login' in entry and LOGIN_LOGOUT_EPOCH_RE.match(entry['login']):
timestamp = jc.utils.timestamp(entry['login'])
entry['login_epoch'] = timestamp.naive
if 'logout' in entry and re.match(r'.*\d\d:\d\d:\d\d \d\d\d\d.*', entry['logout']):
if 'logout' in entry and LOGIN_LOGOUT_EPOCH_RE.match(entry['logout']):
timestamp = jc.utils.timestamp(entry['logout'])
entry['logout_epoch'] = timestamp.naive
@@ -194,66 +196,71 @@ def parse(data, raw=False, quiet=False):
# Clear any blank lines
cleandata = list(filter(None, data.splitlines()))
if jc.utils.has_data(data):
if not jc.utils.has_data(data):
return []
for entry in cleandata:
output_line = {}
for entry in cleandata:
output_line = {}
if (entry.startswith('wtmp begins ') or
entry.startswith('btmp begins ') or
entry.startswith('utx.log begins ')):
if any(
entry.startswith(f'{prefix} begins ')
for prefix in ['wtmp', 'btmp', 'utx.log']
):
continue
continue
entry = entry.replace('boot time', 'boot_time')
entry = entry.replace(' still logged in', '- still_logged_in')
entry = entry.replace(' gone - no logout', '- gone_-_no_logout')
entry = entry.replace('system boot', 'system_boot')
entry = entry.replace('boot time', 'boot_time')
entry = entry.replace(' still logged in', '- still_logged_in')
entry = entry.replace(' gone - no logout', '- gone_-_no_logout')
linedata = entry.split()
linedata = entry.split()
if re.match(r'[MTWFS][ouerha][nedritnu] [JFMASOND][aepuco][nbrynlgptvc]', ' '.join(linedata[2:4])):
linedata.insert(2, '-')
# Adding "-" before the date part.
if DATE_RE.match(' '.join(linedata[2:4])):
linedata.insert(2, '-')
# freebsd fix
if linedata[0] == 'boot_time':
linedata.insert(1, '-')
linedata.insert(1, '~')
# freebsd fix
if linedata[0] == 'boot_time':
linedata.insert(1, '-')
linedata.insert(1, '~')
output_line['user'] = linedata[0]
output_line['tty'] = linedata[1]
output_line['hostname'] = linedata[2]
output_line['user'] = linedata[0]
# last -F support
if re.match(r'\d\d:\d\d:\d\d \d\d\d\d', ' '.join(linedata[6:8])):
output_line['login'] = ' '.join(linedata[3:8])
# Fix for last -x (runlevel).
if output_line['user'] == 'runlevel' and linedata[1] == '(to':
linedata[1] += f' {linedata.pop(2)} {linedata.pop(2)}'
elif output_line['user'] in ['reboot', 'shutdown'] and linedata[1] == 'system': # system down\system boot
linedata[1] += f' {linedata.pop(2)}'
if len(linedata) > 9 and linedata[9] != 'crash' and linedata[9] != 'down':
output_line['tty'] = linedata[1]
output_line['hostname'] = linedata[2]
# last -F support
if LAST_F_DATE_RE.match(' '.join(linedata[6:8])):
output_line['login'] = ' '.join(linedata[3:8])
if len(linedata) > 9:
if linedata[9] not in LOGOUT_IGNORED_EVENTS:
output_line['logout'] = ' '.join(linedata[9:14])
if len(linedata) > 9 and (linedata[9] == 'crash' or linedata[9] == 'down'):
else:
output_line['logout'] = linedata[9]
# add more items to the list to line up duration
linedata.insert(10, '-')
linedata.insert(10, '-')
linedata.insert(10, '-')
linedata.insert(10, '-')
for _ in range(4):
linedata.insert(10, '-')
if len(linedata) > 14:
output_line['duration'] = linedata[14].replace('(', '').replace(')', '')
if len(linedata) > 14:
output_line['duration'] = linedata[14].replace('(', '').replace(')', '')
else: # normal last support
output_line['login'] = ' '.join(linedata[3:7])
# normal last support
else:
output_line['login'] = ' '.join(linedata[3:7])
if len(linedata) > 8:
output_line['logout'] = linedata[8]
if len(linedata) > 8:
output_line['logout'] = linedata[8]
if len(linedata) > 9:
output_line['duration'] = linedata[9].replace('(', '').replace(')', '')
if len(linedata) > 9:
output_line['duration'] = linedata[9].replace('(', '').replace(')', '')
raw_output.append(output_line)
raw_output.append(output_line)
if raw:
return raw_output
else:
return _process(raw_output)
return _process(raw_output)

162
jc/parsers/lsattr.py Normal file
View File

@@ -0,0 +1,162 @@
"""jc - JSON Convert `lsattr` command output parser
Usage (cli):
$ lsattr | jc --lsattr
or
$ jc lsattr
Usage (module):
import jc
result = jc.parse('lsattr', lsattr_command_output)
Schema:
Information from https://github.com/mirror/busybox/blob/2d4a3d9e6c1493a9520b907e07a41aca90cdfd94/e2fsprogs/e2fs_lib.c#L40
used to define field names
[
{
"file": string,
"compressed_file": Optional[boolean],
"compressed_dirty_file": Optional[boolean],
"compression_raw_access": Optional[boolean],
"secure_deletion": Optional[boolean],
"undelete": Optional[boolean],
"synchronous_updates": Optional[boolean],
"synchronous_directory_updates": Optional[boolean],
"immutable": Optional[boolean],
"append_only": Optional[boolean],
"no_dump": Optional[boolean],
"no_atime": Optional[boolean],
"compression_requested": Optional[boolean],
"encrypted": Optional[boolean],
"journaled_data": Optional[boolean],
"indexed_directory": Optional[boolean],
"no_tailmerging": Optional[boolean],
"top_of_directory_hierarchies": Optional[boolean],
"extents": Optional[boolean],
"no_cow": Optional[boolean],
"casefold": Optional[boolean],
"inline_data": Optional[boolean],
"project_hierarchy": Optional[boolean],
"verity": Optional[boolean],
}
]
Examples:
$ sudo lsattr /etc/passwd | jc --lsattr
[
{
"file": "/etc/passwd",
"extents": true
}
]
"""
from typing import List, Dict
from jc.jc_types import JSONDictType
import jc.utils
class info():
"""Provides parser metadata (version, author, etc.)"""
version = '1.0'
description = '`lsattr` command parser'
author = 'Mark Rotner'
author_email = 'rotner.mr@gmail.com'
compatible = ['linux']
magic_commands = ['lsattr']
tags = ['command']
__version__ = info.version
ERROR_PREFIX = "lsattr:"
# https://github.com/mirror/busybox/blob/2d4a3d9e6c1493a9520b907e07a41aca90cdfd94/e2fsprogs/e2fs_lib.c#L40
# https://github.com/landley/toybox/blob/f1682dc79fd75f64042b5438918fe5a507977e1c/toys/other/lsattr.c#L97
ATTRIBUTES = {
"B": "compressed_file",
"Z": "compressed_dirty_file",
"X": "compression_raw_access",
"s": "secure_deletion",
"u": "undelete",
"S": "synchronous_updates",
"D": "synchronous_directory_updates",
"i": "immutable",
"a": "append_only",
"d": "no_dump",
"A": "no_atime",
"c": "compression_requested",
"E": "encrypted",
"j": "journaled_data",
"I": "indexed_directory",
"t": "no_tailmerging",
"T": "top_of_directory_hierarchies",
"e": "extents",
"C": "no_cow",
"F": "casefold",
"N": "inline_data",
"P": "project_hierarchy",
"V": "verity",
}
def parse(
data: str,
raw: bool = False,
quiet: bool = False
) -> List[JSONDictType]:
"""
Main text parsing function
Parameters:
data: (string) text data to parse
quiet: (boolean) suppress warning messages if True
Returns:
List of Dictionaries. Raw or processed structured data.
"""
jc.utils.compatibility(__name__, info.compatible, quiet)
jc.utils.input_type_check(data)
output: List = []
cleandata = list(filter(None, data.splitlines()))
if not jc.utils.has_data(data):
return output
for line in cleandata:
# -R flag returns the output in the format:
# Folder:
# attributes file_in_folder
if line.endswith(':'):
continue
# lsattr: Operation not supported ....
if line.startswith(ERROR_PREFIX):
continue
line_output: Dict = {}
# attributes file
# --------------e----- /etc/passwd
attributes, file = line.split()
line_output['file'] = file
for attribute in list(attributes):
attribute_key = ATTRIBUTES.get(attribute)
if attribute_key:
line_output[attribute_key] = True
if line_output:
output.append(line_output)
return output

89
jc/parsers/lsb_release.py Normal file
View File

@@ -0,0 +1,89 @@
"""jc - JSON Convert `lsb_release` command parser
This parser is an alias to the Key/Value parser (`--kv`).
Usage (cli):
$ lsb_release -a | jc --lsb-release
or
$ jc lsb_release -a
Usage (module):
import jc
result = jc.parse('lsb_release', lsb_release_command_output)
Schema:
{
"<key>": string
}
Examples:
$ lsb_release -a | jc --lsb-release -p
{
"Distributor ID": "Ubuntu",
"Description": "Ubuntu 16.04.6 LTS",
"Release": "16.04",
"Codename": "xenial"
}
"""
from jc.jc_types import JSONDictType
import jc.parsers.kv
import jc.utils
class info():
"""Provides parser metadata (version, author, etc.)"""
version = '1.0'
description = '`lsb_release` command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
details = 'Using the Key/Value parser'
compatible = ['linux', 'darwin', 'cygwin', 'win32', 'aix', 'freebsd']
magic_commands = ['lsb_release']
tags = ['command']
__version__ = info.version
def _process(proc_data: JSONDictType) -> JSONDictType:
"""
Final processing to conform to the schema.
Parameters:
proc_data: (Dictionary) raw structured data to process
Returns:
Dictionary. Structured to conform to the schema.
"""
return jc.parsers.kv._process(proc_data)
def parse(
data: str,
raw: bool = False,
quiet: bool = False
) -> JSONDictType:
"""
Main text parsing function
Parameters:
data: (string) text data to parse
raw: (boolean) unprocessed output if True
quiet: (boolean) suppress warning messages if True
Returns:
Dictionary. Raw or processed structured data.
"""
jc.utils.compatibility(__name__, info.compatible, quiet)
raw_output = jc.parsers.kv.parse(data, raw, quiet)
return raw_output if raw else _process(raw_output)

View File

@@ -97,6 +97,24 @@ Schema:
]
}
},
"cdc_mbim": {
"<item>": {
"value": string,
"description": string,
"attributes": [
string
]
}
},
"cdc_mbim_extended": {
"<item>": {
"value": string,
"description": string,
"attributes": [
string
]
}
},
"videocontrol_descriptors": [
{
"<item>": {
@@ -291,7 +309,7 @@ from jc.exceptions import ParseError
class info():
"""Provides parser metadata (version, author, etc.)"""
version = '1.3'
version = '1.4'
description = '`lsusb` command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
@@ -500,6 +518,8 @@ class _LsUsb():
self.cdc_call_management = _descriptor_obj('cdc_call_management')
self.cdc_acm = _descriptor_obj('cdc_acm')
self.cdc_union = _descriptor_obj('cdc_union')
self.cdc_mbim = _descriptor_obj('cdc_mbim')
self.cdc_mbim_extended = _descriptor_obj('cdc_mbim_extended')
self.endpoint_descriptors = _descriptor_list('endpoint_descriptor')
self.videocontrol_interface_descriptors = _descriptor_list('videocontrol_interface_descriptor')
self.videostreaming_interface_descriptors = _descriptor_list('videostreaming_interface_descriptor')
@@ -538,7 +558,8 @@ class _LsUsb():
section_header = self.normal_section_header
if self.section == 'videocontrol_interface_descriptor' \
or self.section == 'videostreaming_interface_descriptor':
or self.section == 'videostreaming_interface_descriptor' \
or self.section == 'cdc_mbim_extended':
section_header = self.larger_section_header
@@ -689,6 +710,8 @@ class _LsUsb():
' CDC Union:': 'cdc_union',
' HID Device Descriptor:': 'hid_device_descriptor',
' Report Descriptors:': 'report_descriptors',
' CDC MBIM:': 'cdc_mbim',
' CDC MBIM Extended:': 'cdc_mbim_extended',
'Hub Descriptor:': 'hub_descriptor',
' Hub Port Status:': 'hub_port_status',
'Device Qualifier (for other device speed):': 'device_qualifier',
@@ -713,6 +736,8 @@ class _LsUsb():
'cdc_call_management': self.cdc_call_management.list,
'cdc_acm': self.cdc_acm.list,
'cdc_union': self.cdc_union.list,
'cdc_mbim': self.cdc_mbim.list,
'cdc_mbim_extended': self.cdc_mbim_extended.list,
'hid_device_descriptor': self.hid_device_descriptor.list,
# 'report_descriptors': self.report_descriptors_list, # not implemented
'videocontrol_interface_descriptor': self.videocontrol_interface_descriptors.list,
@@ -757,6 +782,8 @@ class _LsUsb():
['device_descriptor']['configuration_descriptor']['interface_descriptors'][0]['cdc_call_management'] = {}
['device_descriptor']['configuration_descriptor']['interface_descriptors'][0]['cdc_acm'] = {}
['device_descriptor']['configuration_descriptor']['interface_descriptors'][0]['cdc_union'] = {}
['device_descriptor']['configuration_descriptor']['interface_descriptors'][0]['cdc_mbim'] = {}
['device_descriptor']['configuration_descriptor']['interface_descriptors'][0]['cdc_mbim_extended'] = {}
['device_descriptor']['configuration_descriptor']['interface_descriptors'][0]['hid_device_descriptor'] = {}
['device_descriptor']['configuration_descriptor']['interface_descriptors'][0]['endpoint_descriptors'] = []
['device_descriptor']['configuration_descriptor']['interface_descriptors'][0]['endpoint_descriptors'][0] = {}
@@ -847,6 +874,12 @@ class _LsUsb():
if self.cdc_union._entries_for_this_bus_and_interface_idx_exist(idx, iface_idx):
self.cdc_union._update_output(idx, iface_idx, i_desc_obj)
if self.cdc_mbim._entries_for_this_bus_and_interface_idx_exist(idx, iface_idx):
self.cdc_mbim._update_output(idx, iface_idx, i_desc_obj)
if self.cdc_mbim_extended._entries_for_this_bus_and_interface_idx_exist(idx, iface_idx):
self.cdc_mbim_extended._update_output(idx, iface_idx, i_desc_obj)
if self.hid_device_descriptor._entries_for_this_bus_and_interface_idx_exist(idx, iface_idx):
self.hid_device_descriptor._update_output(idx, iface_idx, i_desc_obj)
@@ -923,6 +956,10 @@ def parse(data, raw=False, quiet=False):
lsusb = _LsUsb()
if jc.utils.has_data(data):
# fix known too-long field names
data = data.replace('bmNetworkCapabilities', 'bmNetworkCapabilit ')
for line in data.splitlines():
# only -v option or no options are supported
if line.startswith('/'):

View File

@@ -160,7 +160,7 @@ def _aix_parse(data):
# AIX mount entries have the remote node as the zeroth element. If the
# mount is local, the zeroth element is the filesystem instead. We can
# detect this by the lenth of the list. For local mounts, length is 7,
# detect this by the length of the list. For local mounts, length is 7,
# and for remote mounts, the length is 8. In the remote case, pop off
# the zeroth element. Then parsed_line has a consistent format.
if len(parsed_line) == 8:

View File

@@ -355,17 +355,18 @@ import jc.utils
class info():
"""Provides parser metadata (version, author, etc.)"""
version = '1.13'
version = '1.15'
description = '`netstat` command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
compatible = ['linux', 'darwin', 'freebsd']
compatible = ['linux', 'darwin', 'freebsd', 'win32']
magic_commands = ['netstat']
tags = ['command']
__version__ = info.version
WINDOWS_NETSTAT_HEADER = "Active Connections"
def _process(proc_data):
"""
@@ -450,9 +451,10 @@ def parse(data, raw=False, quiet=False):
import jc.parsers.netstat_freebsd_osx
raw_output = jc.parsers.netstat_freebsd_osx.parse(cleandata)
# use linux parser
else:
elif cleandata[0] == WINDOWS_NETSTAT_HEADER: # use windows parser.
import jc.parsers.netstat_windows
raw_output = jc.parsers.netstat_windows.parse(cleandata)
else: # use linux parser.
import jc.parsers.netstat_linux
raw_output = jc.parsers.netstat_linux.parse(cleandata)

View File

@@ -31,10 +31,19 @@ def normalize_interface_headers(header):
def parse_network(headers, entry):
LIST_OF_STATES = [
"ESTABLISHED", "SYN_SENT", "SYN_RECV", "FIN_WAIT1", "FIN_WAIT2",
"TIME_WAIT", "CLOSED", "CLOSE_WAIT", "LAST_ACK", "LISTEN", "CLOSING",
"UNKNOWN", "7"
]
# split entry based on presence of value in "State" column
contains_state = any(state in entry for state in LIST_OF_STATES)
split_modifier = 1 if contains_state else 2
entry = entry.split(maxsplit=len(headers) - split_modifier)
# Count words in header
# if len of line is one less than len of header, then insert None in field 5
entry = entry.split(maxsplit=len(headers) - 1)
if len(entry) == len(headers) - 1:
entry.insert(5, None)

View File

@@ -0,0 +1,75 @@
"""
jc - JSON Convert Windows `netstat` command output parser
"""
from typing import Dict, List
POSSIBLE_PROTOCOLS = ("TCP", "UDP", "TCPv6", "UDPv6")
def normalize_headers(headers: str):
"""
Normalizes the headers to match the jc netstat parser style
(local_address -> local_address, local_port...).
"""
headers = headers.lower().strip()
headers = headers.replace("local address", "local_address")
headers = headers.replace("foreign address", "foreign_address")
return headers.split()
def parse(cleandata: List[str]):
"""
Main text parsing function for Windows netstat
Parameters:
cleandata: (string) text data to parse
Returns:
List of Dictionaries. Raw structured data.
"""
raw_output = []
cleandata.pop(0) # Removing the "Active Connections" header.
headers = normalize_headers(cleandata.pop(0))
for line in cleandata:
line = line.strip()
if not line.startswith(POSSIBLE_PROTOCOLS): # -b.
line_data = raw_output.pop(len(raw_output) - 1)
line_data['program_name'] = line
raw_output.append(line_data)
continue
line_data = line.split()
line_data: Dict[str, str] = dict(zip(headers, line_data))
for key in list(line_data.keys()):
if key == "local_address":
local_address, local_port = line_data[key].rsplit(
":", maxsplit=1)
line_data["local_address"] = local_address
line_data["local_port"] = local_port
continue
if key == "foreign_address":
foreign_address, foreign_port = line_data[key].rsplit(
":", maxsplit=1)
line_data["foreign_address"] = foreign_address
line_data["foreign_port"] = foreign_port
continue
# There is no state in UDP, so the data after the "state" header will leak.
if key == "proto" and "state" in headers and line_data["proto"] == "UDP":
next_header = headers.index("state") + 1
if len(headers) > next_header:
next_header = headers[next_header]
line_data[next_header] = line_data["state"]
line_data["state"] = ''
raw_output.append(line_data)
return raw_output

236
jc/parsers/nsd_control.py Normal file
View File

@@ -0,0 +1,236 @@
"""jc - JSON Convert `nsd-control` command output parser
Usage (cli):
$ nsd-control | jc --nsd-control
or
$ jc nsd-control
Usage (module):
import jc
result = jc.parse('nsd_control', nsd_control_command_output)
Schema:
[
{
"version": string,
"verbosity": integer,
"ratelimit": integer
}
]
[
{
"zone": string
"status": {
"state": string,
"served-serial": string,
"commit-serial": string,
"wait": string
}
}
]
Examples:
$ nsd-control | jc --nsd-control status
[
{
"version": "4.6.2",
"verbosity": "2",
"ratelimit": "0"
}
]
$ nsd-control | jc --nsd-control zonestatus sunet.se
[
{
"zone": "sunet.se",
"status": {
"state": "ok",
"served-serial": "2023090704 since 2023-09-07T16:34:27",
"commit-serial": "2023090704 since 2023-09-07T16:34:27",
"wait": "28684 sec between attempts"
}
}
]
"""
from typing import List, Dict
import jc.utils
class info():
"""Provides parser metadata (version, author, etc.)"""
version = '1.0'
description = '`nsd-control` command parser'
author = 'Pettai'
author_email = 'pettai@sunet.se'
compatible = ['linux', 'darwin', 'cygwin', 'win32', 'aix', 'freebsd']
tags = ['command']
magic_commands = ['nsd-control']
__version__ = info.version
def _process(proc_data):
"""
Final processing to conform to the schema.
Parameters:
proc_data: (List of Dictionaries) raw structured data to process
Returns:
List of Dictionaries. Structured to conform to the schema.
"""
int_list = {'verbosity', 'ratelimit', 'wait'}
for entry in proc_data:
for key in entry:
if key in int_list:
entry[key] = jc.utils.convert_to_int(entry[key])
return proc_data
def parse(data: str, raw: bool = False, quiet: bool = False):
"""
Main text parsing function
Parameters:
data: (string) text data to parse
raw: (boolean) unprocessed output if True
quiet: (boolean) suppress warning messages if True
Returns:
List of Dictionaries. Raw or processed structured data.
"""
jc.utils.compatibility(__name__, info.compatible, quiet)
jc.utils.input_type_check(data)
raw_output: List[Dict] = []
if jc.utils.has_data(data):
itrparse = False
itr: Dict = {}
for line in filter(None, data.splitlines()):
line = line.strip()
# default 'ok'
if line.startswith('ok'):
raw_output.append({'command': 'ok'})
continue
# status
if line.startswith('version:'):
status = {}
linedata = line.split(':', maxsplit=1)
version = linedata[1].strip()
status.update({'version': version})
continue
if line.startswith('verbosity:'):
linedata = line.split(':', maxsplit=1)
verbosity = linedata[1]
status.update({'verbosity': verbosity})
continue
if line.startswith('ratelimit:'):
linedata = line.split(':', maxsplit=1)
ratelimit = linedata[1]
status.update({'ratelimit': ratelimit})
raw_output.append(status)
continue
# print_cookie_secrets
if line.startswith('active'):
itrparse = True
itr = {}
linedata = line.split(':', maxsplit=1)
active = linedata[1].strip()
itr.update({'active': active})
continue
if line.startswith('staging'):
linedata = line.split(':', maxsplit=1)
staging = linedata[1].strip()
itr.update({'staging': staging})
continue
# print_tsig
if line.startswith('key:'):
tsigs = {}
tsigdata = dict()
linedata = line.split(' ', maxsplit=6)
name = linedata[2].strip('"').rstrip('"')
tsigdata.update({'name': name})
secret = linedata[4].strip('"').rstrip('"')
tsigdata.update({'secret': secret})
algorithm = linedata[6].strip('"').rstrip('"')
tsigdata.update({'algorithm': algorithm})
tsigs.update({'key': tsigdata})
raw_output.append(tsigs)
continue
# zonestatus
if line.startswith('zone:'):
zonename: Dict = dict()
zstatus: Dict = dict()
linedata = line.split(':\t', maxsplit=1)
zone = linedata[1]
zonename.update({'zone': zone})
continue
if line.startswith('state:'):
linedata = line.split(': ', maxsplit=1)
state = linedata[1]
zstatus.update({'state': state})
continue
if line.startswith('served-serial:'):
linedata = line.split(': ', maxsplit=1)
served = linedata[1].strip('"').rstrip('"')
zstatus.update({'served-serial': served})
continue
if line.startswith('commit-serial:'):
linedata = line.split(': ', maxsplit=1)
commit = linedata[1].strip('"').rstrip('"')
zstatus.update({'commit-serial': commit})
continue
if line.startswith('wait:'):
linedata = line.split(': ', maxsplit=1)
wait = linedata[1].strip('"').rstrip('"')
zstatus.update({'wait': wait})
zonename.update({'status': zstatus})
raw_output.append(zonename)
continue
# stats
if line.startswith('server') or line.startswith('num.') or line.startswith('size.') or line.startswith('time.') or line.startswith('zone.'):
itrparse = True
linedata = line.split('=', maxsplit=1)
key = linedata[0]
if key.startswith('time.'):
value = float(linedata[1])
else:
value = int(linedata[1])
itr.update({key: value})
continue
if itrparse:
raw_output.append(itr)
return raw_output if raw else _process(raw_output)

113
jc/parsers/os_release.py Normal file
View File

@@ -0,0 +1,113 @@
"""jc - JSON Convert `/etc/os-release` file parser
This parser is an alias to the Key/Value parser (`--kv`).
Usage (cli):
$ cat /etc/os-release | jc --os-release
Usage (module):
import jc
result = jc.parse('os_release', os_release_output)
Schema:
{
"<key>": string
}
Examples:
$ cat /etc/os-release | jc --os-release -p
{
"NAME": "CentOS Linux",
"VERSION": "7 (Core)",
"ID": "centos",
"ID_LIKE": "rhel fedora",
"VERSION_ID": "7",
"PRETTY_NAME": "CentOS Linux 7 (Core)",
"ANSI_COLOR": "0;31",
"CPE_NAME": "cpe:/o:centos:centos:7",
"HOME_URL": "https://www.centos.org/",
"BUG_REPORT_URL": "https://bugs.centos.org/",
"CENTOS_MANTISBT_PROJECT": "CentOS-7",
"CENTOS_MANTISBT_PROJECT_VERSION": "7",
"REDHAT_SUPPORT_PRODUCT": "centos",
"REDHAT_SUPPORT_PRODUCT_VERSION": "7"
}
$ cat /etc/os-release | jc --os-release -p -r
{
"NAME": "\\"CentOS Linux\\"",
"VERSION": "\\"7 (Core)\\"",
"ID": "\\"centos\\"",
"ID_LIKE": "\\"rhel fedora\\"",
"VERSION_ID": "\\"7\\"",
"PRETTY_NAME": "\\"CentOS Linux 7 (Core)\\"",
"ANSI_COLOR": "\\"0;31\\"",
"CPE_NAME": "\\"cpe:/o:centos:centos:7\\"",
"HOME_URL": "\\"https://www.centos.org/\\"",
"BUG_REPORT_URL": "\\"https://bugs.centos.org/\\"",
"CENTOS_MANTISBT_PROJECT": "\\"CentOS-7\\"",
"CENTOS_MANTISBT_PROJECT_VERSION": "\\"7\\"",
"REDHAT_SUPPORT_PRODUCT": "\\"centos\\"",
"REDHAT_SUPPORT_PRODUCT_VERSION": "\\"7\\""
}
"""
from jc.jc_types import JSONDictType
import jc.parsers.kv
import jc.utils
class info():
"""Provides parser metadata (version, author, etc.)"""
version = '1.0'
description = '`/etc/os-release` file parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
details = 'Using the Key/Value parser'
compatible = ['linux', 'darwin', 'cygwin', 'win32', 'aix', 'freebsd']
tags = ['file', 'standard', 'string']
__version__ = info.version
def _process(proc_data: JSONDictType) -> JSONDictType:
"""
Final processing to conform to the schema.
Parameters:
proc_data: (Dictionary) raw structured data to process
Returns:
Dictionary. Structured to conform to the schema.
"""
return jc.parsers.kv._process(proc_data)
def parse(
data: str,
raw: bool = False,
quiet: bool = False
) -> JSONDictType:
"""
Main text parsing function
Parameters:
data: (string) text data to parse
raw: (boolean) unprocessed output if True
quiet: (boolean) suppress warning messages if True
Returns:
Dictionary. Raw or processed structured data.
"""
jc.utils.compatibility(__name__, info.compatible, quiet)
raw_output = jc.parsers.kv.parse(data, raw, quiet)
return raw_output if raw else _process(raw_output)

View File

@@ -96,14 +96,14 @@ class PBParser(object):
prefix = self.data[0:6]
for case in Switch(prefix):
if case('bplist'):
self.file_type = 'binary'
import biplist
parsed_plist = biplist.readPlist(self.file_path)
# self.file_type = 'binary'
# import biplist
# parsed_plist = biplist.readPlist(self.file_path)
break
if case('<?xml '):
self.file_type = 'xml'
import plistlib
parsed_plist = plistlib.readPlist(self.file_path)
# self.file_type = 'xml'
# import plistlib
# parsed_plist = plistlib.readPlist(self.file_path)
break
if case():
self.file_type = 'ascii'
@@ -111,7 +111,7 @@ class PBParser(object):
if self.data[0:2] == '//':
# this is to try to see if we can locate the desired string encoding of the file
import re
result = re.search('^// !\$\*(.+?)\*\$!', self.data) # pylint: disable=anomalous-backslash-in-string
result = re.search(r'^// !\$\*(.+?)\*\$!', self.data) # pylint: disable=anomalous-backslash-in-string
if result:
self.string_encoding = result.group(1)
#now return the parse

View File

@@ -57,7 +57,7 @@ def KeySorter(obj1, obj2):
result = StringCmp(str(obj1), str(obj2))
return result
class pbRoot(collections.MutableMapping):
class pbRoot(collections.abc.MutableMapping):
def __init__(self, *args, **kwargs):
self.store = dict()

View File

@@ -40,6 +40,9 @@ Schema:
"kb_ccwr_s": float,
"cswch_s": float,
"nvcswch_s": float,
"usr_ms": integer,
"system_ms": integer,
"guest_ms": integer,
"command": string
}
]
@@ -128,7 +131,7 @@ from jc.exceptions import ParseError
class info():
"""Provides parser metadata (version, author, etc.)"""
version = '1.1'
version = '1.3'
description = '`pidstat -H` command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
@@ -152,11 +155,16 @@ def _process(proc_data: List[Dict]) -> List[Dict]:
List of Dictionaries. Structured to conform to the schema.
"""
int_list = {'time', 'uid', 'pid', 'cpu', 'vsz', 'rss', 'stksize', 'stkref'}
int_list = {
'time', 'uid', 'pid', 'cpu', 'vsz', 'rss', 'stksize', 'stkref',
'usr_ms', 'system_ms', 'guest_ms'
}
float_list = {'percent_usr', 'percent_system', 'percent_guest', 'percent_cpu',
'minflt_s', 'majflt_s', 'percent_mem', 'kb_rd_s', 'kb_wr_s',
'kb_ccwr_s', 'cswch_s', 'nvcswch_s'}
float_list = {
'percent_usr', 'percent_system', 'percent_guest', 'percent_cpu',
'minflt_s', 'majflt_s', 'percent_mem', 'kb_rd_s', 'kb_wr_s',
'kb_ccwr_s', 'cswch_s', 'nvcswch_s', 'percent_wait'
}
for entry in proc_data:
for key in entry:
@@ -169,6 +177,14 @@ def _process(proc_data: List[Dict]) -> List[Dict]:
return proc_data
def normalize_header(header: str) -> str:
return header.replace('#', ' ')\
.replace('-', '_')\
.replace('/', '_')\
.replace('%', 'percent_')\
.lower()
def parse(
data: str,
raw: bool = False,
@@ -191,29 +207,28 @@ def parse(
jc.utils.input_type_check(data)
raw_output: List = []
table_list: List = []
header_found = False
if jc.utils.has_data(data):
# check for line starting with # as the start of the table
data_list = list(filter(None, data.splitlines()))
for line in data_list.copy():
if line.startswith('#'):
break
else:
data_list.pop(0)
if not data_list:
for line in data_list:
if line.startswith('#'):
header_found = True
if len(table_list) > 1:
raw_output.extend(simple_table_parse(table_list))
table_list = [normalize_header(line)]
continue
if header_found:
table_list.append(line)
if len(table_list) > 1:
raw_output.extend(simple_table_parse(table_list))
if not header_found:
raise ParseError('Could not parse pidstat output. Make sure to use "pidstat -h".')
# normalize header
data_list[0] = data_list[0].replace('#', ' ')\
.replace('/', '_')\
.replace('%', 'percent_')\
.lower()
# remove remaining header lines (e.g. pidstat -H 2 5)
data_list = [i for i in data_list if not i.startswith('#')]
raw_output = simple_table_parse(data_list)
return raw_output if raw else _process(raw_output)

View File

@@ -34,6 +34,7 @@ Schema:
"percent_usr": float,
"percent_system": float,
"percent_guest": float,
"percent_wait": float,
"percent_cpu": float,
"cpu": integer,
"minflt_s": float,
@@ -48,6 +49,9 @@ Schema:
"kb_ccwr_s": float,
"cswch_s": float,
"nvcswch_s": float,
"usr_ms": integer,
"system_ms": integer,
"guest_ms": integer,
"command": string,
# below object only exists if using -qq or ignore_exceptions=True
@@ -72,7 +76,7 @@ Examples:
{"time":"1646859134","uid":"0","pid":"9","percent_usr":"0.00","perc...}
...
"""
from typing import Dict, Iterable, Union
from typing import List, Dict, Iterable, Union
import jc.utils
from jc.streaming import (
add_jc_meta, streaming_input_type_check, streaming_line_input_type_check, raise_or_yield
@@ -83,7 +87,7 @@ from jc.exceptions import ParseError
class info():
"""Provides parser metadata (version, author, etc.)"""
version = '1.1'
version = '1.2'
description = '`pidstat -H` command streaming parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
@@ -107,11 +111,16 @@ def _process(proc_data: Dict) -> Dict:
Dictionary. Structured data to conform to the schema.
"""
int_list = {'time', 'uid', 'pid', 'cpu', 'vsz', 'rss', 'stksize', 'stkref'}
int_list = {
'time', 'uid', 'pid', 'cpu', 'vsz', 'rss', 'stksize', 'stkref',
'usr_ms', 'system_ms', 'guest_ms'
}
float_list = {'percent_usr', 'percent_system', 'percent_guest', 'percent_cpu',
'minflt_s', 'majflt_s', 'percent_mem', 'kb_rd_s', 'kb_wr_s',
'kb_ccwr_s', 'cswch_s', 'nvcswch_s'}
float_list = {
'percent_usr', 'percent_system', 'percent_guest', 'percent_wait',
'percent_cpu', 'minflt_s', 'majflt_s', 'percent_mem', 'kb_rd_s',
'kb_wr_s', 'kb_ccwr_s', 'cswch_s', 'nvcswch_s'
}
for key in proc_data:
if key in int_list:
@@ -123,6 +132,14 @@ def _process(proc_data: Dict) -> Dict:
return proc_data
def normalize_header(header: str) -> str:
return header.replace('#', ' ')\
.replace('-', '_')\
.replace('/', '_')\
.replace('%', 'percent_')\
.lower()
@add_jc_meta
def parse(
data: Iterable[str],
@@ -149,8 +166,8 @@ def parse(
jc.utils.compatibility(__name__, info.compatible, quiet)
streaming_input_type_check(data)
found_first_hash = False
header = ''
table_list: List = []
header: str = ''
for line in data:
try:
@@ -161,29 +178,30 @@ def parse(
# skip blank lines
continue
if not line.startswith('#') and not found_first_hash:
# skip preamble lines before header row
if line.startswith('#'):
if len(table_list) > 1:
output_line = simple_table_parse(table_list)[0]
yield output_line if raw else _process(output_line)
header = ''
header = normalize_header(line)
table_list = [header]
continue
if line.startswith('#') and not found_first_hash:
# normalize header
header = line.replace('#', ' ')\
.replace('/', '_')\
.replace('%', 'percent_')\
.lower()
found_first_hash = True
continue
if line.startswith('#') and found_first_hash:
# skip header lines after first one is found
continue
output_line = simple_table_parse([header, line])[0]
if output_line:
if header:
table_list.append(line)
output_line = simple_table_parse(table_list)[0]
yield output_line if raw else _process(output_line)
else:
raise ParseError('Not pidstat data')
table_list = [header]
continue
except Exception as e:
yield raise_or_yield(ignore_exceptions, e, line)
try:
if len(table_list) > 1:
output_line = simple_table_parse(table_list)[0]
yield output_line if raw else _process(output_line)
except Exception as e:
yield raise_or_yield(ignore_exceptions, e, str(table_list))

View File

@@ -30,6 +30,8 @@ Schema:
"packets_received": integer,
"packet_loss_percent": float,
"duplicates": integer,
"errors": integer,
"corrupted": integer,
"round_trip_ms_min": float,
"round_trip_ms_avg": float,
"round_trip_ms_max": float,
@@ -157,6 +159,7 @@ Examples:
]
}
"""
import re
import string
import ipaddress
import jc.utils
@@ -164,7 +167,7 @@ import jc.utils
class info():
"""Provides parser metadata (version, author, etc.)"""
version = '1.8'
version = '1.10'
description = '`ping` and `ping6` command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
@@ -190,7 +193,7 @@ def _process(proc_data):
"""
int_list = {
'data_bytes', 'packets_transmitted', 'packets_received', 'bytes', 'icmp_seq', 'ttl',
'duplicates', 'vr', 'hl', 'tos', 'len', 'id', 'flg', 'off', 'pro', 'cks'
'duplicates', 'corrupted', 'errors', 'vr', 'hl', 'tos', 'len', 'id', 'flg', 'off', 'pro', 'cks'
}
float_list = {
@@ -284,6 +287,8 @@ def _linux_parse(data):
if ipv4 and linedata[0][5] not in string.digits:
hostname = True
# fixup for missing hostname
linedata[0] = linedata[0][:5] + 'nohost' + linedata[0][5:]
elif ipv4 and linedata[0][5] in string.digits:
hostname = False
elif not ipv4 and ' (' in linedata[0]:
@@ -314,45 +319,52 @@ def _linux_parse(data):
if line.startswith('---'):
footer = True
raw_output['destination'] = line.split()[1]
if line[4] != ' ': # fixup for missing hostname
raw_output['destination'] = line.split()[1]
continue
if footer:
if 'packets transmitted' in line:
if ' duplicates,' in line:
raw_output.update(
{
'packets_transmitted': line.split()[0],
'packets_received': line.split()[3],
'packet_loss_percent': line.split()[7].rstrip('%'),
'duplicates': line.split()[5].lstrip('+'),
'time_ms': line.split()[11].replace('ms', '')
}
)
continue
else:
raw_output.update(
{
'packets_transmitted': line.split()[0],
'packets_received': line.split()[3],
'packet_loss_percent': line.split()[5].rstrip('%'),
'duplicates': '0',
'time_ms': line.split()[9].replace('ms', '')
}
)
continue
# Init in zero, to keep compatibility with previous behaviour
if 'duplicates' not in raw_output:
raw_output['duplicates'] = '0'
else:
split_line = line.split(' = ')[1]
split_line = split_line.split('/')
raw_output.update(
{
'round_trip_ms_min': split_line[0],
'round_trip_ms_avg': split_line[1],
'round_trip_ms_max': split_line[2],
'round_trip_ms_stddev': split_line[3].split()[0]
}
)
#
# See: https://github.com/dgibson/iputils/blob/master/ping_common.c#L995
#
m = re.search(r'(\d+) packets transmitted', line)
if m:
raw_output['packets_transmitted'] = m.group(1)
m = re.search(r'(\d+) received,', line)
if m:
raw_output['packets_received'] = m.group(1)
m = re.search(r'[+](\d+) duplicates', line)
if m:
raw_output['duplicates'] = m.group(1)
m = re.search(r'[+](\d+) corrupted', line)
if m:
raw_output['corrupted'] = m.group(1)
m = re.search(r'[+](\d+) errors', line)
if m:
raw_output['errors'] = m.group(1)
m = re.search(r'([\d\.]+)% packet loss', line)
if m:
raw_output['packet_loss_percent'] = m.group(1)
m = re.search(r'time (\d+)ms', line)
if m:
raw_output['time_ms'] = m.group(1)
m = re.search(r'rtt min\/avg\/max\/mdev += +([\d\.]+)\/([\d\.]+)\/([\d\.]+)\/([\d\.]+) ms', line)
if m:
raw_output['round_trip_ms_min'] = m.group(1)
raw_output['round_trip_ms_avg'] = m.group(2)
raw_output['round_trip_ms_max'] = m.group(3)
raw_output['round_trip_ms_stddev'] = m.group(4)
# ping response lines
else:

View File

@@ -31,7 +31,7 @@ Schema:
"source_ip": string,
"destination_ip": string,
"sent_bytes": integer,
"pattern": string, # (null if not set)
"pattern": string, # null if not set
"destination": string,
"timestamp": float,
"response_bytes": integer,
@@ -44,10 +44,12 @@ Schema:
"packets_received": integer,
"packet_loss_percent": float,
"duplicates": integer,
"round_trip_ms_min": float,
"round_trip_ms_avg": float,
"round_trip_ms_max": float,
"round_trip_ms_stddev": float,
"errors": integer, # null if not set
"corrupted": integer, # null if not set
"round_trip_ms_min": float, # null if not set
"round_trip_ms_avg": float, # null if not set
"round_trip_ms_max": float, # null if not set
"round_trip_ms_stddev": float, # null if not set
# below object only exists if using -qq or ignore_exceptions=True
"_jc_meta": {
@@ -74,6 +76,7 @@ Examples:
{"type":"reply","destination_ip":"1.1.1.1","sent_bytes":"56","patte...}
...
"""
import re
import string
import ipaddress
import jc.utils
@@ -85,7 +88,7 @@ from jc.exceptions import ParseError
class info():
"""Provides parser metadata (version, author, etc.)"""
version = '1.2'
version = '1.4'
description = '`ping` and `ping6` command streaming parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
@@ -110,13 +113,14 @@ def _process(proc_data):
Dictionary. Structured data to conform to the schema.
"""
int_list = {
'sent_bytes', 'packets_transmitted', 'packets_received', 'response_bytes', 'icmp_seq',
'ttl', 'duplicates', 'vr', 'hl', 'tos', 'len', 'id', 'flg', 'off', 'pro', 'cks'
'sent_bytes', 'packets_transmitted', 'packets_received',
'response_bytes', 'icmp_seq', 'ttl', 'duplicates', 'vr', 'hl', 'tos',
'len', 'id', 'flg', 'off', 'pro', 'cks', 'errors', 'corrupted'
}
float_list = {
'packet_loss_percent', 'round_trip_ms_min', 'round_trip_ms_avg', 'round_trip_ms_max',
'round_trip_ms_stddev', 'timestamp', 'time_ms'
'packet_loss_percent', 'round_trip_ms_min', 'round_trip_ms_avg',
'round_trip_ms_max', 'round_trip_ms_stddev', 'timestamp', 'time_ms'
}
for key in proc_data:
@@ -144,6 +148,12 @@ class _state:
packet_loss_percent = None
time_ms = None
duplicates = None
corrupted = None
errors = None
round_trip_ms_min = None
round_trip_ms_avg = None
round_trip_ms_max = None
round_trip_ms_stddev = None
def _ipv6_in(line):
@@ -340,6 +350,8 @@ def _linux_parse(line, s):
if s.ipv4 and line[5] not in string.digits:
s.hostname = True
# fixup for missing hostname
line = line[:5] + 'nohost' + line[5:]
elif s.ipv4 and line[5] in string.digits:
s.hostname = False
elif not s.ipv4 and ' (' in line:
@@ -367,24 +379,44 @@ def _linux_parse(line, s):
return None
if s.footer:
if 'packets transmitted' in line:
if ' duplicates,' in line:
s.packets_transmitted = line.split()[0]
s.packets_received = line.split()[3]
s.packet_loss_percent = line.split()[7].rstrip('%')
s.duplicates = line.split()[5].lstrip('+')
s.time_ms = line.split()[11].replace('ms', '')
return None
#
# See: https://github.com/dgibson/iputils/blob/master/ping_common.c#L995
#
m = re.search(r'(\d+) packets transmitted', line)
if m:
s.packets_transmitted = m.group(1)
s.packets_transmitted = line.split()[0]
s.packets_received = line.split()[3]
s.packet_loss_percent = line.split()[5].rstrip('%')
s.duplicates = '0'
s.time_ms = line.split()[9].replace('ms', '')
return None
m = re.search(r'(\d+) received,', line)
if m:
s.packets_received = m.group(1)
m = re.search(r'[+](\d+) duplicates', line)
if m:
s.duplicates = m.group(1)
m = re.search(r'[+](\d+) corrupted', line)
if m:
s.corrupted = m.group(1)
m = re.search(r'[+](\d+) errors', line)
if m:
s.errors = m.group(1)
m = re.search(r'([\d\.]+)% packet loss', line)
if m:
s.packet_loss_percent = m.group(1)
m = re.search(r'time (\d+)ms', line)
if m:
s.time_ms = m.group(1)
m = re.search(r'rtt min\/avg\/max\/mdev += +([\d\.]+)\/([\d\.]+)\/([\d\.]+)\/([\d\.]+) ms', line)
if m:
s.round_trip_ms_min = m.group(1)
s.round_trip_ms_avg = m.group(2)
s.round_trip_ms_max = m.group(3)
s.round_trip_ms_stddev = m.group(4)
split_line = line.split(' = ')[1]
split_line = split_line.split('/')
output_line = {
'type': 'summary',
'destination_ip': s.destination_ip or None,
@@ -392,15 +424,16 @@ def _linux_parse(line, s):
'pattern': s.pattern or None,
'packets_transmitted': s.packets_transmitted or None,
'packets_received': s.packets_received or None,
'packet_loss_percent': s.packet_loss_percent or None,
'duplicates': s.duplicates or None,
'time_ms': s.time_ms or None,
'round_trip_ms_min': split_line[0],
'round_trip_ms_avg': split_line[1],
'round_trip_ms_max': split_line[2],
'round_trip_ms_stddev': split_line[3].split()[0]
'packet_loss_percent': s.packet_loss_percent,
'duplicates': s.duplicates or '0',
'errors': s.errors,
'corrupted': s.corrupted,
'time_ms': s.time_ms,
'round_trip_ms_min': s.round_trip_ms_min,
'round_trip_ms_avg': s.round_trip_ms_avg,
'round_trip_ms_max': s.round_trip_ms_max,
'round_trip_ms_stddev': s.round_trip_ms_stddev
}
return output_line
# ping response lines
@@ -486,6 +519,7 @@ def parse(data, raw=False, quiet=False, ignore_exceptions=False):
streaming_input_type_check(data)
s = _state()
summary_obj = {}
for line in data:
try:
@@ -526,6 +560,12 @@ def parse(data, raw=False, quiet=False, ignore_exceptions=False):
if s.os_detected and s.linux:
output_line = _linux_parse(line, s)
# summary can be multiple lines so don't output until the end
if output_line:
if output_line.get('type', None) == 'summary':
summary_obj = output_line
continue
elif s.os_detected and s.bsd:
output_line = _bsd_parse(line, s)
@@ -540,3 +580,10 @@ def parse(data, raw=False, quiet=False, ignore_exceptions=False):
except Exception as e:
yield raise_or_yield(ignore_exceptions, e, line)
# yield summary, if it exists
try:
if summary_obj:
yield summary_obj if raw else _process(summary_obj)
except Exception as e:
yield raise_or_yield(ignore_exceptions, e, str(summary_obj))

View File

@@ -194,6 +194,7 @@ def parse(
net_packet_p = re.compile(r'^sk RefCnt Type Proto Iface R Rmem User Inode\n')
net_protocols_p = re.compile(r'^protocol size sockets memory press maxhdr slab module cl co di ac io in de sh ss gs se re sp bi br ha uh gp em\n')
net_route_p = re.compile(r'^Iface\tDestination\tGateway \tFlags\tRefCnt\tUse\tMetric\tMask\t\tMTU\tWindow\tIRTT\s+\n')
net_tcp_p = re.compile(r'^\s+sl\s+local_address\s+(?:rem_address|remote_address)\s+st\s+tx_queue\s+rx_queue\s+tr\s+tm->when\s+retrnsmt\s+uid\s+timeout\s+inode')
net_unix_p = re.compile(r'^Num RefCount Protocol Flags Type St Inode Path\n')
pid_fdinfo_p = re.compile(r'^pos:\t\d+\nflags:\t\d+\nmnt_id:\t\d+\n')
@@ -204,7 +205,7 @@ def parse(
pid_smaps_p = re.compile(r'^[0-9a-f]{12}-[0-9a-f]{12} [rwxsp\-]{4} [0-9a-f]{8} [0-9a-f]{2}:[0-9a-f]{2} \d+ [^\n]+\nSize:\s+\d+ \S\S\n')
pid_stat_p = re.compile(r'^\d+ \(.+\) \S \d+ \d+ \d+ \d+ -?\d+ (?:\d+ ){43}\d+$', re.DOTALL)
pid_statm_p = re.compile(r'^\d+ \d+ \d+\s\d+\s\d+\s\d+\s\d+$')
pid_status_p = re.compile(r'^Name:\t.+\nUmask:\t\d+\nState:\t.+\nTgid:\t\d+\n')
pid_status_p = re.compile(r'^Name:\t.+\n(?:Umask:\t\d+\n)?State:\t.+\nTgid:\t\d+\n')
# scsi_device_info = re.compile(r"^'\w+' '.+' 0x\d+")
# scsi_scsi_p = re.compile(r'^Attached devices:\nHost: \w+ ')
@@ -249,6 +250,7 @@ def parse(
net_packet_p: 'proc_net_packet',
net_protocols_p: 'proc_net_protocols',
net_route_p: 'proc_net_route',
net_tcp_p: 'proc_net_tcp',
net_unix_p: 'proc_net_unix',
net_ipv6_route_p: 'proc_net_ipv6_route', # before net_dev_mcast
net_dev_mcast_p: 'proc_net_dev_mcast', # after net_ipv6_route

293
jc/parsers/proc_net_tcp.py Normal file
View File

@@ -0,0 +1,293 @@
"""jc - JSON Convert `/proc/net/tcp` and `proc/net/tcp6` file parser
IPv4 and IPv6 addresses are converted to standard notation unless the raw
(--raw) option is used.
Usage (cli):
$ cat /proc/net/tcp | jc --proc
or
$ jc /proc/net/tcp
or
$ cat /proc/net/tcp | jc --proc-net-tcp
Usage (module):
import jc
result = jc.parse('proc', proc_net_tcp_file)
or
import jc
result = jc.parse('proc_net_tcp', proc_net_tcp_file)
Schema:
Field names and types gathered from the following:
https://www.kernel.org/doc/Documentation/networking/proc_net_tcp.txt
https://github.com/torvalds/linux/blob/master/net/ipv4/tcp_ipv4.c
https://github.com/torvalds/linux/blob/master/net/ipv6/tcp_ipv6.c
[
{
"entry": integer,
"local_address": string,
"local_port": integer,
"remote_address": string,
"remote_port": integer,
"state": string,
"tx_queue": string,
"rx_queue": string,
"timer_active": integer,
"jiffies_until_timer_expires": string,
"unrecovered_rto_timeouts": string,
"uid": integer,
"unanswered_0_window_probes": integer,
"inode": integer,
"sock_ref_count": integer,
"sock_mem_loc": string,
"retransmit_timeout": integer,
"soft_clock_tick": integer,
"ack_quick_pingpong": integer,
"sending_congestion_window": integer,
"slow_start_size_threshold": integer
}
]
Examples:
$ cat /proc/net/tcp | jc --proc -p
[
{
"entry": "0",
"local_address": "10.0.0.28",
"local_port": 42082,
"remote_address": "64.12.0.108",
"remote_port": 80,
"state": "04",
"tx_queue": "00000001",
"rx_queue": "00000000",
"timer_active": 1,
"jiffies_until_timer_expires": "00000015",
"unrecovered_rto_timeouts": "00000000",
"uid": 0,
"unanswered_0_window_probes": 0,
"inode": 0,
"sock_ref_count": 3,
"sock_mem_loc": "ffff8c7a0de930c0",
"retransmit_timeout": 21,
"soft_clock_tick": 4,
"ack_quick_pingpong": 30,
"sending_congestion_window": 10,
"slow_start_size_threshold": -1
},
{
"entry": "1",
"local_address": "10.0.0.28",
"local_port": 38864,
"remote_address": "104.244.42.65",
"remote_port": 80,
"state": "06",
"tx_queue": "00000000",
"rx_queue": "00000000",
"timer_active": 3,
"jiffies_until_timer_expires": "000007C5",
"unrecovered_rto_timeouts": "00000000",
"uid": 0,
"unanswered_0_window_probes": 0,
"inode": 0,
"sock_ref_count": 3,
"sock_mem_loc": "ffff8c7a12d31aa0"
},
...
]
$ cat /proc/net/tcp | jc --proc -p -r
[
{
"entry": "1",
"local_address": "1C00000A",
"local_port": "A462",
"remote_address": "6C000C40",
"remote_port": "0050",
"state": "04",
"tx_queue": "00000001",
"rx_queue": "00000000",
"timer_active": "01",
"jiffies_until_timer_expires": "00000015",
"unrecovered_rto_timeouts": "00000000",
"uid": "0",
"unanswered_0_window_probes": "0",
"inode": "0",
"sock_ref_count": "3",
"sock_mem_loc": "ffff8c7a0de930c0",
"retransmit_timeout": "21",
"soft_clock_tick": "4",
"ack_quick_pingpong": "30",
"sending_congestion_window": "10",
"slow_start_size_threshold": "-1"
},
{
"entry": "2",
"local_address": "1C00000A",
"local_port": "97D0",
"remote_address": "412AF468",
"remote_port": "0050",
"state": "06",
"tx_queue": "00000000",
"rx_queue": "00000000",
"timer_active": "03",
"jiffies_until_timer_expires": "000007C5",
"unrecovered_rto_timeouts": "00000000",
"uid": "0",
"unanswered_0_window_probes": "0",
"inode": "0",
"sock_ref_count": "3",
"sock_mem_loc": "ffff8c7a12d31aa0"
},
...
]
"""
import binascii
import socket
import struct
from typing import List, Dict
import jc.utils
class info():
"""Provides parser metadata (version, author, etc.)"""
version = '1.0'
description = '`/proc/net/tcp` and `/proc/net/tcp6` file parser'
author = 'Alvin Solomon'
author_email = 'alvinms01@gmail.com'
compatible = ['linux']
tags = ['file']
hidden = True
__version__ = info.version
def hex_to_ip(hexaddr: str) -> str:
if len(hexaddr) == 8:
addr_long = int(hexaddr, 16)
return socket.inet_ntop(socket.AF_INET, struct.pack("<L", addr_long))
elif len(hexaddr) == 32:
addr = binascii.a2b_hex(hexaddr)
addr_tup = struct.unpack('>IIII', addr)
addr_bytes = struct.pack('@IIII', *addr_tup)
return socket.inet_ntop(socket.AF_INET6, addr_bytes)
return ''
def _process(proc_data: List[Dict]) -> List[Dict]:
"""
Final processing to conform to the schema.
Parameters:
proc_data: (List of Dictionaries) raw structured data to process
Returns:
List of Dictionaries. Structured to conform to the schema.
"""
int_list = {
'timer_active', 'uid', 'unanswered_0_window_probes', 'inode',
'sock_ref_count', 'retransmit_timeout', 'soft_clock_tick',
'ack_quick_pingpong', 'sending_congestion_window',
'slow_start_size_threshold'
}
for entry in proc_data:
if 'local_address' in entry:
entry['local_address'] = hex_to_ip(entry['local_address'])
entry['local_port'] = int(entry['local_port'], 16)
entry['remote_address'] = hex_to_ip(entry['remote_address'])
entry['remote_port'] = int(entry['remote_port'], 16)
for item in int_list:
if item in entry:
entry[item] = jc.utils.convert_to_int(entry[item])
return proc_data
def parse(
data: str,
raw: bool = False,
quiet: bool = False
) -> List[Dict]:
"""
Main text parsing function
Parameters:
data: (string) text data to parse
raw: (boolean) unprocessed output if True
quiet: (boolean) suppress warning messages if True
Returns:
List of Dictionaries. Raw or processed structured data.
"""
jc.utils.compatibility(__name__, info.compatible, quiet)
jc.utils.input_type_check(data)
raw_output: List = []
if jc.utils.has_data(data):
line_data = data.splitlines()[1:]
for entry in line_data:
line = entry.split()
output_line = {}
output_line['entry'] = line[0][:-1]
local_ip_port = line[1]
local_ip = local_ip_port.split(':')[0]
local_port = local_ip_port.split(':')[1]
output_line['local_address'] = local_ip
output_line['local_port'] = local_port
remote_ip_port = line[2]
remote_ip = remote_ip_port.split(':')[0]
remote_port = remote_ip_port.split(':')[1]
output_line['remote_address'] = remote_ip
output_line['remote_port'] = remote_port
output_line['state'] = line[3]
output_line['tx_queue'] = line[4][:8]
output_line['rx_queue'] = line[4][9:]
output_line['timer_active'] = line[5][:2]
output_line['jiffies_until_timer_expires'] = line[5][3:]
output_line['unrecovered_rto_timeouts'] = line[6]
output_line['uid'] = line[7]
output_line['unanswered_0_window_probes'] = line[8]
output_line['inode'] = line[9]
output_line['sock_ref_count'] = line[10]
output_line['sock_mem_loc'] = line[11]
# fields not always included
if len(line) > 12:
output_line['retransmit_timeout'] = line[12]
output_line['soft_clock_tick'] = line[13]
output_line['ack_quick_pingpong'] = line[14]
output_line['sending_congestion_window'] = line[15]
output_line['slow_start_size_threshold'] = line[16]
raw_output.append(output_line)
return raw_output if raw else _process(raw_output)

View File

@@ -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
View 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.

View File

171
jc/parsers/pyedid/edid.py Executable file
View 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))

View File

View 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

View File

@@ -0,0 +1,136 @@
"""
Module for working with PNP ID REGISTRY
"""
import csv
import string
from html.parser import HTMLParser
from urllib import request
__all__ = ["Registry"]
class WebPnpIdParser(HTMLParser):
"""Parser pnp id from https://uefi.org/PNP_ID_List
Examples:
p = WebPnpIdParser()
p.feed(html_data)
p.result
"""
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self._find_table = False
self._find_row = False
# first -- company name, second -- pnp id, third -- approved date
self._last_field = []
# key -- pnp id, value -- tuple (company_name, approved_date)
self.result = {}
def handle_starttag(self, tag, attrs):
if tag == "tbody":
self._find_table = True
elif self._find_table and tag == "tr":
self._find_row = True
def handle_endtag(self, tag):
if tag == "tbody":
self._find_table = False
elif self._find_table and tag == "tr":
self._find_row = False
# add table row to result
self.result[self._last_field[1]] = (
self._last_field[0],
self._last_field[-1],
)
self._last_field.clear()
def handle_data(self, data):
# skip processing until table is found
if not self._find_table:
return
if self._find_row:
data = data.strip()
if data:
self._last_field.append(data)
def error(self, message):
super().close()
class Registry(dict):
"""Registry pnp id data dictionary
key -- pnp_id
value -- company name
"""
@classmethod
def from_web(cls, filter_by_id: str = None):
"""Get registry from https://uefi.org/PNP_ID_List
Args:
filter_by_id (str), optional: filter registry by id
Raises:
Returns:
"""
url = "https://uefi.org/PNP_ID_List"
if filter_by_id:
url += "?search={}".format(filter_by_id)
with request.urlopen(url) as req:
parse = WebPnpIdParser()
parse.feed(req.read().decode())
registry = cls()
for key, value in parse.result.items():
# skip invalid search value
if filter_by_id and key != filter_by_id:
continue
registry[key] = value[0]
return registry
@classmethod
def from_csv(cls, csv_path: str, filter_by_id: str = None):
"""Get registry by csv local file
Args:
csv_path (str): path to csv file
filter_by_id (str), optional: filter registry by id
Raises:
Returns:
"""
registry = cls()
with open(csv_path, "r") as file:
reader = csv.reader(file)
for line in reader:
# filter
if filter_by_id and filter_by_id != line[0]:
continue
registry[line[0]] = line[1]
return registry
def to_csv(self, csv_path: str):
"""Dump registry to csv file"""
with open(csv_path, "w") as csv_file:
writer = csv.writer(csv_file)
writer.writerows(self.items())
return self
def get_company_from_id(self, pnp_id: str) -> str:
"""Convert PNP id to company name"""
return self.get(pnp_id, "Unknown")
def get_company_from_raw(self, raw: int) -> str:
"""Convert raw edid value to company name"""
tmp = [(raw >> 10) & 31, (raw >> 5) & 31, raw & 31]
pnp_id = "".join(string.ascii_uppercase[n - 1] for n in tmp)
return self.get_company_from_id(pnp_id)

Some files were not shown because too many files have changed in this diff Show More