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

Compare commits

...

574 Commits

Author SHA1 Message Date
Kelly Brazil
5344883394 spelling/add ping parser update 2020-08-05 11:35:52 -07:00
Kelly Brazil
3fcd2f6c2e version bump 2020-08-05 11:34:29 -07:00
Kelly Brazil
f3d84bd5bf tighten crontab and crontab-u parser variable detection 2020-08-05 11:28:37 -07:00
Kelly Brazil
549780c232 add debian/apt-get info 2020-08-05 11:04:07 -07:00
Kelly Brazil
2a6da69b82 improve linux/bsd check 2020-08-05 09:58:01 -07:00
Kelly Brazil
5c538816cf ping parser fix for raspberry pi 2020-08-04 17:36:35 -07:00
Kelly Brazil
7b8b378a7d add pydoc-markdown version requirement 2020-08-03 12:39:18 -07:00
Kelly Brazil
e30a75e25c Merge pull request #76 from kellyjonbrazil/dev
Dev v1.13.2
2020-08-03 10:18:39 -07:00
Kelly Brazil
85ad5cfd0b date change 2020-08-03 10:14:13 -07:00
Kelly Brazil
88b9d5068c finish date parser 2020-08-03 09:26:37 -07:00
Kelly Brazil
f8c4948a09 remove comment 2020-07-31 16:52:48 -07:00
Kelly Brazil
412322447f add month_num and weekday_num fields 2020-07-31 16:51:05 -07:00
Kelly Brazil
d4f289e40f documentation fixup 2020-07-31 14:47:06 -07:00
Kelly Brazil
e1f3feb8f5 cover empty data case in process 2020-07-31 14:45:30 -07:00
Kelly Brazil
37d3bc699c add date parser 2020-07-31 14:39:02 -07:00
Kelly Brazil
672fd18016 date bump 2020-07-31 12:59:44 -07:00
Kelly Brazil
bc2c23a2a0 version bump 2020-07-31 12:58:58 -07:00
Kelly Brazil
865f7e7812 add kv parser to man page 2020-07-31 12:58:11 -07:00
Kelly Brazil
720212b552 fixup traceroute example with new behavior 2020-07-31 11:02:24 -07:00
Kelly Brazil
d3be61f608 version bump 2020-07-31 10:59:46 -07:00
Kelly Brazil
13418b16b8 doc update 2020-07-31 10:56:51 -07:00
Kelly Brazil
42d2017cd6 traceroute updates: handle missing header row, add annotations, don't print timeouts as probes 2020-07-31 10:53:47 -07:00
Kelly Brazil
4345e76ead change to use --kv for key/value files 2020-07-30 16:49:38 -07:00
Kelly Brazil
741431322b update tests for kv parser 2020-07-30 16:45:36 -07:00
Kelly Brazil
980beaaf41 fix docgen issue 2020-07-30 16:21:03 -07:00
Kelly Brazil
2205034e09 add kv parser 2020-07-30 16:20:51 -07:00
Kelly Brazil
82b9c87a66 update docs 2020-07-30 16:20:24 -07:00
Kelly Brazil
dda517a937 shorten more examples 2020-07-29 14:36:20 -07:00
Kelly Brazil
4e6d283b9e shorten netstat example 2020-07-29 14:27:13 -07:00
Kelly Brazil
55acab05aa change name to CHANGELOG 2020-07-29 11:55:16 -07:00
Kelly Brazil
ed38a18d23 remove more examples 2020-07-29 11:49:00 -07:00
Kelly Brazil
95b3c11203 remove more examples 2020-07-29 11:43:22 -07:00
Kelly Brazil
dce318f4fd remove examples to reduce file size 2020-07-29 11:40:47 -07:00
Kelly Brazil
85127f0fb8 move examples to root 2020-07-29 11:34:43 -07:00
Kelly Brazil
fb45058244 add examples file 2020-07-29 11:32:18 -07:00
Kelly Brazil
45bb5ae389 spelling 2020-07-28 11:03:41 -07:00
Kelly Brazil
339238ab36 version bump and add route -6 tests 2020-07-27 20:37:33 -07:00
Kelly Brazil
032cda8b3d Merge pull request #74 from kellyjonbrazil/dev
Dev v1.13.0
2020-07-27 19:12:21 -07:00
Kelly Brazil
6badd3fb1e add parser count test 2020-07-27 19:02:23 -07:00
Kelly Brazil
724d825745 add tracepath parser 2020-07-27 19:02:11 -07:00
Kelly Brazil
ff1e32ad2e version bump 2020-07-27 16:49:34 -07:00
Kelly Brazil
a5f97febd3 update traceroute, tracepath, and uname tests 2020-07-27 16:47:41 -07:00
Kelly Brazil
5baa6cc865 add route parser update 2020-07-27 15:44:13 -07:00
Kelly Brazil
7a4f30b843 fix for iface issue 2020-07-27 15:37:44 -07:00
Kelly Brazil
b2c385dc4f change 'if' to 'iface' 2020-07-27 15:30:09 -07:00
Kelly Brazil
5d5da8d33f more fixes for ipv6 fix 2020-07-27 15:27:11 -07:00
Kelly Brazil
e604571578 fix next_hop fix 2020-07-27 15:20:51 -07:00
Kelly Brazil
f9dacc3f95 fixup for ipv6 2020-07-27 15:18:13 -07:00
Kelly Brazil
6086920332 update ParseError message 2020-07-27 15:13:32 -07:00
Kelly Brazil
f52f3163bc add tracepath example 2020-07-27 13:57:58 -07:00
Kelly Brazil
d18ff73e88 update author info 2020-07-27 13:51:51 -07:00
Kelly Brazil
1e5d602cae working tracepath parser 2020-07-27 13:48:46 -07:00
Kelly Brazil
12912521ec doc update 2020-07-27 11:04:27 -07:00
Kelly Brazil
842ea3a94b add tracepath parser skeleton 2020-07-27 11:02:13 -07:00
Kelly Brazil
a8560dbc15 add tracepath 2020-07-27 11:01:57 -07:00
Kelly Brazil
a65e27540a update docs 2020-07-27 11:01:45 -07:00
Kelly Brazil
c3c5ed11e6 change name from tr to trparse 2020-07-27 10:33:40 -07:00
Kelly Brazil
ce24149335 formatting 2020-07-27 10:33:25 -07:00
Kelly Brazil
0314ca8c48 add trparse acknowledgement 2020-07-27 10:33:15 -07:00
Kelly Brazil
ebd8ee49a9 add key/value info to ini example 2020-07-27 10:28:15 -07:00
Kelly Brazil
38d10c9781 add ping and traceroute examples 2020-07-27 09:29:30 -07:00
Kelly Brazil
360106c24d add tracepath 2020-07-27 09:23:01 -07:00
Kelly Brazil
ca470a5d02 add tracepath fixtures 2020-07-27 09:20:00 -07:00
Kelly Brazil
57f66e6b1d add exception with hint to use "uname -a" 2020-07-27 09:19:48 -07:00
Kelly Brazil
e774f67924 turn off interpolation and coerce None to '' 2020-07-24 16:53:17 -07:00
Kelly Brazil
ac10e576c1 spelling 2020-07-24 16:29:27 -07:00
Kelly Brazil
bcae0a99cd add key/value to ini description 2020-07-24 16:23:45 -07:00
Kelly Brazil
c73c2ff879 add ping, traceroute, and ini update 2020-07-24 16:23:30 -07:00
Kelly Brazil
c39b1a3356 add ping, traceroute and update ini description 2020-07-24 16:17:51 -07:00
Kelly Brazil
125dc2d9e0 add info about key/value files to doc 2020-07-24 16:17:16 -07:00
Kelly Brazil
b7d4ddc7ce add tests for key/value files 2020-07-24 16:16:54 -07:00
Kelly Brazil
f5e546c6fa add support for simple key/value pairs 2020-07-24 16:16:24 -07:00
Kelly Brazil
928e39cd10 add generic traceroute tests 2020-07-24 14:16:41 -07:00
Kelly Brazil
d0b7ea68a0 check for key in dictionary 2020-07-24 13:47:47 -07:00
Kelly Brazil
8444690133 add traceroute 2020-07-24 13:47:29 -07:00
Kelly Brazil
c03c42d767 add traceroute tests 2020-07-24 13:47:23 -07:00
Kelly Brazil
ab67688a00 add test skeleton 2020-07-23 16:45:09 -07:00
Kelly Brazil
5dcb7166da add traceroute doc 2020-07-23 16:44:57 -07:00
Kelly Brazil
14697b86d7 add MIT license 2020-07-23 15:48:08 -07:00
Kelly Brazil
4f4b6276d4 update docstring 2020-07-23 15:46:22 -07:00
Kelly Brazil
7bc497e129 updated process() function to set integers and floats 2020-07-23 15:42:33 -07:00
Kelly Brazil
68a37a6a5a remove unused function load() 2020-07-23 12:48:33 -07:00
Kelly Brazil
6f5cd1d7c5 change to use f-string 2020-07-23 12:03:21 -07:00
Kelly Brazil
126b1b121c add traceroute6 example 2020-07-23 11:31:56 -07:00
Kelly Brazil
2341e456a0 use ParseError instead of generic Exception 2020-07-23 11:31:35 -07:00
Kelly Brazil
72d80e95bb remove unused regex patterns 2020-07-23 10:52:40 -07:00
Kelly Brazil
f5ec82440c simplify regex patterns 2020-07-23 10:19:56 -07:00
Kelly Brazil
c8e526ead3 fixes for bsd-style ipv6 output 2020-07-22 17:23:24 -07:00
Kelly Brazil
066adfb764 handle warning lines in the traceroute output 2020-07-22 15:02:02 -07:00
Kelly Brazil
5b444d4717 add traceroute parser 2020-07-22 12:19:27 -07:00
Kelly Brazil
69c95adc8d add osx ipv6 ping dup test 2020-07-22 09:06:11 -07:00
Kelly Brazil
2b0e0d8f5c add ipv6 dup test 2020-07-21 17:34:30 -07:00
Kelly Brazil
778d1bacbf update docs to add "duplicates" fields 2020-07-21 15:16:39 -07:00
Kelly Brazil
7e1b041016 add duplicate replies tests 2020-07-21 15:12:43 -07:00
Kelly Brazil
313b9b329c update fixtures for added 'duplicate' fields 2020-07-21 15:05:54 -07:00
Kelly Brazil
6830062256 add support for duplicate replies 2020-07-21 14:47:25 -07:00
Kelly Brazil
323072c982 add source_ip to schema doc 2020-07-21 09:02:44 -07:00
Kelly Brazil
8719d96bdd change description 2020-07-20 16:54:43 -07:00
Kelly Brazil
dd5d318ab5 version bump and add ping command 2020-07-20 16:25:20 -07:00
Kelly Brazil
d6dc7f5e65 add osx ping tests 2020-07-20 16:11:18 -07:00
Kelly Brazil
c203664eb5 freebsd ping tests 2020-07-20 15:46:27 -07:00
Kelly Brazil
19ecf1fa19 add Fedora32 tests 2020-07-20 14:35:41 -07:00
Kelly Brazil
b8deb0426c add ubuntu ping tests 2020-07-20 13:51:39 -07:00
Kelly Brazil
3b8371f020 add centos ping tests 2020-07-20 12:49:05 -07:00
Kelly Brazil
20bb1cdf39 add TypeError to except for None values 2020-07-20 11:53:06 -07:00
Kelly Brazil
301daa48d0 update documentation 2020-07-19 15:30:54 -07:00
Kelly Brazil
8421ec8803 remove cygwin compatibility 2020-07-19 15:19:47 -07:00
Kelly Brazil
74211eb012 add examples 2020-07-19 15:16:04 -07:00
Kelly Brazil
60bd42f298 add process() logic 2020-07-19 15:13:52 -07:00
Kelly Brazil
14bdd74526 add ping test fixtures 2020-07-19 14:46:02 -07:00
Kelly Brazil
fb0f3eda04 add ping commands 2020-07-19 14:45:50 -07:00
Kelly Brazil
91ee6e6701 add osx ping test fixtures 2020-07-19 14:18:56 -07:00
Kelly Brazil
51f4e6927c add support for pattern in osx/bsd 2020-07-19 14:18:40 -07:00
Kelly Brazil
94988d8667 add fedora ping fixtures 2020-07-19 13:50:40 -07:00
Kelly Brazil
fe36f5a98c add fixtures for ping 2020-07-19 12:56:53 -07:00
Kelly Brazil
f9eb18b927 change 'request_timeout' field to 'type', fix compatibility, other formatting fixes 2020-07-19 12:56:34 -07:00
Kelly Brazil
cc60f36748 add ping parser 2020-07-18 12:35:46 -07:00
Kelly Brazil
604ade791f add ping parser 2020-07-17 15:57:07 -07:00
Kelly Brazil
690ac52a91 add man page 2020-07-13 07:18:48 -07:00
Kelly Brazil
34ed772775 version bump 2020-07-11 09:46:47 -07:00
Kelly Brazil
d5ab95571f fix tests when using older versions of pygments 2020-07-11 09:44:08 -07:00
Kelly Brazil
ffb3a0ee5f Merge pull request #73 from kellyjonbrazil/dev
Dev v1.12.0
2020-07-10 16:34:56 -07:00
Kelly Brazil
94b12b57aa spelling 2020-07-10 16:30:08 -07:00
Kelly Brazil
6d149e8457 version bump 2020-07-10 16:25:03 -07:00
Kelly Brazil
1ad89c90d8 add pacman 2020-07-10 15:58:02 -07:00
Kelly Brazil
fb71c7b020 function name spelling 2020-07-10 15:49:35 -07:00
Kelly Brazil
28ed17ad3b add parser_count test to test_cli_about_jc 2020-07-10 15:45:21 -07:00
Kelly Brazil
0c2a4e2bf7 add cli tests 2020-07-10 15:35:05 -07:00
Kelly Brazil
62bec30de2 add json_out tests 2020-07-10 14:44:50 -07:00
Kelly Brazil
3fced77e4e add set_env_colors tests 2020-07-10 12:23:48 -07:00
Kelly Brazil
a09d1d8b76 move environment variable assignment to main() to simplify tests 2020-07-10 12:23:25 -07:00
Kelly Brazil
8f4243fbd8 formatting 2020-07-10 10:54:34 -07:00
Kelly Brazil
47aaf20549 add sysctl command parser 2020-07-10 10:50:51 -07:00
Kelly Brazil
0c5289ea50 add sysctl tests 2020-07-10 10:47:23 -07:00
Kelly Brazil
3e53323514 don't filter out empty lines 2020-07-10 09:58:56 -07:00
Kelly Brazil
a5ee9861b9 update fixtures 2020-07-10 09:28:32 -07:00
Kelly Brazil
feb8ca7654 spelling 2020-07-10 09:28:20 -07:00
Kelly Brazil
a7abe4473b spelling 2020-07-10 09:03:04 -07:00
Kelly Brazil
780b9b61de specify IndexError exception in try/except block 2020-07-10 08:30:31 -07:00
Kelly Brazil
19ace36ffa add fixtures 2020-07-10 08:21:30 -07:00
Kelly Brazil
5fff8afc9f add fixes for freebsd where values can be on separate lines under the key 2020-07-10 08:21:15 -07:00
Kelly Brazil
4ad230c927 doc update and add test fixtures 2020-07-09 16:35:36 -07:00
Kelly Brazil
dd98eb1ec8 append duplicate key values to original key instead of adding unique keys 2020-07-09 16:25:41 -07:00
Kelly Brazil
c6baf42e72 doc updates 2020-07-09 16:18:33 -07:00
Kelly Brazil
e2bac97d56 fix for multiple identical keys in sysctl output 2020-07-09 14:51:15 -07:00
Kelly Brazil
d112ee94d0 use try/except and add support for floats in process() 2020-07-09 14:26:35 -07:00
Kelly Brazil
27b21b2faf formatting and docstring updates 2020-07-09 11:11:29 -07:00
Kelly Brazil
8c96d5cd20 reduce pygments version requirement 2020-07-09 10:59:36 -07:00
Kelly Brazil
c29ed3fd69 formatting of quotation marks and docstrings 2020-07-09 10:54:49 -07:00
Kelly Brazil
cedf603f12 minor formatting 2020-07-09 09:59:00 -07:00
Kelly Brazil
279161c36f Merge pull request #72 from duelafn/pygments-2.3
Support older pygments
2020-07-09 09:48:58 -07:00
Dean Serenevy
ce0b43d919 Remove dependency on 3rd party packaging library 2020-07-09 12:44:41 -04:00
Dean Serenevy
ddafa5bf06 Support older pygments 2020-07-09 11:39:34 -04:00
Kelly Brazil
bc7116c31b fix JC_COLORS env bug and simplify set_env_colors() 2020-07-09 08:30:10 -07:00
Kelly Brazil
53b7092721 remove side-effects from functions and print in main() 2020-07-08 16:40:28 -07:00
Kelly Brazil
beb9174b1b add sysctl parser 2020-07-08 15:42:06 -07:00
Kelly Brazil
aea41ed341 move verbose_debug enable earlier in code to catch more issues. add sysctl and version bump 2020-07-08 15:41:46 -07:00
Kelly Brazil
d789494cb1 change type check to use isinstance 2020-07-08 05:59:19 -07:00
Kelly Brazil
608e7b4cff add license info 2020-07-06 10:52:12 -07:00
Kelly Brazil
4ee199c02a use tracebackplus instead of cgitb since cgitb is depricated 2020-07-06 10:41:01 -07:00
Kelly Brazil
fbf47d4085 add arch linux 2020-07-01 13:28:58 -07:00
Kelly Brazil
5a238e4b42 remove updates-testing from fedora command 2020-07-01 09:11:32 -07:00
Kelly Brazil
f852b8246a wrap warning message 2020-06-30 11:50:37 -07:00
Kelly Brazil
88140d929a wrap error message in code 2020-06-30 11:37:33 -07:00
Kelly Brazil
45f7268240 add -dd to error message 2020-06-30 11:34:08 -07:00
Kelly Brazil
3a3c8e4d4a move verbose_debug under debug check 2020-06-30 11:31:08 -07:00
Kelly Brazil
c1ac183a04 simplify debug option 2020-06-30 11:26:09 -07:00
Kelly Brazil
18bb779ee5 formatting: double quotes to single quotes 2020-06-30 09:39:05 -07:00
Kelly Brazil
8b6612fe79 move JC_COLORS parsing error message 2020-06-30 07:56:34 -07:00
Kelly Brazil
fde0bc8534 improve package install info 2020-06-27 18:53:19 -07:00
Kelly Brazil
e661a78939 Merge pull request #71 from wigust/guix
add guix package info
2020-06-27 18:43:02 -07:00
Oleg Pykhalov
847e346602 add guix package info 2020-06-27 13:01:56 +03:00
Kelly Brazil
b969751688 add other references 2020-06-26 09:53:57 -07:00
Kelly Brazil
ad6f2ba03a formatting 2020-06-25 21:16:52 -07:00
Kelly Brazil
63c6a5edc0 formatting 2020-06-25 21:12:30 -07:00
Kelly Brazil
9f4cf9dd5e formatting 2020-06-25 21:11:21 -07:00
Kelly Brazil
51331b6dc0 formatting 2020-06-25 13:01:10 -07:00
Kelly Brazil
efb6761033 formatting 2020-06-25 12:59:56 -07:00
Kelly Brazil
6a4f737a0f update json syntax highlighting 2020-06-25 12:56:06 -07:00
Kelly Brazil
be6864b778 add syntax highlighting tags 2020-06-25 12:28:23 -07:00
Kelly Brazil
de3b91a36c add -dd option 2020-06-25 07:38:39 -07:00
Kelly Brazil
ef5482c3b5 add verbose debug option 2020-06-25 07:29:28 -07:00
Kelly Brazil
d20b795137 Merge pull request #70 from kellyjonbrazil/dev
Dev v1.11.7
2020-06-22 11:28:23 -07:00
Kelly Brazil
8a134065df update fixtures for last chain fix 2020-06-22 11:23:37 -07:00
Kelly Brazil
22aee1bfa4 version bump 2020-06-22 11:23:15 -07:00
Kelly Brazil
b282820fd6 fix to include the final chain in output 2020-06-22 11:09:09 -07:00
Kelly Brazil
3ee098306d version bump 2020-06-22 10:48:51 -07:00
Kelly Brazil
09e8f379a6 iptables code optimizations 2020-06-22 10:47:34 -07:00
Kelly Brazil
69018cdb3a fix date 2020-06-14 17:39:14 -07:00
Kelly Brazil
d0d7254c6a add docstring 2020-06-14 17:23:10 -07:00
Kelly Brazil
cc0f0971d7 Improve and standardize empty data check for all parsers 2020-06-14 17:17:40 -07:00
Kelly Brazil
2af61730f0 Merge pull request #68 from kellyjonbrazil/dev
Dev v1.11.5
2020-06-12 12:34:34 -07:00
Kelly Brazil
83f41b83dc version bump 2020-06-12 12:30:19 -07:00
Kelly Brazil
1fb84fce88 fix for no data 2020-06-12 12:25:07 -07:00
Kelly Brazil
a8837e1244 remove --upgrade from pip install 2020-06-12 07:57:40 -07:00
Kelly Brazil
04d2eec558 fix for no data 2020-06-11 17:59:06 -07:00
Kelly Brazil
1b57ec92f0 fix for no data 2020-06-11 17:52:03 -07:00
Kelly Brazil
4d88595404 enhance empty data check 2020-06-11 17:16:11 -07:00
Kelly Brazil
52b1272a3a enhance empty data check 2020-06-11 17:13:45 -07:00
Kelly Brazil
d2ccad6a83 fix for no data 2020-06-11 17:09:51 -07:00
Kelly Brazil
cad6dde4ac fix for no data 2020-06-10 17:54:06 -07:00
Kelly Brazil
06811c3539 add test for no data 2020-06-10 17:41:54 -07:00
Kelly Brazil
0cb23c2b21 add fix for no data 2020-06-10 17:40:18 -07:00
Kelly Brazil
ac4688dca2 add test for no data 2020-06-10 17:35:40 -07:00
Kelly Brazil
326c3b4670 add test for no data 2020-06-10 17:34:22 -07:00
Kelly Brazil
9b29d0c268 add test for no data 2020-06-10 17:32:39 -07:00
Kelly Brazil
e0013c3871 add test for no data 2020-06-10 17:31:14 -07:00
Kelly Brazil
a75744075b add no data test 2020-06-10 17:29:41 -07:00
Kelly Brazil
525aec1a02 fix for no data 2020-06-10 17:27:46 -07:00
Kelly Brazil
0bf9a7a072 add test for no data 2020-06-10 17:22:59 -07:00
Kelly Brazil
d8f2f4c95b fix for no data 2020-06-10 17:20:09 -07:00
Kelly Brazil
35d733b44f fix for no data 2020-06-10 17:10:53 -07:00
Kelly Brazil
9179b4175c add nodata tests 2020-06-10 16:40:11 -07:00
Kelly Brazil
bb07d78c78 add nodata fix 2020-06-10 16:39:49 -07:00
Kelly Brazil
07b179cd7f Merge pull request #67 from kellyjonbrazil/Dev
Dev v1.11.4
2020-06-10 06:07:42 -07:00
Kelly Brazil
054422d837 add test for empty directory 2020-06-10 06:04:50 -07:00
Kelly Brazil
3e052d1810 version bump 2020-06-10 05:53:20 -07:00
Kelly Brazil
c8e72805cf fix error on empty directory 2020-06-10 05:51:12 -07:00
Kelly Brazil
12a80e7db0 add fedora package info 2020-06-09 15:13:53 -07:00
Kelly Brazil
ee7ff9a09d Merge pull request #66 from kellyjonbrazil/dev
Dev v1.11.3
2020-06-09 11:22:39 -07:00
Kelly Brazil
f6478fb636 version bump 2020-06-09 11:18:47 -07:00
Kelly Brazil
811a0b0495 add info regarding the local parser plugin files 2020-06-08 10:54:42 -07:00
Kelly Brazil
aeb48edf72 use $LOCALAPPDATA variable for windows 2020-06-08 10:48:58 -07:00
Kelly Brazil
b1e94f0df7 heading formatting 2020-06-08 10:44:09 -07:00
Kelly Brazil
60050e3c0f fix linux/unix directory and add note about the XDG specification followed 2020-06-08 10:42:45 -07:00
Kelly Brazil
39ef09aa5b add local parser plugin feature 2020-06-07 13:30:22 -07:00
Kelly Brazil
8377d43116 formatting 2020-06-07 13:26:03 -07:00
Kelly Brazil
54e4c447ab clean up formatting 2020-06-07 12:52:16 -07:00
Kelly Brazil
937a9fa9cf vendorize appdirs module 2020-06-07 12:41:50 -07:00
Kelly Brazil
808ff6cf0e more acknowledgments updates 2020-06-07 12:29:10 -07:00
Kelly Brazil
7f5c649a95 update acknowledgments 2020-06-07 12:23:28 -07:00
Kelly Brazil
b72727dec9 update custom parsers info 2020-06-07 12:13:40 -07:00
Kelly Brazil
3fc88bfb33 Merge pull request #65 from duelafn/local-parsers
Load custom parsers from <user_data_dir>/jc/jcparsers
2020-06-07 12:04:44 -07:00
Dean Serenevy
9f2279d586 Load custom parsers from <user_data_dir>/jc/jcparsers 2020-06-06 14:42:27 -04:00
Kelly Brazil
346a14cb9b change osx_device to unix_device 2020-05-30 20:44:14 -07:00
Kelly Brazil
dac00d17ff add nixos test 2020-05-30 20:33:50 -07:00
Kelly Brazil
9ca7cd4060 update docs 2020-05-30 20:33:39 -07:00
Kelly Brazil
aa31628970 update docs 2020-05-30 20:33:00 -07:00
Kelly Brazil
bed694fcf5 version bump 2020-05-30 20:13:21 -07:00
Kelly Brazil
4b4af69fa1 fix date 2020-05-30 20:12:51 -07:00
Kelly Brazil
9d96190a5b Merge pull request #64 from kellyjonbrazil/dev
Dev v1.11.2
2020-05-30 20:08:32 -07:00
Kelly Brazil
fa44d48c09 freebsd fixes, tests, and fixtures 2020-05-30 19:50:38 -07:00
Kelly Brazil
4ef961c278 add freebsd test and fixtures 2020-05-30 19:18:01 -07:00
Kelly Brazil
292a837d5c add tests and fixtures for freebsd12 2020-05-30 18:54:09 -07:00
Kelly Brazil
aa7b915d84 version bump 2020-05-30 18:53:46 -07:00
Kelly Brazil
c46fe73236 add last fixes for freebsd 2020-05-30 18:53:35 -07:00
Kelly Brazil
039b2c129c freebsd fixes 2020-05-30 18:42:26 -07:00
Kelly Brazil
8f2e5e4808 fix compatible logic 2020-05-30 17:46:09 -07:00
Kelly Brazil
c4da8e4f78 add nixos and freebsd to tested. update new arp fields 2020-05-30 17:05:41 -07:00
Kelly Brazil
bcab9078a4 add w parser fix 2020-05-30 17:02:09 -07:00
Kelly Brazil
b3c6c1ea92 strip whitespace in string fields and add tests 2020-05-30 17:01:59 -07:00
Kelly Brazil
a3af8662bd add permanent field 2020-05-30 16:26:07 -07:00
Kelly Brazil
35940d0bc8 add freebsd permanent and expires fields 2020-05-30 16:25:53 -07:00
Kelly Brazil
26994cdcb7 add freebsd compatibility info 2020-05-30 15:51:54 -07:00
Kelly Brazil
017159a829 add freebsd nestat tests and fixtures 2020-05-30 15:51:06 -07:00
Kelly Brazil
b4e9c85e08 fixup -T freebsd output and add whitespace stripping to parse_post 2020-05-30 15:50:45 -07:00
Kelly Brazil
189146cd84 add more ints. remove whitespace strip code and move to freebsd_osx module 2020-05-30 15:50:07 -07:00
Kelly Brazil
af34153ffa version bump 2020-05-30 15:48:56 -07:00
Kelly Brazil
bf2ff3ffbb fix compatibility search for platform names that append the version number (e.g. freebsd12) 2020-05-30 15:48:29 -07:00
Kelly Brazil
6423c9efd6 integer and float updates 2020-05-29 15:48:51 -07:00
Kelly Brazil
58ab0d4ece strip whitespace from string fields 2020-05-29 15:14:44 -07:00
Kelly Brazil
83a738bf4d update fixtures for osx_flags and osx_inode name change to unix_flags and unix_inode 2020-05-29 14:16:11 -07:00
Kelly Brazil
3640671fc6 rename module 2020-05-29 14:15:28 -07:00
Kelly Brazil
1da623b30e add items 2020-05-29 14:14:48 -07:00
Kelly Brazil
b10ca64646 change osx_inode and osx_flags to unix_inode and unix_flags. Also rename netstat_osx module to netstat_freebsd_osx 2020-05-29 14:14:37 -07:00
Kelly Brazil
2128763ee6 fix osx version from 16.4 to 14.6 2020-05-29 13:25:19 -07:00
Kelly Brazil
a27e7ed39c test updates for added route_flags_pretty and flags_pretty fields 2020-05-29 13:24:52 -07:00
Kelly Brazil
f07b7eaa47 add flags_pretty 2020-05-29 12:55:16 -07:00
Kelly Brazil
6ce18de84c add route_flags_pretty 2020-05-29 12:51:04 -07:00
Kelly Brazil
8631b756e7 add freebsd test files 2020-05-29 12:05:46 -07:00
Kelly Brazil
7414d98412 add freebsd compatibility 2020-05-29 12:05:34 -07:00
Kelly Brazil
d7b19892e8 add freebsd support for netstat -i 2020-05-29 12:04:58 -07:00
Kelly Brazil
96df396eaf formatting 2020-05-29 07:35:25 -07:00
Kelly Brazil
2f6f640317 spelling 2020-05-27 17:11:20 -07:00
Kelly Brazil
c4a0a50f3a add nix-env 2020-05-27 17:08:54 -07:00
Kelly Brazil
658f8a3842 add zypper and ports info 2020-05-27 14:58:16 -07:00
Kelly Brazil
bfb876a1e3 formatting 2020-05-27 09:52:13 -07:00
Kelly Brazil
90c34b1f4e fix dmidecode example 2020-05-26 17:31:56 -07:00
Kelly Brazil
3f9164ea77 bold formatting 2020-05-23 21:11:17 -07:00
Kelly Brazil
7fd6fecbf5 formatting 2020-05-23 21:09:49 -07:00
Kelly Brazil
8029f72363 change osx_flags from integer to string 2020-05-23 21:09:40 -07:00
Kelly Brazil
c7fdce5d3b Merge pull request #62 from kellyjonbrazil/dev
Dev v1.11.0
2020-05-22 16:37:50 -07:00
Kelly Brazil
84f48aa369 version bump 2020-05-22 16:30:22 -07:00
Kelly Brazil
2e9a0a9c12 add features 2020-05-22 16:21:55 -07:00
Kelly Brazil
c1f6f2b950 osx fixes and tests 2020-05-22 16:21:40 -07:00
Kelly Brazil
ede21bca13 add OSX support for stat 2020-05-22 16:05:04 -07:00
Kelly Brazil
8dd9a9f9cb add netstat -i tests 2020-05-22 15:12:10 -07:00
Kelly Brazil
04f92cd133 add linux support for netstat -i 2020-05-22 14:04:11 -07:00
Kelly Brazil
8be8d2393b add netstat -i support for OSX 2020-05-22 13:38:25 -07:00
Kelly Brazil
0a879681be add netstat -r to docs 2020-05-22 12:56:27 -07:00
Kelly Brazil
2ca1587a49 add linux netstat -r tests 2020-05-22 12:44:51 -07:00
Kelly Brazil
ec2cd2d708 add netstat -r support for linux 2020-05-22 12:00:26 -07:00
Kelly Brazil
5d0dbece93 add netstat -r functionality for OSX 2020-05-22 11:09:41 -07:00
Kelly Brazil
df1e4b414b remove unused folder 2020-05-22 08:00:18 -07:00
Kelly Brazil
40760991e7 update copyright date 2020-05-22 07:51:47 -07:00
Kelly Brazil
464f5f86cf update description 2020-05-22 07:50:22 -07:00
Kelly Brazil
7b09e9fccd set empty values to Null and update fixtures 2020-05-21 17:01:17 -07:00
Kelly Brazil
6cba7d4298 remove linux from description 2020-05-21 11:10:00 -07:00
Kelly Brazil
9730f62e49 fixup name field and update test fixtures 2020-05-21 09:44:28 -07:00
Kelly Brazil
e0c1c87f54 formatting 2020-05-21 09:07:01 -07:00
Kelly Brazil
931b3d2b83 formatting 2020-05-20 19:56:28 -07:00
Kelly Brazil
e5d561baee add multipath condition for osx detection 2020-05-20 17:31:51 -07:00
Kelly Brazil
2867593e7a changelog update 2020-05-20 17:28:44 -07:00
Kelly Brazil
dd52fee563 osx netstat tests and fixtures 2020-05-20 17:25:25 -07:00
Kelly Brazil
8e1f885827 fix filtered netstat views 2020-05-20 16:43:53 -07:00
Kelly Brazil
2d39a58f90 doc update 2020-05-20 16:14:03 -07:00
Kelly Brazil
9c4fa2ae26 integer conversions and icmp fix 2020-05-20 15:39:47 -07:00
Kelly Brazil
de52d84e82 fix udp state and udp46 entries 2020-05-20 12:02:32 -07:00
Kelly Brazil
ce9b55059a organize files 2020-05-20 11:24:38 -07:00
Kelly Brazil
bcd370a6a0 code cleanup 2020-05-20 08:19:45 -07:00
Kelly Brazil
c8216850ab code cleanup 2020-05-20 07:19:24 -07:00
Kelly Brazil
f5feedb90b fix comments 2020-05-19 17:38:14 -07:00
Kelly Brazil
a4371cd187 support netstat -A 2020-05-19 17:36:16 -07:00
Kelly Brazil
9d5ba4c834 formatting 2020-05-19 17:14:04 -07:00
Kelly Brazil
1639dee1bb fix parse_post 2020-05-19 17:13:03 -07:00
Kelly Brazil
9363f430f2 use list extend method to simplify code 2020-05-19 16:26:41 -07:00
Kelly Brazil
9192a09073 parse all sections 2020-05-19 16:26:04 -07:00
Kelly Brazil
b915eb9755 initial osx parser 2020-05-19 15:15:08 -07:00
Kelly Brazil
1cfcc2b592 tighten up line test logic when counting tabs 2020-05-14 09:58:16 -07:00
Kelly Brazil
7138dd02b7 cleanup variables after adding to item 2020-05-14 09:51:10 -07:00
Kelly Brazil
b4276643b7 add dmidecode tests and fixtures 2020-05-14 09:43:13 -07:00
Kelly Brazil
2ef00763bf fix first item 2020-05-14 09:33:45 -07:00
Kelly Brazil
54364928fc fix oddities like hybrid single/multiline data and items containing multiple records 2020-05-14 08:57:23 -07:00
Kelly Brazil
09b3b4932b add dmidecode output fixtures 2020-05-13 10:51:38 -07:00
Kelly Brazil
29d6670119 convert integers 2020-05-13 10:18:49 -07:00
Kelly Brazil
2f654b5f1a doc update 2020-05-13 10:13:27 -07:00
Kelly Brazil
e53b9f5992 add caveats to documentation 2020-05-13 10:12:40 -07:00
Kelly Brazil
addb234e61 add dmidecode doc 2020-05-13 09:44:20 -07:00
Kelly Brazil
76eca3b659 add dmidecode 2020-05-13 09:43:35 -07:00
Kelly Brazil
f90dec4c0e add examples to documentation 2020-05-13 09:43:23 -07:00
Kelly Brazil
8900a59d4c simplify logic by removing redundant block 2020-05-13 09:31:12 -07:00
Kelly Brazil
6685138200 fix for missing multi-line values that come immediately after a previous multi-line value 2020-05-13 09:25:02 -07:00
Kelly Brazil
4d3e65b980 fix missing values 2020-05-13 09:00:32 -07:00
Kelly Brazil
e9282bb546 add dmidecode parser 2020-05-13 08:22:52 -07:00
Kelly Brazil
f5627a4594 version bump 2020-05-11 11:03:02 -07:00
Kelly Brazil
81ffdb2510 remove shebang for Fedora packaging 2020-05-11 10:54:26 -07:00
Kelly Brazil
4c00a99850 version bump 2020-05-11 10:37:14 -07:00
Kelly Brazil
2bfcb45b28 make cli.py executable 2020-05-11 10:32:42 -07:00
Kelly Brazil
ab0c10e791 remove execute permissions 2020-05-11 10:27:16 -07:00
Kelly Brazil
2c1935115d Merge pull request #61 from kellyjonbrazil/dev
Dev v1.10.10
2020-05-09 11:47:05 -07:00
Kelly Brazil
d98e43dc78 add netstat item 2020-05-09 11:42:56 -07:00
Kelly Brazil
9348988d64 add netstat test for Fedora32 2020-05-09 11:41:30 -07:00
Kelly Brazil
1285c66467 netstat with bluetooth section 2020-05-09 11:36:23 -07:00
Kelly Brazil
b7191bbc13 handle bluetooth section (ignore for now) 2020-05-09 11:36:03 -07:00
Kelly Brazil
98b97509f7 version bump 2020-05-09 11:25:13 -07:00
Kelly Brazil
2b2b570490 add tests for 'gone - no logout' 2020-05-09 11:22:26 -07:00
Kelly Brazil
cce2d1ff29 add condition for 'gone - no logout' 2020-05-09 11:22:01 -07:00
Kelly Brazil
b79600c572 version bump 2020-05-09 11:01:48 -07:00
Kelly Brazil
140f1a8543 test fixes for issue #60 2020-05-09 11:00:04 -07:00
Kelly Brazil
e34657cfde fix issue #60 that was skipping the first file in some instances using -R without -l 2020-05-08 15:26:11 -07:00
Kelly Brazil
99070fa607 version bump 2020-05-08 10:51:13 -07:00
Kelly Brazil
2b46785b1f add MIT license to vendorized IfconfigParser class 2020-05-08 10:49:30 -07:00
Kelly Brazil
c72562524b fully remove tests from packaging 2020-05-08 09:30:31 -07:00
Kelly Brazil
b7dd6441c7 version bump 2020-05-08 08:20:33 -07:00
Kelly Brazil
31fcc2f755 remove manifest.in - no longer needed due to removing tests 2020-05-08 08:20:21 -07:00
Kelly Brazil
b391aa14bc add license_file to metadata 2020-05-08 08:19:51 -07:00
Kelly Brazil
d3c45debbb remove tests and add license file 2020-05-08 08:19:31 -07:00
Kelly Brazil
5b08469b87 Merge pull request #57 from kellyjonbrazil/dev
Dev v1.10.7
2020-05-01 15:55:50 -07:00
Kelly Brazil
4a77ec63a4 add IfconfigParser class 2020-05-01 14:57:50 -07:00
Kelly Brazil
d13606b6dc modify dependencies for easier packaging into Fedora 2020-05-01 14:37:23 -07:00
Kelly Brazil
05291c93bb vendorize ifconfig-parser module for easier packaging in Fedora 2020-05-01 14:36:54 -07:00
Kelly Brazil
8cf00a208e change text to strings 2020-04-29 15:57:55 -07:00
Kelly Brazil
06d73c8876 formatting 2020-04-23 07:06:44 -07:00
Kelly Brazil
649c646ea2 add brew install option 2020-04-22 16:08:34 -07:00
Kelly Brazil
b7756d9250 version bump 2020-04-20 16:33:26 -07:00
Kelly Brazil
1cd2cd954c remove references to homebrew/shim to allow tests to pass in homebrew packaging ci/cd 2020-04-20 16:31:22 -07:00
Kelly Brazil
72020b8da9 move packages info to jc-packages github page 2020-04-17 10:20:25 -07:00
Kelly Brazil
cf9720b749 update install info 2020-04-16 14:03:31 -07:00
Kelly Brazil
967b9db7f9 spelling 2020-04-15 21:27:22 -07:00
Kelly Brazil
bb3acb1182 formatting 2020-04-15 21:25:06 -07:00
Kelly Brazil
560c7f7e6d formatting 2020-04-15 21:23:55 -07:00
Kelly Brazil
79b2841764 add new binary package install info 2020-04-15 21:22:43 -07:00
Kelly Brazil
a06a89cbd1 version bump 2020-04-14 11:15:24 -07:00
Kelly Brazil
431bd969eb use sys.exit(0) instead of exit() 2020-04-14 11:10:31 -07:00
Kelly Brazil
c87b722aec spelling 2020-04-12 13:23:58 -07:00
Kelly Brazil
3688b8b014 Merge pull request #56 from kellyjonbrazil/dev
Dev v1.10.4
2020-04-12 13:21:38 -07:00
Kelly Brazil
07b8d9e0c0 version bump 2020-04-12 13:18:28 -07:00
Kelly Brazil
7454b53e39 formatting 2020-04-12 13:13:28 -07:00
Kelly Brazil
3d6a76024d update with JC_COLORS info 2020-04-12 13:10:57 -07:00
Kelly Brazil
421b980957 JC_COLORS working 2020-04-12 13:03:09 -07:00
Kelly Brazil
4a22e27d6a add set_env_colors function 2020-04-12 12:43:51 -07:00
Kelly Brazil
99f7842dee fix brek on pipe error 2020-04-09 13:38:33 -07:00
Kelly Brazil
7f869b4b18 change colors to ansi and match jello style 2020-04-09 07:31:21 -07:00
Kelly Brazil
9665f4ee84 add pypi badge 2020-04-07 08:44:15 -07:00
Kelly Brazil
606904d48b Merge pull request #53 from kellyjonbrazil/dev
Dev v1.10.1
2020-04-04 17:28:21 -07:00
Kelly Brazil
3f5279b97c version bump to 1.10.1 2020-04-04 17:25:55 -07:00
Kelly Brazil
f5ec21e6ac use in instead of find() 2020-04-04 17:19:32 -07:00
Kelly Brazil
578a284465 use in instead of find() 2020-04-04 17:18:39 -07:00
Kelly Brazil
422e392d9d use in instead of find() 2020-04-04 17:17:43 -07:00
Kelly Brazil
54dfffd34a use in instead of find() 2020-04-04 17:16:25 -07:00
Kelly Brazil
cffba64d2b use in and startswith() instead of find() 2020-04-04 17:15:03 -07:00
Kelly Brazil
56a0c12a59 use in instead of find() 2020-04-04 17:12:22 -07:00
Kelly Brazil
c174d3de18 use in and startswith() instead of find() 2020-04-04 17:10:46 -07:00
Kelly Brazil
a9c59ef9fc fix logic to not for ' type ' in cleandata[0] 2020-04-04 17:07:38 -07:00
Kelly Brazil
abdb9b2673 use in instead of find() 2020-04-04 17:05:17 -07:00
Kelly Brazil
548aaab626 remove old commented code 2020-04-04 17:04:20 -07:00
Kelly Brazil
20571c87ae us in instead of find() 2020-04-04 17:01:56 -07:00
Kelly Brazil
19e49200de version bump 2020-04-04 16:59:48 -07:00
Kelly Brazil
d32f5c67a9 use startswith() instead of find() 2020-04-04 16:59:03 -07:00
Kelly Brazil
b83b626435 use startswith() instead of find() 2020-04-04 16:57:23 -07:00
Kelly Brazil
ab2c1b25ec use startswith() and in instead of find() 2020-04-04 16:56:11 -07:00
Kelly Brazil
f2d46313a4 use startswith() instead of find() 2020-04-04 16:53:55 -07:00
Kelly Brazil
87e4796a6c use in instead of .find() 2020-04-04 16:52:45 -07:00
Kelly Brazil
0014a5c2f4 us startswith() and in instead of .find() 2020-04-04 16:51:36 -07:00
Kelly Brazil
7af56e0dad use startswith() and in instead of find() 2020-04-04 16:50:05 -07:00
Kelly Brazil
a5ae6e3c01 use startswith() instead of find() 2020-04-04 16:48:16 -07:00
Kelly Brazil
fe1a0d1faf use in instead of .find() 2020-04-04 16:46:09 -07:00
Kelly Brazil
302f05cdda prettify style block 2020-04-03 14:50:20 -07:00
Kelly Brazil
c0044be7b0 rename color grey to gray 2020-04-03 14:48:19 -07:00
Kelly Brazil
0110078807 update badge 2020-04-03 14:47:08 -07:00
Kelly Brazil
42eacb45f8 rename to Tests 2020-04-03 14:45:04 -07:00
Kelly Brazil
a43e2e1991 add -m option info 2020-04-02 17:40:06 -07:00
Kelly Brazil
c8b721d4f6 version bump to 1.10.0 2020-04-02 17:35:35 -07:00
Kelly Brazil
d0bfddc3d9 add color and -m monochrome option 2020-04-02 17:29:25 -07:00
Kelly Brazil
6b925a16c8 add tests badge 2020-04-02 10:55:32 -07:00
Kelly Brazil
89ebd9fc22 add axfr info to schema 2020-03-27 07:31:08 -07:00
Kelly Brazil
6b4ba66231 Merge pull request #52 from kellyjonbrazil/dev
Dev v1.9.3
2020-03-26 17:15:13 -07:00
Kelly Brazil
5b697dc381 add more dig tests 2020-03-26 17:09:29 -07:00
Kelly Brazil
9ba73c95d1 add tests 2020-03-26 16:45:29 -07:00
Kelly Brazil
93aa390447 version bump to v1.9.3 2020-03-26 16:30:17 -07:00
Kelly Brazil
3cfb8945dd use startswith() instead of find 2020-03-26 16:29:59 -07:00
Kelly Brazil
cd8d38f2a1 add axfr support 2020-03-26 16:22:53 -07:00
Kelly Brazil
8ec8cd6294 Merge pull request #50 from kellyjonbrazil/dev
Dev v1.9.2
2020-03-12 17:09:04 -07:00
Kelly Brazil
c028113561 version bump to v1.9.2 2020-03-12 17:06:55 -07:00
Kelly Brazil
5f22e1c803 fix and test for osx arp entries without ifscope 2020-03-12 17:02:26 -07:00
Kelly Brazil
d3351787e5 change osx detection 2020-03-12 16:52:33 -07:00
Kelly Brazil
e5bea9ae3b version bump 2020-03-12 08:52:01 -07:00
Kelly Brazil
93c710abe9 Merge pull request #49 from kellyjonbrazil/dev
Dev v1.9.1
2020-03-12 08:23:10 -07:00
Kelly Brazil
c29e7cfe5c version bump to 1.9.1 2020-03-12 08:17:28 -07:00
Kelly Brazil
cb5c1ba00d add tests for fix to make the file parser splitting more robust 2020-03-11 17:04:48 -07:00
Kelly Brazil
9a012b94e1 make splitting more robust 2020-03-11 15:40:34 -07:00
Kelly Brazil
400f5a44ec Merge pull request #48 from kellyjonbrazil/dev
Dev v1.9.0
2020-03-11 13:37:22 -07:00
Kelly Brazil
a2ab5bab91 version bump to v1.9.0 2020-03-11 13:32:58 -07:00
Kelly Brazil
fc8ab27361 bugfix for misaligned columns and additional test for ntpq #31 2020-03-11 13:24:55 -07:00
Kelly Brazil
59f19d33a5 add file command tests for #41 2020-03-11 12:39:59 -07:00
Kelly Brazil
dfc9618115 add file parser for issue #41 2020-03-11 12:20:58 -07:00
Kelly Brazil
8e02e5c75a fix issue with getting options with some commands #47 2020-03-11 09:21:14 -07:00
Kelly Brazil
970493ab93 add magic commands 2020-03-11 06:22:54 -07:00
Kelly Brazil
64d78956eb update acknowledgment 2020-03-10 22:18:26 -07:00
Kelly Brazil
40c05346f4 re-adding optimizations from https://github.com/philippeitis 2020-03-10 22:16:40 -07:00
Kelly Brazil
e9b0bc1409 doc update 2020-03-10 22:03:54 -07:00
Kelly Brazil
798e6bb7d9 tests passing for airport -s. issue #46 2020-03-10 22:03:44 -07:00
Kelly Brazil
12a370deed add airport -s parser for issue #46 2020-03-10 21:51:02 -07:00
Kelly Brazil
553bfbe1a0 tests passing for airport -I. Issue #46 2020-03-10 21:02:17 -07:00
Kelly Brazil
52494321fc fixes and docs for airport parser issue #46 2020-03-10 20:55:07 -07:00
Kelly Brazil
c6c9e06496 added airport command parser 2020-03-10 20:35:52 -07:00
Kelly Brazil
e3a6c05a58 timedatectl fixes, tests, and fixtures for issue #42 2020-03-10 20:26:53 -07:00
Kelly Brazil
391d06f68d change selection_state to state 2020-03-10 20:16:41 -07:00
Kelly Brazil
99804ea06e added timedatectl status parser 2020-03-10 18:37:55 -07:00
Kelly Brazil
51935deb2a timedatectl test fixtures 2020-03-10 18:00:47 -07:00
Kelly Brazil
b24d0c3a47 ntpq docs 2020-03-10 18:00:26 -07:00
Kelly Brazil
762a886d6f add ntpq tests 2020-03-10 15:17:25 -07:00
Kelly Brazil
2c3e9ddfe4 add ntpq parser for issue #31 2020-03-10 14:18:55 -07:00
Kelly Brazil
c7cd2b63c8 delete unused test file 2020-03-09 11:46:17 -07:00
Kelly Brazil
f0528ea831 Merge pull request #45 from kellyjonbrazil/dev
Dev v1.8.1
2020-03-08 14:54:13 -07:00
Kelly Brazil
5bc5596f60 version bump to 1.8.1 2020-03-08 14:49:23 -07:00
Kelly Brazil
2c27ac46be add ls test fixtures 2020-03-08 14:43:51 -07:00
Kelly Brazil
caad840153 Merge pull request #44 from philippeitis/patch-5
Move core magic() logic into seperate function for testability, minor tweaks
2020-03-08 14:19:37 -07:00
philippeitis
65bd7e2904 Merge pull request #1 from kellyjonbrazil/pr/44
Merge changes
2020-03-08 14:10:35 -07:00
Kelly Brazil
c3d7d7db12 removed whitespace 2020-03-08 14:03:08 -07:00
Kelly Brazil
5605310362 added tests, removed os import, changed to 'assertEqual' 2020-03-08 14:02:54 -07:00
philippeitis
17b6efe82e Create basic tests for generate_magic_commands() 2020-03-08 13:35:01 -07:00
philippeitis
a032ae56ae Pass args to generate_magic_command() to allow testing. 2020-03-08 13:26:15 -07:00
philippeitis
eab2f4b056 Move core magic() logic into seperate function for testability, minor tweaks.
We only care about the command when testing magic() - by moving that out, we can easily test the command. I modified the code to return a boolean signalling that the command is valid, and the command itself to maintain the original functionality.

I also made some small tweaks (removed a list() call, fixed a possible bug with no arguments., moved magic_dict instantiation past the fast path checks to avoid making a dict if not needed.)
2020-03-08 13:20:38 -07:00
Kelly Brazil
aff86ae6c7 reimpliment magic() based on the dictionary approach suggested by philippeitis 2020-03-08 12:58:26 -07:00
Kelly Brazil
7ece9ddc1a version bump ls 2020-03-07 17:26:42 -08:00
Kelly Brazil
7cd048e839 changelog update 2020-03-07 17:25:10 -08:00
Kelly Brazil
1e22f610a3 fix for osx - doesn't print 'total xx' line if empty directory (issue #40) 2020-03-07 17:22:08 -08:00
Kelly Brazil
5249c972ae add to changelog 2020-03-06 12:09:20 -08:00
Kelly Brazil
fd45f856a0 import jc.utils instead of jc 2020-03-06 12:09:09 -08:00
Kelly Brazil
c8ab40cd33 ignore .github folder 2020-03-05 09:19:58 -08:00
Kelly Brazil
b2c872925b add utf-8 encoding for testing on Windows 2020-03-04 19:47:03 -08:00
Kelly Brazil
f48e229202 utf-8 open for windows tests 2020-03-04 19:40:32 -08:00
Kelly Brazil
799fec92c3 utf-8 for windows support 2020-03-04 19:33:45 -08:00
Kelly Brazil
87a41c2fca add utf-8 to open function 2020-03-04 19:30:30 -08:00
Kelly Brazil
7f85de0c46 add windows-latest 2020-03-04 19:28:21 -08:00
Kelly Brazil
13661b1993 Merge pull request #37 from philippeitis/continuous_integration
Enable Continuous Integration with GitHub Actions.
2020-03-04 16:54:26 -08:00
philippeitis
51d5c3892d Remove Windows tests, due to lack of support. 2020-03-04 16:21:06 -08:00
philippeitis
e4eab4641a Change line in blkid.py to trigger CI 2020-03-04 16:17:58 -08:00
philippeitis
9b148e0ba3 Add requirements.txt 2020-03-04 16:16:19 -08:00
philippeitis
de28932650 Consolidate dictionary into creation, trigger CI 2020-03-04 16:14:03 -08:00
Kelly Brazil
5f798d603e version bump and ack to philippeitis 2020-03-04 16:11:14 -08:00
Kelly Brazil
a0757b2dd3 optimize line parsing 2020-03-04 16:07:53 -08:00
philippeitis
498d51b4e8 Enable Continuous Integration with GitHub Actions.
This automatically runs unit tests on various operating systems and Python versions when Python files are modified to ensure that functionality remains correct.
2020-03-04 16:07:32 -08:00
Kelly Brazil
b06b6bae3f Merge pull request #36 from philippeitis/patch-3
Simplify process() in history.py, avoid list allocation in parse()
2020-03-04 16:04:05 -08:00
Kelly Brazil
b5eaff2137 Merge pull request #35 from kellyjonbrazil/revert-34-patch-3
Revert "Simplify process() in history.py, avoid list allocation in parse()"
2020-03-04 15:33:13 -08:00
Kelly Brazil
c01bcd3734 Revert "Simplify process() in history.py, avoid list allocation in parse()" 2020-03-04 15:32:23 -08:00
Kelly Brazil
d75c4068ca Merge pull request #34 from philippeitis/patch-3
Simplify process() in history.py, avoid list allocation in parse()
2020-03-04 15:31:55 -08:00
Kelly Brazil
6aa2d5a3d2 Merge pull request #33 from philippeitis/patch-2
Handle case where only options are passed.
2020-03-04 15:28:09 -08:00
philippeitis
065276805f Simplify process() in history.py, avoid list allocation in parse() 2020-03-04 13:35:31 -08:00
philippeitis
a63408c8cf Handle case where only options are passed. 2020-03-04 13:16:35 -08:00
Kelly Brazil
69576f6bfa minor sytax fixes 2020-03-04 12:03:40 -08:00
Kelly Brazil
19845624e2 Merge pull request #32 from philippeitis/patch-1
Simplify main(), magic() methods.
2020-03-04 11:59:29 -08:00
philippeitis
22ff2964e9 Simplify main(), magic() methods. 2020-03-04 10:33:42 -08:00
Kelly Brazil
d96b3a65a9 formatting 2020-03-04 08:30:52 -08:00
Kelly Brazil
4989445ef4 Merge pull request #30 from kellyjonbrazil/dev
Dev v1.8.0
2020-03-03 11:46:09 -08:00
Kelly Brazil
6770892acd add release notes link 2020-03-03 11:37:59 -08:00
Kelly Brazil
d4eba8740f release date 3/3 2020-03-03 11:08:52 -08:00
Kelly Brazil
9f60760560 add group and gshadow tests 2020-03-03 10:54:27 -08:00
Kelly Brazil
0a8f8ac934 add group and gshadow parsers 2020-03-03 09:55:43 -08:00
Kelly Brazil
6ae24c8244 add group and gshadow test fixtures 2020-03-03 09:55:17 -08:00
Kelly Brazil
d3679082a8 add group and gshadow parsers 2020-03-03 09:36:16 -08:00
Kelly Brazil
fb08b42dca change 'group_list' to 'members' 2020-03-03 09:32:56 -08:00
Kelly Brazil
4aeaa9f42a add /etc/gshadow parser 2020-03-03 09:32:25 -08:00
Kelly Brazil
5f5693da04 spelling fix 2020-03-03 09:07:28 -08:00
Kelly Brazil
5eb0f61727 add /etc/group file parser 2020-03-03 09:07:09 -08:00
Kelly Brazil
958e998991 formatting 2020-03-02 17:15:05 -08:00
Kelly Brazil
b78c1509f6 try/except dialect detection 2020-03-02 15:06:25 -08:00
Kelly Brazil
ce184d4d57 add csv parser tests 2020-03-02 15:05:56 -08:00
Kelly Brazil
b4c3714ced removed [OPTIONS] PARSER syntax. still works but prefer the PARSER [OPTIONS] syntax for better performance 2020-03-02 14:10:15 -08:00
Kelly Brazil
5b7dfa0438 formatting 2020-03-02 14:07:56 -08:00
Kelly Brazil
391a388476 doc update 2020-03-02 14:07:29 -08:00
Kelly Brazil
d9c4e2ed4c add csv file parser 2020-03-02 14:03:58 -08:00
Kelly Brazil
0c42db38b1 doc update 2020-03-02 10:30:12 -08:00
Kelly Brazil
2f9be8bf33 simplify usage 2020-03-02 08:32:42 -08:00
Kelly Brazil
e8c00155e8 add -b to warning message 2020-03-02 07:43:45 -08:00
Kelly Brazil
cc88fdd9ee update example 2020-03-01 21:17:50 -08:00
Kelly Brazil
d9de11ef1d add another who example 2020-03-01 21:16:57 -08:00
Kelly Brazil
0ceda97d09 who parser tests 2020-03-01 19:03:27 -08:00
Kelly Brazil
d0dec92ba8 add who test fixtures 2020-03-01 18:57:51 -08:00
Kelly Brazil
d420c008d8 fix for pts lines with no user info 2020-03-01 17:52:14 -08:00
Kelly Brazil
f0b32db433 who doc update 2020-03-01 17:39:02 -08:00
Kelly Brazil
bc838eda59 fix output for non-extended 2020-03-01 17:38:51 -08:00
Kelly Brazil
afe55b6af0 add who parser 2020-03-01 17:07:28 -08:00
Kelly Brazil
dd3a3ac302 doc update and process pid integers 2020-03-01 17:04:06 -08:00
Kelly Brazil
f9982a7947 fixes for from and comment fields 2020-03-01 16:49:52 -08:00
Kelly Brazil
07c1be9e9a add who command parser 2020-03-01 16:30:04 -08:00
Kelly Brazil
f832b88755 add passwd and shadow tests 2020-03-01 10:17:47 -08:00
Kelly Brazil
0fac757efc add passwd and shadow parsers 2020-02-29 12:25:41 -08:00
Kelly Brazil
fc15742065 passwd and shadow test fixtures 2020-02-29 12:25:22 -08:00
Kelly Brazil
6f2466a131 update readme with /etc/passwd and /etc/shadow file parsers 2020-02-29 11:56:12 -08:00
Kelly Brazil
4b90e22f0a doc update 2020-02-29 11:50:46 -08:00
Kelly Brazil
c493568785 doc fix 2020-02-29 11:46:38 -08:00
Kelly Brazil
1cdf004b77 add /etc/shadow parser 2020-02-29 11:46:24 -08:00
Kelly Brazil
a4ea504261 add /etc/passwd parser 2020-02-29 11:33:14 -08:00
Kelly Brazil
4c2c234c3b add last and lastb tests 2020-02-28 15:15:24 -08:00
Kelly Brazil
3d4c0f3e89 add blkid tests 2020-02-28 14:50:29 -08:00
Kelly Brazil
52fad02903 doc update 2020-02-28 13:31:38 -08:00
Kelly Brazil
9dcabc057c support multi device udev output 2020-02-28 10:57:14 -08:00
Kelly Brazil
db8c1079dd use maxsplit=1 in case there are multiple '=' delimiters 2020-02-28 09:54:07 -08:00
Kelly Brazil
8f954673ab use shlex split for values within quotations that have spaces 2020-02-28 09:07:36 -08:00
Kelly Brazil
79522d1c7d doc fixes 2020-02-28 08:51:48 -08:00
Kelly Brazil
a18bf03079 use raw strings for regular expressions 2020-02-28 08:50:35 -08:00
Kelly Brazil
c02b6b5d82 doc updates 2020-02-27 21:21:19 -08:00
Kelly Brazil
f99b423284 doc update 2020-02-27 21:04:24 -08:00
Kelly Brazil
d7d9d45d4f add missing comma 2020-02-27 20:59:09 -08:00
Kelly Brazil
90065ec0cd add more integers 2020-02-27 20:57:22 -08:00
Kelly Brazil
51157ebb86 another devname fix 2020-02-27 20:49:14 -08:00
Kelly Brazil
96d95c79ca devname fix 2020-02-27 20:47:07 -08:00
Kelly Brazil
e5da34c233 check if devname key exists before renaming 2020-02-27 20:41:06 -08:00
Kelly Brazil
f09d657f77 rename devname to device 2020-02-27 20:36:19 -08:00
Kelly Brazil
0f4b0189f5 process integer values 2020-02-27 20:31:17 -08:00
Kelly Brazil
4666042abb add blkid parser 2020-02-27 20:21:02 -08:00
Kelly Brazil
027d544c2b add last and lastb parser 2020-02-27 16:08:56 -08:00
Kelly Brazil
f1967d0138 system_boot fix 2020-02-27 16:08:39 -08:00
Kelly Brazil
c1d896027d fix system_boot tty 2020-02-27 15:58:12 -08:00
Kelly Brazil
5c2d2a6618 process function and docs 2020-02-27 15:44:36 -08:00
Kelly Brazil
997b269b0b btmp fix 2020-02-27 15:26:09 -08:00
Kelly Brazil
61257e7525 add last and lastb parser 2020-02-27 15:14:43 -08:00
Kelly Brazil
53ee2c3631 Merge pull request #29 from kellyjonbrazil/dev
Dev v1.7.5
2020-02-27 10:59:14 -08:00
Kelly Brazil
8bfa0bddec version bump to 1.7.5 2020-02-27 10:50:05 -08:00
Kelly Brazil
ad61e6bc81 add ls tests for filenames with newline characters 2020-02-27 10:48:09 -08:00
Kelly Brazil
873b5ba8ac move examples to bottom 2020-02-27 09:36:57 -08:00
Kelly Brazil
6ae50054e2 readme update 2020-02-24 20:30:44 -08:00
Kelly Brazil
22a35f41bf move variables to top 2020-02-24 17:50:56 -08:00
Kelly Brazil
961696c963 add a warning if newlines are detected in naked ls 2020-02-24 17:47:31 -08:00
Kelly Brazil
c7b7f1a5dc fix for files with newlines in naked ls 2020-02-24 17:24:56 -08:00
Kelly Brazil
b5a0d650b1 ls output with newlines 2020-02-24 17:01:33 -08:00
Kelly Brazil
573b279464 fixup for filenames that start with a newline character 2020-02-24 17:01:12 -08:00
Kelly Brazil
116e07f161 fixes for multiple consecutive newlines and trailing newlines in filenames 2020-02-24 16:38:29 -08:00
Kelly Brazil
964868c8af add support for newlines in filenames (only with ls -l) 2020-02-24 15:19:43 -08:00
Kelly Brazil
c8dac32df8 readme update 2020-02-24 13:05:35 -08:00
Kelly Brazil
72a0016bd8 use link to anchor for Parsers 2020-02-20 15:38:45 -08:00
Kelly Brazil
2ad3167434 update doc url 2020-02-19 07:12:43 -08:00
618 changed files with 93419 additions and 3204 deletions

31
.github/workflows/pythonapp.yml vendored Normal file
View File

@@ -0,0 +1,31 @@
name: Tests
on:
push:
paths:
- "**/*.py"
pull_request:
paths:
- "**/*.py"
jobs:
build:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [macos-latest, ubuntu-latest, windows-latest]
python-version: [3.6, 3.7, 3.8]
steps:
- uses: actions/checkout@v2
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v1
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
- name: Test with unittest
run: |
python -m unittest discover tests

2
.gitignore vendored
View File

@@ -3,4 +3,4 @@ __pycache__
dist/
build/
*.egg-info/
jc/parsers.old/
.github/

280
CHANGELOG Normal file
View File

@@ -0,0 +1,280 @@
jc changelog
20200805 v1.13.4
- Update crontab and crontab-u parsers to tighten up variable detection
- Update ping parser to tighten linux/bsd detection
20200804 v1.13.3
- Update ping parser for Raspberry Pi compatibility
20200803 v1.13.2
- Add key/value file parser (wrapper for ini parser)
- Add date command parser
- Update traceroute parser to more gracefully handle missing header row
- Update traceroute parser to handle annotations
- Update traceroute parser to only return successful probes
20200727 v1.13.1
- Add route -6 tests
20200727 v1.13.0
- Add ping and ping6 command parser tested on linux, macos, and freebsd
- Add traceroute and traceroute6 command parser tested on linux, macos, and freebsd
- Add tracepath command parser tested on linux
- Update ini parser to support files only containing key/value pairs
- Update uname parser exception with a hint to use "uname -a"
- Update route parser to support IPv6 tables
20200711 v1.12.1
- Fix tests when using older version of pygments library
20200710 v1.12.0
- Add sysctl command parser tested on linux, macOS, and freebsd
- Update the cli code to allow older versions of the pygments library (2.3.0) for debian packaging
- Code cleanup on the cli
- Add tests for the cli
- Vendorize cgitb as tracebackplus for verbose debug messages
20200625 v1.11.8
- Add verbose debug option using -dd argument
20200622 v1.11.7
- Fix iptables parser issue which would not output the last chain
20200614 v1.11.6
- Improve and standardize empty data check for all parsers
20200612 v1.11.5
- Update airport_s parser to fix error on parsing empty data
- Update arp parser to fix error on parsing empty data
- Update blkid parser to fix error on parsing empty data
- Update crontab parser to fix error on parsing empty data
- Update crontab_u parser to fix error on parsing empty data
- Update df parser to fix error on parsing empty data
- Update free parser to fix error on parsing empty data
- Update lsblk parser to fix error on parsing empty data
- Update lsmod parser to fix error on parsing empty data
- Update mount parser to fix error on parsing empty data
- Update netstat parser to fix error on parsing empty data
- Update ntpq parser to fix error on parsing empty data
- Update ps parser to fix error on parsing empty data
- Update route parser to fix error on parsing empty data
- Update systemctl parser to fix error on parsing empty data
- Update systemctl_lj parser to fix error on parsing empty data
- Update systemctl_ls parser to fix error on parsing empty data
- Update systemctl_luf parser to fix error on parsing empty data
- Update uptime parser to fix error on parsing empty data
- Update w parser to fix error on parsing empty data
- Update xml parser to fix error on parsing empty data
- Add tests to all parsers for no data condition
- Update ss parser to fix integer fields
20200610 v1.11.4
- Update ls parser to fix error on parsing an empty directory
20200609 v1.11.3
- Add local parser plugin feature (contributed by Dean Serenevy)
20200530 v1.11.2
- Update netstat parser to add freebsd support
- Update netstat parser to add route_flags_pretty field
- Update netstat parser to change osx_inode field name to unix_inode
- Update netstat parser to change osx_flags field name to unix_flags
- Update netstat parser to strip whitespace from state field
- Update route parser to add flags_pretty field
- Update arp parser to add permanent field (freebsd and osx)
- Update arp parser to add expires field (freebsd)
- Update w parser to strip whitespace from what field
- Update last parser to fix FreeBSD issues
- Update stat parser to change osx_flags field name to unix_flags
- Update stat parser to add unix_device field for freebsd and osx
- Fix freebsd compatibility message for df, fstab, mount, ntpq, stat, and uname parsers
- Fix compatibility message for platforms that include the version number at the end (e.g. freebsd12)
20200523 v1.11.1
- Update stat command parser to change osx_flags field to string
20200522 v1.11.0
- Add dmidecode command parser
- Update stat command parser to add OSX support
- Update netstat command parser to add OSX support
- Update netstat command parser to add -r (route) functionality for linux and OSX
- Update netstat command parser to add -i (interface) functionality for linux and OSX
20200511 v1.10.12
- Remove shebang from jc/cli.py for Fedora packaging
20200511 v1.10.11
- Change file permissions for Fedora packaging
20200509 v1.10.10
- Fix ls parser issue where the first file was skipped for ls -R on some platforms
- Update last parser to handle 'gone - no logout' condition
- Update netstat parser to handle bluetooth section (ignore gracefully for now)
20200508 v1.10.9
- Add license info to vendorized ifconfig-parser class
20200508 v1.10.8
- Add license file to dist for Fedora RPM packaging requirements
- Remove tests from package to keep from polluting the global site-packages
20200501 v1.10.7
- Requirements modifications for Fedora RPM packaging requirements
20200420 v1.10.6
- Remove homebrew shim references from du osx tests
20200414 v1.10.5
- Minor change of using sys.exit(0) instead of exit()
20200412 v1.10.4
- Add color customization via JC_COLORS env variable
20200409 v1.10.3
- Fix break on pipe error
20200409 v1.10.2
- Change colors to ansi and match jello colors
20200402 v1.10.1
- Code cleanup
20200402 v1.10.0
- Add color output by default when not piping data to another program
- Add -m option for monochrome output
20200326 v1.9.3
- Add axfr support for dig command parser
20200312 v1.9.2
- Updated arp parser to fix OSX detection for some edge cases
20200312 v1.9.1
- Updated file command parser to make filename splitting more robust
20200311 v1.9.0
- Added ntpq command parser
- Added timedatectl status command parser
- Added airport -I and airport -s command parser
- Added file command parser
- Optimized history command parser by https://github.com/philippeitis
- Magic syntax fix for certain edge cases
20200308 v1.8.1
- CLI optimizations by https://github.com/philippeitis
- Refactored magic syntax function and added tests (https://github.com/philippeitis)
- Github actions for CI testing on multiple platforms by https://github.com/philippeitis
- Updated ls parser to fix parsing error in OSX with -lR when there are empty folders
20200303 v1.8.0
- Added blkid command parser
- Added last and lastb command parser
- Added who command parser
- Added CSV file parser
- Added /etc/passwd file parser
- Added /etc/shadow file parser
- Added /etc/group file parser
- Added /etc/gshadow file parser
20200227 v1.7.5
- Updated ls parser to support filenames with newline characters
20200219 v1.7.4
- Updated ls parser to support multiple directories, globbing, and -R (recursive)
20200211 v1.7.3
- Add alternative 'magic' syntax: e.g. `jc ls -al`
- Options can now be condensed (e.g. -prq is equivalant to -p -r -q)
20200208 v1.7.2
- Include test fixtures in wheel and sdist
20200205 v1.7.1
- Add YAML file parser
- Add INI file parser
- Add XML file parser
- Add id parser (tested on linux and OSX)
- Add crontab file parser with user support (tested on linux)
- Add __version__ variable to parser modules
- Add exit code on error
- Updated history parser to output "line" as an integer
- Updated compatibility list for some parsers
- Bugfix in crontab file parser: header insertion was clobbering first row
- Just-in-time loading of parser modules instead of loading all at start
20191217 v1.6.1
- Add du parser (tested on linux and OSX)
- Add crontab parser (tested on linux and OSX)
- Add pip list parser (tested on linux and OSX)
- Add pip show parser (tested on linux and OSX)
- Add OSX support for the ifconfig, arp, df, mount, and uname parsers
- Add tests for ls, dig, ps, w, uptime on OSX
- Add about option
- Add universal parsers to refactor repetitive code
- Updated ifconfig parser to output 'state' as an array
20191117 v1.5.1
- Add ss parser
- Add stat parser
- Add /etc/hosts parser
- Add /etc/fstab parser
- Add systemctl parser (includes list-jobs, list-sockets, and list-unit-files)
- Add -r and raw=True options. By default, jc will now convert numbers and boolean, if possible, and add other semantic information, while the raw output will keep all values as text and provide a more literal JSON output
- Add -q and quiet=True options to suppress warnings to stderr
- Add -d option to debug parsing issues
- Add compatibility warnings to stderr
- Add documentation
- Updated iptables parser to allow --line-numbers option
- Updated lsblk parser to allow parsing of added columns
- Updated mount parser: changed 'access' field name to 'options'
- Updated netstat parser to allow parsing of unix sockets and raw network connections
- Updated w parser to fix unaligned data where blanks are possible
- Clean up code and reorganize package
20191031 v1.1.1
- Add arp parser
- Add dig parser
- Add unit tests
- fix netstat parser (remove side effects due to global variable)
20191025 v1.0.1
- Add w parser
- Add uptime parser
- Add history parser
- Flatten env parser output
- Remove problematic characters from key names in: df, free, history, lsblk, lsof, and w
- Where possible, lowercase all keys (except cases like env where the key is the variable name)
- Remove integer values
- Handle CTRL-C gracefully
20191023 v0.9.1
- Add jobs parser
- Add lsof parser
- Add lsmod parser
- No blocking if no piped data
- Better help text
- Clean up iptables parser code
20191022 v0.8.1
- Add env parser
- Add df parser
- Add free parser
- Add lsblk parser
- Add mount parser
- Add uname parser
- Add iptables parser
20191021 v0.6.4
- Flatten netstat parser output
- Clean up argument parsing
- Fix command help
20191018 v0.5.5
- Fix netstat -p parsing for Ubuntu
- Add ps parser
- Add route parser
- ls parser fixes
20191017 v0.2.0
- ifconfig, ls, and netstat support

2651
EXAMPLES.md Normal file

File diff suppressed because it is too large Load Diff

2
LICENSE.md Executable file → Normal file
View File

@@ -1,6 +1,6 @@
MIT License
Copyright (c) 2019 Kelly Brazil
Copyright (c) 2020 Kelly Brazil
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@@ -1 +0,0 @@
graft tests/fixtures

1699
README.md Executable file → Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,100 +0,0 @@
jc changelog
20200219 v1.7.4
- Updated ls parser to support multiple directories, globbing, and -R (recursive)
20200211 v1.7.3
- Add alternative 'magic' syntax: e.g. `jc ls -al`
- Options can now be condensed (e.g. -prq is equivalant to -p -r -q)
20200208 v1.7.2
- Include test fixtures in wheel and sdist
20200205 v1.7.1
- Add YAML file parser
- Add INI file parser
- Add XML file parser
- Add id parser (tested on linux and OSX)
- Add crontab file parser with user support (tested on linux)
- Add __version__ variable to parser modules
- Add exit code on error
- Updated history parser to output "line" as an integer
- Updated compatibility list for some parsers
- Bugfix in crontab file parser: header insertion was clobbering first row
- Just-in-time loading of parser modules instead of loading all at start
20191217 v1.6.1
- Add du parser (tested on linux and OSX)
- Add crontab parser (tested on linux and OSX)
- Add pip list parser (tested on linux and OSX)
- Add pip show parser (tested on linux and OSX)
- Add OSX support for the ifconfig, arp, df, mount, and uname parsers
- Add tests for ls, dig, ps, w, uptime on OSX
- Add about option
- Add universal parsers to refactor repetitive code
- Updated ifconfig parser to output 'state' as an array
20191117 v1.5.1
- Add ss parser
- Add stat parser
- Add /etc/hosts parser
- Add /etc/fstab parser
- Add systemctl parser (includes list-jobs, list-sockets, and list-unit-files)
- Add -r and raw=True options. By default, jc will now convert numbers and boolean, if possible, and add other semantic information, while the raw output will keep all values as text and provide a more literal JSON output
- Add -q and quiet=True options to suppress warnings to stderr
- Add -d option to debug parsing issues
- Add compatibility warnings to stderr
- Add documentation
- Updated iptables parser to allow --line-numbers option
- Updated lsblk parser to allow parsing of added columns
- Updated mount parser: changed 'access' field name to 'options'
- Updated netstat parser to allow parsing of unix sockets and raw network connections
- Updated w parser to fix unaligned data where blanks are possible
- Clean up code and reorganize package
20191031 v1.1.1
- Add arp parser
- Add dig parser
- Add unit tests
- fix netstat parser (remove side effects due to global variable)
20191025 v1.0.1
- Add w parser
- Add uptime parser
- Add history parser
- Flatten env parser output
- Remove problematic characters from key names in: df, free, history, lsblk, lsof, and w
- Where possible, lowercase all keys (except cases like env where the key is the variable name)
- Remove integer values
- Handle CTRL-C gracefully
20191023 v0.9.1
- Add jobs parser
- Add lsof parser
- Add lsmod parser
- No blocking if no piped data
- Better help text
- Clean up iptables parser code
20191022 v0.8.1
- Add env parser
- Add df parser
- Add free parser
- Add lsblk parser
- Add mount parser
- Add uname parser
- Add iptables parser
20191021 v0.6.4
- Flatten netstat parser output
- Clean up argument parsing
- Fix command help
20191018 v0.5.5
- Fix netstat -p parsing for Ubuntu
- Add ps parser
- Add route parser
- ls parser fixes
20191017 v0.2.0
- ifconfig, ls, and netstat support

View File

@@ -1,18 +1,28 @@
#!/bin/bash
# Generate docs.md
# requires pydoc-markdown 2.1.0.post1
cd jc
pydocmd simple jc+ > ../docs/readme.md
pydocmd simple utils+ > ../docs/utils.md
pydocmd simple jc.parsers.airport+ > ../docs/parsers/airport.md
pydocmd simple jc.parsers.airport_s+ > ../docs/parsers/airport_s.md
pydocmd simple jc.parsers.arp+ > ../docs/parsers/arp.md
pydocmd simple jc.parsers.blkid+ > ../docs/parsers/blkid.md
pydocmd simple jc.parsers.crontab+ > ../docs/parsers/crontab.md
pydocmd simple jc.parsers.crontab_u+ > ../docs/parsers/crontab_u.md
pydocmd simple jc.parsers.csv+ > ../docs/parsers/csv.md
pydocmd simple jc.parsers.date+ > ../docs/parsers/date.md
pydocmd simple jc.parsers.df+ > ../docs/parsers/df.md
pydocmd simple jc.parsers.dig+ > ../docs/parsers/dig.md
pydocmd simple jc.parsers.dmidecode+ > ../docs/parsers/dmidecode.md
pydocmd simple jc.parsers.du+ > ../docs/parsers/du.md
pydocmd simple jc.parsers.env+ > ../docs/parsers/env.md
pydocmd simple jc.parsers.file+ > ../docs/parsers/file.md
pydocmd simple jc.parsers.free+ > ../docs/parsers/free.md
pydocmd simple jc.parsers.fstab+ > ../docs/parsers/fstab.md
pydocmd simple jc.parsers.group+ > ../docs/parsers/group.md
pydocmd simple jc.parsers.gshadow+ > ../docs/parsers/gshadow.md
pydocmd simple jc.parsers.history+ > ../docs/parsers/history.md
pydocmd simple jc.parsers.hosts+ > ../docs/parsers/hosts.md
pydocmd simple jc.parsers.id+ > ../docs/parsers/id.md
@@ -20,24 +30,35 @@ pydocmd simple jc.parsers.ifconfig+ > ../docs/parsers/ifconfig.md
pydocmd simple jc.parsers.ini+ > ../docs/parsers/ini.md
pydocmd simple jc.parsers.iptables+ > ../docs/parsers/iptables.md
pydocmd simple jc.parsers.jobs+ > ../docs/parsers/jobs.md
pydocmd simple jc.parsers.kv+ > ../docs/parsers/kv.md
pydocmd simple jc.parsers.last+ > ../docs/parsers/last.md
pydocmd simple jc.parsers.ls+ > ../docs/parsers/ls.md
pydocmd simple jc.parsers.lsblk+ > ../docs/parsers/lsblk.md
pydocmd simple jc.parsers.lsmod+ > ../docs/parsers/lsmod.md
pydocmd simple jc.parsers.lsof+ > ../docs/parsers/lsof.md
pydocmd simple jc.parsers.mount+ > ../docs/parsers/mount.md
pydocmd simple jc.parsers.netstat+ > ../docs/parsers/netstat.md
pydocmd simple jc.parsers.ntpq+ > ../docs/parsers/ntpq.md
pydocmd simple jc.parsers.passwd+ > ../docs/parsers/passwd.md
pydocmd simple jc.parsers.ping+ > ../docs/parsers/ping.md
pydocmd simple jc.parsers.pip_list+ > ../docs/parsers/pip_list.md
pydocmd simple jc.parsers.pip_show+ > ../docs/parsers/pip_show.md
pydocmd simple jc.parsers.ps+ > ../docs/parsers/ps.md
pydocmd simple jc.parsers.route+ > ../docs/parsers/route.md
pydocmd simple jc.parsers.shadow+ > ../docs/parsers/shadow.md
pydocmd simple jc.parsers.ss+ > ../docs/parsers/ss.md
pydocmd simple jc.parsers.stat+ > ../docs/parsers/stat.md
pydocmd simple jc.parsers.sysctl+ > ../docs/parsers/sysctl.md
pydocmd simple jc.parsers.systemctl+ > ../docs/parsers/systemctl.md
pydocmd simple jc.parsers.systemctl_lj+ > ../docs/parsers/systemctl_lj.md
pydocmd simple jc.parsers.systemctl_ls+ > ../docs/parsers/systemctl_ls.md
pydocmd simple jc.parsers.systemctl_luf+ > ../docs/parsers/systemctl_luf.md
pydocmd simple jc.parsers.timedatectl+ > ../docs/parsers/timedatectl.md
pydocmd simple jc.parsers.tracepath+ > ../docs/parsers/tracepath.md
pydocmd simple jc.parsers.traceroute+ > ../docs/parsers/traceroute.md
pydocmd simple jc.parsers.uname+ > ../docs/parsers/uname.md
pydocmd simple jc.parsers.uptime+ > ../docs/parsers/uptime.md
pydocmd simple jc.parsers.w+ > ../docs/parsers/w.md
pydocmd simple jc.parsers.who+ > ../docs/parsers/who.md
pydocmd simple jc.parsers.xml+ > ../docs/parsers/xml.md
pydocmd simple jc.parsers.yaml+ > ../docs/parsers/yaml.md

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

@@ -0,0 +1,113 @@
# jc.parsers.airport
jc - JSON CLI output utility airport -I Parser
Usage:
specify --airport as the first argument if the piped input is coming from airport -I (OSX)
This program can be found at:
/System/Library/PrivateFrameworks/Apple80211.framework/Versions/Current/Resources/airport
Compatibility:
'darwin'
Examples:
$ airport -I | jc --airport -p
{
"agrctlrssi": -66,
"agrextrssi": 0,
"agrctlnoise": -90,
"agrextnoise": 0,
"state": "running",
"op_mode": "station",
"lasttxrate": 195,
"maxrate": 867,
"lastassocstatus": 0,
"802_11_auth": "open",
"link_auth": "wpa2-psk",
"bssid": "3c:37:86:15:ad:f9",
"ssid": "SnazzleDazzle",
"mcs": 0,
"channel": "48,80"
}
$ airport -I | jc --airport -p -r
{
"agrctlrssi": "-66",
"agrextrssi": "0",
"agrctlnoise": "-90",
"agrextnoise": "0",
"state": "running",
"op_mode": "station",
"lasttxrate": "195",
"maxrate": "867",
"lastassocstatus": "0",
"802_11_auth": "open",
"link_auth": "wpa2-psk",
"bssid": "3c:37:86:15:ad:f9",
"ssid": "SnazzleDazzle",
"mcs": "0",
"channel": "48,80"
}
## info
```python
info()
```
## process
```python
process(proc_data)
```
Final processing to conform to the schema.
Parameters:
proc_data: (dictionary) raw structured data to process
Returns:
Dictionary. Structured data with the following schema:
{
"agrctlrssi": integer,
"agrextrssi": integer,
"agrctlnoise": integer,
"agrextnoise": integer,
"state": string,
"op_mode": string,
"lasttxrate": integer,
"maxrate": integer,
"lastassocstatus": integer,
"802_11_auth": string,
"link_auth": string,
"bssid": string,
"ssid": string,
"mcs": integer,
"channel": string
}
## parse
```python
parse(data, raw=False, quiet=False)
```
Main text parsing function
Parameters:
data: (string) text data to parse
raw: (boolean) output preprocessed JSON if True
quiet: (boolean) suppress warning messages if True
Returns:
Dictionary. Raw or processed structured data.

140
docs/parsers/airport_s.md Normal file
View File

@@ -0,0 +1,140 @@
# jc.parsers.airport_s
jc - JSON CLI output utility airport -s Parser
Usage:
specify --airport as the first argument if the piped input is coming from airport -s (OSX)
This program can be found at:
/System/Library/PrivateFrameworks/Apple80211.framework/Versions/Current/Resources/airport
Compatibility:
'darwin'
Examples:
$ airport -s | jc --airport-s -p
[
{
"ssid": "DIRECT-4A-HP OfficeJet 3830",
"bssid": "00:67:eb:2a:a7:3b",
"rssi": -90,
"channel": "6",
"ht": true,
"cc": "--",
"security": [
"WPA2(PSK/AES/AES)"
]
},
{
"ssid": "Latitude38",
"bssid": "c0:ff:d5:d2:7a:f3",
"rssi": -85,
"channel": "11",
"ht": true,
"cc": "US",
"security": [
"WPA2(PSK/AES/AES)"
]
},
{
"ssid": "xfinitywifi",
"bssid": "6e:e3:0e:b8:45:99",
"rssi": -83,
"channel": "11",
"ht": true,
"cc": "US",
"security": [
"NONE"
]
},
...
]
$ airport -s | jc --airport -p -r
[
{
"ssid": "DIRECT-F3-HP ENVY 5660 series",
"bssid": "b0:5a:da:6f:0a:d4",
"rssi": "-93",
"channel": "1",
"ht": "Y",
"cc": "--",
"security": "WPA2(PSK/AES/AES)"
},
{
"ssid": "YouAreInfected-5",
"bssid": "5c:e3:0e:c2:85:da",
"rssi": "-85",
"channel": "36",
"ht": "Y",
"cc": "US",
"security": "WPA(PSK/AES,TKIP/TKIP) WPA2(PSK/AES,TKIP/TKIP)"
},
{
"ssid": "YuanFamily",
"bssid": "5c:e3:0e:b8:5f:9a",
"rssi": "-84",
"channel": "11",
"ht": "Y",
"cc": "US",
"security": "WPA(PSK/AES,TKIP/TKIP) WPA2(PSK/AES,TKIP/TKIP)"
},
...
]
## info
```python
info()
```
## process
```python
process(proc_data)
```
Final processing to conform to the schema.
Parameters:
proc_data: (dictionary) raw structured data to process
Returns:
List of dictionaries. Structured data with the following schema:
[
{
"ssid": string,
"bssid": string,
"rssi": integer,
"channel": string,
"ht": boolean,
"cc": string,
"security": [
string,
]
}
]
## parse
```python
parse(data, raw=False, quiet=False)
```
Main text parsing function
Parameters:
data: (string) text data to parse
raw: (boolean) output preprocessed JSON if True
quiet: (boolean) suppress warning messages if True
Returns:
List of dictionaries. Raw or processed structured data.

View File

@@ -1,3 +1,4 @@
# jc.parsers.arp
jc - JSON CLI output utility arp Parser
@@ -59,6 +60,8 @@ Examples:
"hwtype": "ether",
"hwaddress": "00:50:56:f0:98:26",
"iface": "ens33"
"permanent": false,
"expires": 1182
},
{
"name": "gateway",
@@ -66,6 +69,8 @@ Examples:
"hwtype": "ether",
"hwaddress": "00:50:56:f7:4a:fc",
"iface": "ens33"
"permanent": false,
"expires": 110
}
]
@@ -77,6 +82,8 @@ Examples:
"hwtype": "ether",
"hwaddress": "00:50:56:fe:7a:b4",
"iface": "ens33"
"permanent": false,
"expires": "1182"
},
{
"name": "_gateway",
@@ -84,14 +91,18 @@ Examples:
"hwtype": "ether",
"hwaddress": "00:50:56:f7:4a:fc",
"iface": "ens33"
"permanent": false,
"expires": "110"
}
]
## info
```python
info(self, /, *args, **kwargs)
info()
```
## process
```python
process(proc_data)
@@ -114,10 +125,13 @@ Returns:
"hwtype": string,
"hwaddress": string,
"flags_mask": string,
"iface": string
"iface": string,
"permanent": boolean,
"expires": integer
}
]
## parse
```python
parse(data, raw=False, quiet=False)

152
docs/parsers/blkid.md Normal file
View File

@@ -0,0 +1,152 @@
# jc.parsers.blkid
jc - JSON CLI output utility blkid Parser
Usage:
specify --blkid as the first argument if the piped input is coming from blkid
Compatibility:
'linux'
Examples:
$ blkid | jc --blkid -p
[
{
"device": "/dev/sda1",
"uuid": "05d927ab-5875-49e4-ada1-7f46cb32c932",
"type": "xfs"
},
{
"device": "/dev/sda2",
"uuid": "3klkIj-w1kk-DkJi-0XBJ-y3i7-i2Ac-vHqWBM",
"type": "LVM2_member"
},
{
"device": "/dev/mapper/centos-root",
"uuid": "07d718ff-950c-4e5b-98f0-42a1147c77d9",
"type": "xfs"
},
{
"device": "/dev/mapper/centos-swap",
"uuid": "615eb89a-bcbf-46fd-80e3-c483ff5c931f",
"type": "swap"
}
]
$ sudo blkid -o udev -ip /dev/sda2 | jc --blkid -p
[
{
"id_fs_uuid": "3klkIj-w1kk-DkJi-0XBJ-y3i7-i2Ac-vHqWBM",
"id_fs_uuid_enc": "3klkIj-w1kk-DkJi-0XBJ-y3i7-i2Ac-vHqWBM",
"id_fs_version": "LVM2\x20001",
"id_fs_type": "LVM2_member",
"id_fs_usage": "raid",
"id_iolimit_minimum_io_size": 512,
"id_iolimit_physical_sector_size": 512,
"id_iolimit_logical_sector_size": 512,
"id_part_entry_scheme": "dos",
"id_part_entry_type": "0x8e",
"id_part_entry_number": 2,
"id_part_entry_offset": 2099200,
"id_part_entry_size": 39843840,
"id_part_entry_disk": "8:0"
}
]
$ sudo blkid -ip /dev/sda1 | jc --blkid -p -r
[
{
"devname": "/dev/sda1",
"uuid": "05d927bb-5875-49e3-ada1-7f46cb31c932",
"type": "xfs",
"usage": "filesystem",
"minimum_io_size": "512",
"physical_sector_size": "512",
"logical_sector_size": "512",
"part_entry_scheme": "dos",
"part_entry_type": "0x83",
"part_entry_flags": "0x80",
"part_entry_number": "1",
"part_entry_offset": "2048",
"part_entry_size": "2097152",
"part_entry_disk": "8:0"
}
]
## info
```python
info()
```
## process
```python
process(proc_data)
```
Final processing to conform to the schema.
Parameters:
proc_data: (dictionary) raw structured data to process
Returns:
List of dictionaries. Structured data with the following schema:
[
{
"device": string,
"uuid": string,
"type": string,
"usage": string,
"part_entry_scheme": string,
"part_entry_type": string,
"part_entry_flags": string,
"part_entry_number": integer,
"part_entry_offset": integer,
"part_entry_size": integer,
"part_entry_disk": string,
"id_fs_uuid": string,
"id_fs_uuid_enc": string,
"id_fs_version": string,
"id_fs_type": string,
"id_fs_usage": string,
"id_part_entry_scheme": string,
"id_part_entry_type": string,
"id_part_entry_flags": string,
"id_part_entry_number": integer,
"id_part_entry_offset": integer,
"id_part_entry_size": integer,
"id_iolimit_minimum_io_size": integer,
"id_iolimit_physical_sector_size": integer,
"id_iolimit_logical_sector_size": integer,
"id_part_entry_disk": string,
"minimum_io_size": integer,
"physical_sector_size": integer,
"logical_sector_size": integer
}
]
## parse
```python
parse(data, raw=False, quiet=False)
```
Main text parsing function
Parameters:
data: (string) text data to parse
raw: (boolean) output preprocessed JSON if True
quiet: (boolean) suppress warning messages if True
Returns:
List of dictionaries. Raw or processed structured data.

View File

@@ -1,3 +1,4 @@
# jc.parsers.crontab
jc - JSON CLI output utility crontab command and file Parser
@@ -128,11 +129,13 @@ Examples:
]
}
## info
```python
info(self, /, *args, **kwargs)
info()
```
## process
```python
process(proc_data)
@@ -178,6 +181,7 @@ Returns:
}
## parse
```python
parse(data, raw=False, quiet=False)

View File

@@ -1,3 +1,4 @@
# jc.parsers.crontab_u
jc - JSON CLI output utility crontab file Parser
@@ -129,11 +130,13 @@ Examples:
## info
```python
info(self, /, *args, **kwargs)
info()
```
## process
```python
process(proc_data)
@@ -180,6 +183,7 @@ Returns:
}
## parse
```python
parse(data, raw=False, quiet=False)

109
docs/parsers/csv.md Normal file
View File

@@ -0,0 +1,109 @@
# jc.parsers.csv
jc - JSON CLI output utility csv Parser
Usage:
specify --csv as the first argument if the piped input is coming from a csv file.
the csv parser will attempt to automatically detect the delimiter character.
if the delimiter cannot be detected it will default to comma.
the first row of the file must be a header row.
Compatibility:
'linux', 'darwin', 'cygwin', 'win32', 'aix', 'freebsd'
Examples:
$ cat homes.csv
"Sell", "List", "Living", "Rooms", "Beds", "Baths", "Age", "Acres", "Taxes"
142, 160, 28, 10, 5, 3, 60, 0.28, 3167
175, 180, 18, 8, 4, 1, 12, 0.43, 4033
129, 132, 13, 6, 3, 1, 41, 0.33, 1471
...
$ cat homes.csv | jc --csv -p
[
{
"Sell": "142",
"List": "160",
"Living": "28",
"Rooms": "10",
"Beds": "5",
"Baths": "3",
"Age": "60",
"Acres": "0.28",
"Taxes": "3167"
},
{
"Sell": "175",
"List": "180",
"Living": "18",
"Rooms": "8",
"Beds": "4",
"Baths": "1",
"Age": "12",
"Acres": "0.43",
"Taxes": "4033"
},
{
"Sell": "129",
"List": "132",
"Living": "13",
"Rooms": "6",
"Beds": "3",
"Baths": "1",
"Age": "41",
"Acres": "0.33",
"Taxes": "1471"
},
...
]
## info
```python
info()
```
## process
```python
process(proc_data)
```
Final processing to conform to the schema.
Parameters:
proc_data: (dictionary) raw structured data to process
Returns:
List of dictionaries. Each dictionary represents a row in the csv file:
[
{
csv file converted to a Dictionary
https://docs.python.org/3/library/csv.html
}
]
## parse
```python
parse(data, raw=False, quiet=False)
```
Main text parsing function
Parameters:
data: (string) text data to parse
raw: (boolean) output preprocessed JSON if True
quiet: (boolean) suppress warning messages if True
Returns:
List of dictionaries. Raw or processed structured data.

93
docs/parsers/date.md Normal file
View File

@@ -0,0 +1,93 @@
# jc.parsers.date
jc - JSON CLI output utility date Parser
Usage:
specify --date as the first argument if the piped input is coming from date
Compatibility:
'linux', 'darwin', 'freebsd'
Examples:
$ date | jc --date -p
{
"year": 2020,
"month_num": 7,
"day": 31,
"hour": 16,
"minute": 48,
"second": 11,
"month": "Jul",
"weekday": "Fri",
"weekday_num": 6,
"timezone": "PDT"
}
$ date | jc --date -p -r
{
"year": "2020",
"month": "Jul",
"day": "31",
"weekday": "Fri",
"hour": "16",
"minute": "50",
"second": "01",
"timezone": "PDT"
}
## info
```python
info()
```
## process
```python
process(proc_data)
```
Final processing to conform to the schema.
Parameters:
proc_data: (dictionary) raw structured data to process
Returns:
Dictionary. Structured data with the following schema:
{
"year": integer,
"month_num": integer,
"day": integer,
"hour": integer,
"minute": integer,
"second": integer,
"month": string,
"weekday": string,
"weekday_num": integer,
"timezone": string
}
## parse
```python
parse(data, raw=False, quiet=False)
```
Main text parsing function
Parameters:
data: (string) text data to parse
raw: (boolean) output preprocessed JSON if True
quiet: (boolean) suppress warning messages if True
Returns:
Dictionary. Raw or processed structured data.

View File

@@ -1,3 +1,4 @@
# jc.parsers.df
jc - JSON CLI output utility df Parser
@@ -7,7 +8,7 @@ Usage:
Compatibility:
'linux', 'darwin'
'linux', 'darwin', 'freebsd'
Examples:
@@ -69,11 +70,13 @@ Examples:
...
]
## info
```python
info(self, /, *args, **kwargs)
info()
```
## process
```python
process(proc_data)
@@ -106,6 +109,7 @@ Returns:
}
]
## parse
```python
parse(data, raw=False, quiet=False)

View File

@@ -1,3 +1,4 @@
# jc.parsers.dig
jc - JSON CLI output utility dig Parser
@@ -321,11 +322,13 @@ Examples:
}
]
## info
```python
info(self, /, *args, **kwargs)
info()
```
## process
```python
process(proc_data)
@@ -353,6 +356,15 @@ Returns:
"answer_num": integer,
"authority_num": integer,
"additional_num": integer,
"axfr": [
{
"name": string,
"class": string,
"type": string,
"ttl": integer,
"data": string
}
],
"question": {
"name": string,
"class": string,
@@ -380,9 +392,11 @@ Returns:
"server": string,
"when": string,
"rcvd": integer
"size": string
}
]
## parse
```python
parse(data, raw=False, quiet=False)

157
docs/parsers/dmidecode.md Normal file
View File

@@ -0,0 +1,157 @@
# jc.parsers.dmidecode
jc - JSON CLI output utility dmidecode Parser
Usage:
specify --dmidecode as the first argument if the piped input is coming from dmidecode
Compatibility:
'linux'
Examples:
# dmidecode | jc --dmidecode -p
[
{
"handle": "0x0000",
"type": 0,
"bytes": 24,
"description": "BIOS Information",
"values": {
"vendor": "Phoenix Technologies LTD",
"version": "6.00",
"release_date": "04/13/2018",
"address": "0xEA490",
"runtime_size": "88944 bytes",
"rom_size": "64 kB",
"characteristics": [
"ISA is supported",
"PCI is supported",
"PC Card (PCMCIA) is supported",
"PNP is supported",
"APM is supported",
"BIOS is upgradeable",
"BIOS shadowing is allowed",
"ESCD support is available",
"Boot from CD is supported",
"Selectable boot is supported",
"EDD is supported",
"Print screen service is supported (int 5h)",
"8042 keyboard services are supported (int 9h)",
"Serial services are supported (int 14h)",
"Printer services are supported (int 17h)",
"CGA/mono video services are supported (int 10h)",
"ACPI is supported",
"Smart battery is supported",
"BIOS boot specification is supported",
"Function key-initiated network boot is supported",
"Targeted content distribution is supported"
],
"bios_revision": "4.6",
"firmware_revision": "0.0"
}
},
...
]
# dmidecode | jc --dmidecode -p -r
[
{
"handle": "0x0000",
"type": "0",
"bytes": "24",
"description": "BIOS Information",
"values": {
"vendor": "Phoenix Technologies LTD",
"version": "6.00",
"release_date": "04/13/2018",
"address": "0xEA490",
"runtime_size": "88944 bytes",
"rom_size": "64 kB",
"characteristics": [
"ISA is supported",
"PCI is supported",
"PC Card (PCMCIA) is supported",
"PNP is supported",
"APM is supported",
"BIOS is upgradeable",
"BIOS shadowing is allowed",
"ESCD support is available",
"Boot from CD is supported",
"Selectable boot is supported",
"EDD is supported",
"Print screen service is supported (int 5h)",
"8042 keyboard services are supported (int 9h)",
"Serial services are supported (int 14h)",
"Printer services are supported (int 17h)",
"CGA/mono video services are supported (int 10h)",
"ACPI is supported",
"Smart battery is supported",
"BIOS boot specification is supported",
"Function key-initiated network boot is supported",
"Targeted content distribution is supported"
],
"bios_revision": "4.6",
"firmware_revision": "0.0"
}
},
...
]
## info
```python
info()
```
## process
```python
process(proc_data)
```
Final processing to conform to the schema.
Parameters:
proc_data: (dictionary) raw structured data to process
Returns:
List of dictionaries. Structured data with the following schema:
[
{
"handle": string,
"type": integer,
"bytes": integer,
"description": string,
"values": { (null if empty)
"lowercase_no_spaces_keys": string,
"multiline_key_values": [
string,
]
}
}
]
## parse
```python
parse(data, raw=False, quiet=False)
```
Main text parsing function
Parameters:
data: (string) text data to parse
raw: (boolean) output preprocessed JSON if True
quiet: (boolean) suppress warning messages if True
Returns:
List of dictionaries. Raw or processed structured data.

View File

@@ -1,3 +1,4 @@
# jc.parsers.du
jc - JSON CLI output utility du Parser
@@ -69,11 +70,13 @@ Examples:
...
]
## info
```python
info(self, /, *args, **kwargs)
info()
```
## process
```python
process(proc_data)
@@ -96,6 +99,7 @@ Returns:
}
]
## parse
```python
parse(data, raw=False, quiet=False)

View File

@@ -1,3 +1,4 @@
# jc.parsers.env
jc - JSON CLI output utility env Parser
@@ -49,11 +50,13 @@ Examples:
"_": "/usr/bin/env"
}
## info
```python
info(self, /, *args, **kwargs)
info()
```
## process
```python
process(proc_data)
@@ -76,6 +79,7 @@ Returns:
}
]
## parse
```python
parse(data, raw=False, quiet=False)

94
docs/parsers/file.md Normal file
View File

@@ -0,0 +1,94 @@
# jc.parsers.file
jc - JSON CLI output utility file command Parser
Usage:
specify --file as the first argument if the piped input is coming from file.
Compatibility:
'linux', 'aix', 'freebsd', 'darwin'
Examples:
$ file * | jc --file -p
[
{
"filename": "Applications",
"type": "directory"
},
{
"filename": "another file with spaces",
"type": "empty"
},
{
"filename": "argstest.py",
"type": "Python script text executable, ASCII text"
},
{
"filename": "blkid-p.out",
"type": "ASCII text"
},
{
"filename": "blkid-pi.out",
"type": "ASCII text, with very long lines"
},
{
"filename": "cd_catalog.xml",
"type": "XML 1.0 document text, ASCII text, with CRLF line terminators"
},
{
"filename": "centosserial.sh",
"type": "Bourne-Again shell script text executable, UTF-8 Unicode text"
},
...
]
## info
```python
info()
```
## process
```python
process(proc_data)
```
Final processing to conform to the schema.
Parameters:
proc_data: (dictionary) raw structured data to process
Returns:
List of dictionaries. Structured data with the following schema:
[
{
"filename": string,
"type ": string
}
]
## parse
```python
parse(data, raw=False, quiet=False)
```
Main text parsing function
Parameters:
data: (string) text data to parse
raw: (boolean) output preprocessed JSON if True
quiet: (boolean) suppress warning messages if True
Returns:
List of dictionaries. Raw or processed structured data.

View File

@@ -1,3 +1,4 @@
# jc.parsers.free
jc - JSON CLI output utility free Parser
@@ -49,11 +50,13 @@ Examples:
}
]
## info
```python
info(self, /, *args, **kwargs)
info()
```
## process
```python
process(proc_data)
@@ -81,6 +84,7 @@ Returns:
}
]
## parse
```python
parse(data, raw=False, quiet=False)

View File

@@ -1,3 +1,4 @@
# jc.parsers.fstab
jc - JSON CLI output utility fstab Parser
@@ -7,7 +8,7 @@ Usage:
Compatibility:
'linux'
'linux', 'freebsd'
Examples:
@@ -67,11 +68,13 @@ Examples:
}
]
## info
```python
info(self, /, *args, **kwargs)
info()
```
## process
```python
process(proc_data)
@@ -98,6 +101,7 @@ Returns:
}
]
## parse
```python
parse(data, raw=False, quiet=False)

145
docs/parsers/group.md Normal file
View File

@@ -0,0 +1,145 @@
# jc.parsers.group
jc - JSON CLI output utility /etc/group file Parser
Usage:
specify --group as the first argument if the piped input is coming from /etc/group
Compatibility:
'linux', 'darwin', 'aix', 'freebsd'
Examples:
$ cat /etc/group | jc --group -p
[
{
"group_name": "nobody",
"password": "*",
"gid": -2,
"members": []
},
{
"group_name": "nogroup",
"password": "*",
"gid": -1,
"members": []
},
{
"group_name": "wheel",
"password": "*",
"gid": 0,
"members": [
"root"
]
},
{
"group_name": "certusers",
"password": "*",
"gid": 29,
"members": [
"root",
"_jabber",
"_postfix",
"_cyrus",
"_calendar",
"_dovecot"
]
},
...
]
$ cat /etc/group | jc --group -p -r
[
{
"group_name": "nobody",
"password": "*",
"gid": "-2",
"members": [
""
]
},
{
"group_name": "nogroup",
"password": "*",
"gid": "-1",
"members": [
""
]
},
{
"group_name": "wheel",
"password": "*",
"gid": "0",
"members": [
"root"
]
},
{
"group_name": "certusers",
"password": "*",
"gid": "29",
"members": [
"root",
"_jabber",
"_postfix",
"_cyrus",
"_calendar",
"_dovecot"
]
},
...
]
## info
```python
info()
```
## process
```python
process(proc_data)
```
Final processing to conform to the schema.
Parameters:
proc_data: (dictionary) raw structured data to process
Returns:
List of dictionaries. Structured data with the following schema:
[
{
"group_name": string,
"password": string,
"gid": integer,
"members": [
string
]
}
]
## parse
```python
parse(data, raw=False, quiet=False)
```
Main text parsing function
Parameters:
data: (string) text data to parse
raw: (boolean) output preprocessed JSON if True
quiet: (boolean) suppress warning messages if True
Returns:
List of dictionaries. Raw or processed structured data.

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

@@ -0,0 +1,113 @@
# jc.parsers.gshadow
jc - JSON CLI output utility /etc/gshadow file Parser
Usage:
specify --gshadow as the first argument if the piped input is coming from /etc/gshadow
Compatibility:
'linux', 'aix', 'freebsd'
Examples:
$ cat /etc/gshadow | jc --gshadow -p
[
{
"group_name": "root",
"password": "*",
"administrators": [],
"members": []
},
{
"group_name": "adm",
"password": "*",
"administrators": [],
"members": [
"syslog",
"joeuser"
]
},
...
]
$ cat /etc/gshadow | jc --gshadow -p -r
[
{
"group_name": "root",
"password": "*",
"administrators": [
""
],
"members": [
""
]
},
{
"group_name": "adm",
"password": "*",
"administrators": [
""
],
"members": [
"syslog",
"joeuser"
]
},
...
]
## info
```python
info()
```
## process
```python
process(proc_data)
```
Final processing to conform to the schema.
Parameters:
proc_data: (dictionary) raw structured data to process
Returns:
List of dictionaries. Structured data with the following schema:
[
{
"group_name": string,
"password": string,
"administrators": [
string
],
"members": [
string
]
}
]
## parse
```python
parse(data, raw=False, quiet=False)
```
Main text parsing function
Parameters:
data: (string) text data to parse
raw: (boolean) output preprocessed JSON if True
quiet: (boolean) suppress warning messages if True
Returns:
List of dictionaries. Raw or processed structured data.

View File

@@ -1,3 +1,4 @@
# jc.parsers.history
jc - JSON CLI output utility history Parser
@@ -41,11 +42,13 @@ Examples:
...
}
## info
```python
info(self, /, *args, **kwargs)
info()
```
## process
```python
process(proc_data)
@@ -68,6 +71,7 @@ Returns:
}
]
## parse
```python
parse(data, raw=False, quiet=False)

View File

@@ -1,3 +1,4 @@
# jc.parsers.hosts
jc - JSON CLI output utility hosts Parser
@@ -58,11 +59,13 @@ Examples:
}
]
## info
```python
info(self, /, *args, **kwargs)
info()
```
## process
```python
process(proc_data)
@@ -87,6 +90,7 @@ Returns:
}
]
## parse
```python
parse(data, raw=False, quiet=False)

View File

@@ -1,3 +1,4 @@
# jc.parsers.id
jc - JSON CLI output utility id Parser
@@ -67,11 +68,13 @@ Examples:
}
}
## info
```python
info(self, /, *args, **kwargs)
info()
```
## process
```python
process(proc_data)
@@ -114,6 +117,7 @@ Returns:
}
}
## parse
```python
parse(data, raw=False, quiet=False)

View File

@@ -1,3 +1,4 @@
# jc.parsers.ifconfig
jc - JSON CLI output utility ifconfig Parser
@@ -142,11 +143,19 @@ Examples:
}
]
## info
```python
info(self, /, *args, **kwargs)
info()
```
## IfconfigParser
```python
IfconfigParser(console_output)
```
## process
```python
process(proc_data)
@@ -195,6 +204,7 @@ Returns:
}
]
## parse
```python
parse(data, raw=False, quiet=False)

View File

@@ -1,9 +1,12 @@
# jc.parsers.ini
jc - JSON CLI output utility INI Parser
Usage:
specify --ini as the first argument if the piped input is coming from an INI file
Specify --ini as the first argument if the piped input is coming from an INI file or any
simple key/value pair file. Delimiter can be '=' or ':'. Missing values are supported.
Comment prefix can be '#' or ';'. Comments must be on their own line.
Compatibility:
@@ -43,11 +46,13 @@ Examples:
}
}
## info
```python
info(self, /, *args, **kwargs)
info()
```
## process
```python
process(proc_data)
@@ -61,13 +66,17 @@ Parameters:
Returns:
Dictionary representing an ini document:
Dictionary representing an ini or simple key/value pair document:
{
ini document converted to a dictionary
see configparser standard library documentation for more details
ini or key/value document converted to a dictionary - see configparser standard
library documentation for more details.
Note: Values starting and ending with quotation marks will have the marks removed.
If you would like to keep the quotation marks, use the -r or raw=True argument.
}
## parse
```python
parse(data, raw=False, quiet=False)

View File

@@ -1,3 +1,4 @@
# jc.parsers.iptables
jc - JSON CLI output utility ipables Parser
@@ -131,11 +132,13 @@ Examples:
...
]
## info
```python
info(self, /, *args, **kwargs)
info()
```
## process
```python
process(proc_data)
@@ -172,6 +175,7 @@ Returns:
}
]
## parse
```python
parse(data, raw=False, quiet=False)

View File

@@ -1,3 +1,4 @@
# jc.parsers.jobs
jc - JSON CLI output utility jobs Parser
@@ -73,11 +74,13 @@ Example:
}
]
## info
```python
info(self, /, *args, **kwargs)
info()
```
## process
```python
process(proc_data)
@@ -103,6 +106,7 @@ Returns:
}
]
## parse
```python
parse(data, raw=False, quiet=False)

61
docs/parsers/kv.md Normal file
View File

@@ -0,0 +1,61 @@
# jc.parsers.kv
jc - JSON CLI output utility Key/Value File Parser
Usage:
Specify --kv as the first argument if the piped input is coming from a simple
key/value pair file. Delimiter can be '=' or ':'. Missing values are supported.
Comment prefix can be '#' or ';'. Comments must be on their own line.
Compatibility:
'linux', 'darwin', 'cygwin', 'win32', 'aix', 'freebsd'
Examples:
$ cat keyvalue.txt
# this file contains key/value pairs
name = John Doe
address=555 California Drive
age: 34
; comments can include # or ;
# delimiter can be = or :
# quoted values have quotation marks stripped by default
# but can be preserved with the -r argument
occupation:"Engineer"
$ cat keyvalue.txt | jc --ini -p
{
"name": "John Doe",
"address": "555 California Drive",
"age": "34",
"occupation": "Engineer"
}
## info
```python
info()
```
## parse
```python
parse(data, raw=False, quiet=False)
```
Main text parsing function
Note: this is just a wrapper for jc.parsers.ini
Parameters:
data: (string) text data to parse
raw: (boolean) output preprocessed JSON if True
quiet: (boolean) suppress warning messages if True
Returns:
Dictionary representing the key/value file

122
docs/parsers/last.md Normal file
View File

@@ -0,0 +1,122 @@
# jc.parsers.last
jc - JSON CLI output utility last Parser
Usage:
specify --last as the first argument if the piped input is coming from last or lastb
Compatibility:
'linux', 'darwin', 'aix', 'freebsd'
Examples:
$ last | jc --last -p
[
{
"user": "kbrazil",
"tty": "ttys002",
"hostname": null,
"login": "Thu Feb 27 14:31",
"logout": "still logged in"
},
{
"user": "kbrazil",
"tty": "ttys003",
"hostname": null,
"login": "Thu Feb 27 10:38",
"logout": "10:38",
"duration": "00:00"
},
{
"user": "kbrazil",
"tty": "ttys003",
"hostname": null,
"login": "Thu Feb 27 10:18",
"logout": "10:18",
"duration": "00:00"
},
...
]
$ last | jc --last -p -r
[
{
"user": "kbrazil",
"tty": "ttys002",
"hostname": "-",
"login": "Thu Feb 27 14:31",
"logout": "still_logged_in"
},
{
"user": "kbrazil",
"tty": "ttys003",
"hostname": "-",
"login": "Thu Feb 27 10:38",
"logout": "10:38",
"duration": "00:00"
},
{
"user": "kbrazil",
"tty": "ttys003",
"hostname": "-",
"login": "Thu Feb 27 10:18",
"logout": "10:18",
"duration": "00:00"
},
...
]
## info
```python
info()
```
## process
```python
process(proc_data)
```
Final processing to conform to the schema.
Parameters:
proc_data: (dictionary) raw structured data to process
Returns:
List of dictionaries. Structured data with the following schema:
[
{
"user": string,
"tty": string,
"hostname": string,
"login": string,
"logout": string,
"duration": string
}
]
## parse
```python
parse(data, raw=False, quiet=False)
```
Main text parsing function
Parameters:
data: (string) text data to parse
raw: (boolean) output preprocessed JSON if True
quiet: (boolean) suppress warning messages if True
Returns:
List of dictionaries. Raw or processed structured data.

View File

@@ -1,17 +1,22 @@
# jc.parsers.ls
jc - JSON CLI output utility ls Parser
Note: The -l or -b option of ls should be used to correctly parse filenames that include newline characters.
Since ls does not encode newlines in filenames when outputting to a pipe it will cause jc to see
multiple files instead of a single file if -l or -b is not used.
Usage:
specify --ls as the first argument if the piped input is coming from ls
ls options supported:
- None
- laR
-lbaR
--time-style=full-iso
- h file sizes will be available in text form with -r but larger file sizes
with human readable suffixes will be converted to Null in default view
since the parser attempts to convert this field to an integer.
-h file sizes will be available in text form with -r but larger file sizes
with human readable suffixes will be converted to Null in default view
since the parser attempts to convert this field to an integer.
Compatibility:
@@ -141,11 +146,13 @@ Examples:
"date": "May 3 2019"
}
## info
```python
info(self, /, *args, **kwargs)
info()
```
## process
```python
process(proc_data)
@@ -174,6 +181,7 @@ Returns:
}
]
## parse
```python
parse(data, raw=False, quiet=False)

View File

@@ -1,3 +1,4 @@
# jc.parsers.lsblk
jc - JSON CLI output utility lsblk Parser
@@ -212,11 +213,13 @@ Examples:
...
]
## info
```python
info(self, /, *args, **kwargs)
info()
```
## process
```python
process(proc_data)
@@ -277,6 +280,7 @@ Returns:
}
]
## parse
```python
parse(data, raw=False, quiet=False)

View File

@@ -1,3 +1,4 @@
# jc.parsers.lsmod
jc - JSON CLI output utility lsmod Parser
@@ -103,11 +104,13 @@ Examples:
...
]
## info
```python
info(self, /, *args, **kwargs)
info()
```
## process
```python
process(proc_data)
@@ -134,6 +137,7 @@ Returns:
}
]
## parse
```python
parse(data, raw=False, quiet=False)

View File

@@ -1,3 +1,4 @@
# jc.parsers.lsof
jc - JSON CLI output utility lsof Parser
@@ -93,11 +94,13 @@ Examples:
...
]
## info
```python
info(self, /, *args, **kwargs)
info()
```
## process
```python
process(proc_data)
@@ -128,6 +131,7 @@ Returns:
}
]
## parse
```python
parse(data, raw=False, quiet=False)

View File

@@ -1,3 +1,4 @@
# jc.parsers.mount
jc - JSON CLI output utility mount Parser
@@ -7,7 +8,7 @@ Usage:
Compatibility:
'linux', 'darwin'
'linux', 'darwin', 'freebsd'
Example:
@@ -53,11 +54,13 @@ Example:
...
]
## info
```python
info(self, /, *args, **kwargs)
info()
```
## process
```python
process(proc_data)
@@ -84,6 +87,7 @@ Returns:
}
]
## parse
```python
parse(data, raw=False, quiet=False)

View File

@@ -1,3 +1,4 @@
# jc.parsers.netstat
jc - JSON CLI output utility netstat Parser
@@ -5,13 +6,18 @@ Usage:
Specify --netstat as the first argument if the piped input is coming from netstat
Caveats:
- Use of multiple 'l' options is not supported on OSX (e.g. 'netstat -rlll')
- Use of the 'A' option is not supported on OSX when using the 'r' option (e.g. netstat -rA)
Compatibility:
'linux'
'linux', 'darwin', 'freebsd'
Examples:
$ sudo netstat -apee | jc --netstat -p
# netstat -apee | jc --netstat -p
[
{
"proto": "tcp",
@@ -161,159 +167,92 @@ Examples:
...
]
$ sudo netstat -apee | jc --netstat -p -r
$ netstat -r | jc --netstat -p
[
{
"proto": "tcp",
"recv_q": "0",
"send_q": "0",
"local_address": "localhost",
"foreign_address": "0.0.0.0",
"state": "LISTEN",
"user": "systemd-resolve",
"inode": "26958",
"program_name": "systemd-resolve",
"kind": "network",
"pid": "887",
"local_port": "domain",
"foreign_port": "*",
"transport_protocol": "tcp",
"network_protocol": "ipv4"
"destination": "default",
"gateway": "gateway",
"genmask": "0.0.0.0",
"route_flags": "UG",
"mss": 0,
"window": 0,
"irtt": 0,
"iface": "ens33",
"kind": "route",
"route_flags_pretty": [
"UP",
"GATEWAY"
]
},
{
"proto": "tcp",
"recv_q": "0",
"send_q": "0",
"local_address": "0.0.0.0",
"foreign_address": "0.0.0.0",
"state": "LISTEN",
"user": "root",
"inode": "30499",
"program_name": "sshd",
"kind": "network",
"pid": "1186",
"local_port": "ssh",
"foreign_port": "*",
"transport_protocol": "tcp",
"network_protocol": "ipv4"
"destination": "172.17.0.0",
"gateway": "0.0.0.0",
"genmask": "255.255.0.0",
"route_flags": "U",
"mss": 0,
"window": 0,
"irtt": 0,
"iface": "docker0",
"kind": "route",
"route_flags_pretty": [
"UP"
]
},
{
"proto": "tcp",
"recv_q": "0",
"send_q": "0",
"local_address": "localhost",
"foreign_address": "localhost",
"state": "ESTABLISHED",
"user": "root",
"inode": "46829",
"program_name": "sshd: root",
"kind": "network",
"pid": "2242",
"local_port": "ssh",
"foreign_port": "52186",
"transport_protocol": "tcp",
"network_protocol": "ipv4"
},
{
"proto": "tcp",
"recv_q": "0",
"send_q": "0",
"local_address": "localhost",
"foreign_address": "localhost",
"state": "ESTABLISHED",
"user": "root",
"inode": "46828",
"program_name": "ssh",
"kind": "network",
"pid": "2241",
"local_port": "52186",
"foreign_port": "ssh",
"transport_protocol": "tcp",
"network_protocol": "ipv4"
},
{
"proto": "tcp6",
"recv_q": "0",
"send_q": "0",
"local_address": "[::]",
"foreign_address": "[::]",
"state": "LISTEN",
"user": "root",
"inode": "30510",
"program_name": "sshd",
"kind": "network",
"pid": "1186",
"local_port": "ssh",
"foreign_port": "*",
"transport_protocol": "tcp",
"network_protocol": "ipv6"
},
{
"proto": "udp",
"recv_q": "0",
"send_q": "0",
"local_address": "localhost",
"foreign_address": "0.0.0.0",
"state": null,
"user": "systemd-resolve",
"inode": "26957",
"program_name": "systemd-resolve",
"kind": "network",
"pid": "887",
"local_port": "domain",
"foreign_port": "*",
"transport_protocol": "udp",
"network_protocol": "ipv4"
},
{
"proto": "raw6",
"recv_q": "0",
"send_q": "0",
"local_address": "[::]",
"foreign_address": "[::]",
"state": "7",
"user": "systemd-network",
"inode": "27001",
"program_name": "systemd-network",
"kind": "network",
"pid": "867",
"local_port": "ipv6-icmp",
"foreign_port": "*",
"transport_protocol": null,
"network_protocol": "ipv6"
},
{
"proto": "unix",
"refcnt": "2",
"flags": null,
"type": "DGRAM",
"state": null,
"inode": "33322",
"program_name": "systemd",
"path": "/run/user/1000/systemd/notify",
"kind": "socket",
"pid": " 1607"
},
{
"proto": "unix",
"refcnt": "2",
"flags": "ACC",
"type": "SEQPACKET",
"state": "LISTENING",
"inode": "20835",
"program_name": "init",
"path": "/run/udev/control",
"kind": "socket",
"pid": " 1"
},
...
"destination": "192.168.71.0",
"gateway": "0.0.0.0",
"genmask": "255.255.255.0",
"route_flags": "U",
"mss": 0,
"window": 0,
"irtt": 0,
"iface": "ens33",
"kind": "route",
"route_flags_pretty": [
"UP"
]
}
]
$ netstat -i | jc --netstat -p
[
{
"iface": "ens33",
"mtu": 1500,
"rx_ok": 476,
"rx_err": 0,
"rx_drp": 0,
"rx_ovr": 0,
"tx_ok": 312,
"tx_err": 0,
"tx_drp": 0,
"tx_ovr": 0,
"flg": "BMRU",
"kind": "interface"
},
{
"iface": "lo",
"mtu": 65536,
"rx_ok": 0,
"rx_err": 0,
"rx_drp": 0,
"rx_ovr": 0,
"tx_ok": 0,
"tx_err": 0,
"tx_drp": 0,
"tx_ovr": 0,
"flg": "LRU",
"kind": "interface"
}
]
## info
```python
info(self, /, *args, **kwargs)
info()
```
## process
```python
process(proc_data)
@@ -331,31 +270,104 @@ Returns:
[
{
"proto": string,
"recv_q": integer,
"send_q": integer,
"transport_protocol" string,
"network_protocol": string,
"local_address": string,
"local_port": string,
"local_port_num": integer,
"foreign_address": string,
"foreign_port": string,
"foreign_port_num": integer,
"state": string,
"program_name": string,
"pid": integer,
"user": string,
"security_context": string,
"refcnt": integer,
"flags": string,
"type": string,
"inode": integer,
"path": string,
"kind": string
"proto": string,
"recv_q": integer,
"send_q": integer,
"transport_protocol" string,
"network_protocol": string,
"local_address": string,
"local_port": string,
"local_port_num": integer,
"foreign_address": string,
"foreign_port": string,
"foreign_port_num": integer,
"state": string,
"program_name": string,
"pid": integer,
"user": string,
"security_context": string,
"refcnt": integer,
"flags": string,
"type": string,
"inode": integer,
"path": string,
"kind": string,
"address": string,
"unix_inode": string,
"conn": string,
"refs": string,
"nextref": string,
"name": string,
"unit": integer,
"vendor": integer,
"class": integer,
"subcla": integer,
"unix_flags": integer,
"pcbcount": integer,
"rcvbuf": integer,
"sndbuf": integer,
"rxbytes": integer,
"txbytes": integer,
"destination": string,
"gateway": string,
"route_flags": string,
"route_flags_pretty": [
string,
]
"route_refs": integer,
"use": integer,
"mtu": integer,
"expire": string,
"genmask": string,
"mss": integer,
"window": integer,
"irtt": integer,
"iface": string,
"metric": integer,
"network": string,
"address": string,
"ipkts": integer, - = null
"ierrs": integer, - = null
"idrop": integer, - = null
"opkts": integer, - = null
"oerrs": integer, - = null
"coll": integer, - = null
"rx_ok": integer,
"rx_err": integer,
"rx_drp": integer,
"rx_ovr": integer,
"tx_ok": integer,
"tx_err": integer,
"tx_drp": integer,
"tx_ovr": integer,
"flg": string,
"ibytes": integer,
"obytes": integer,
"r_mbuf": integer,
"s_mbuf": integer,
"r_clus": integer,
"s_clus": integer,
"r_hiwa": integer,
"s_hiwa": integer,
"r_lowa": integer,
"s_lowa": integer,
"r_bcnt": integer,
"s_bcnt": integer,
"r_bmax": integer,
"s_bmax": integer,
"rexmit": integer,
"ooorcv": integer,
"0_win": integer,
"rexmt": float,
"persist": float,
"keep": float,
"2msl": float,
"delack": float,
"rcvtime": float,
}
]
## parse
```python
parse(data, raw=False, quiet=False)

239
docs/parsers/ntpq.md Normal file
View File

@@ -0,0 +1,239 @@
# jc.parsers.ntpq
jc - JSON CLI output utility ntpq Parser
Usage:
specify --ntpq as the first argument if the piped input is coming from ntpq -p
Compatibility:
'linux', 'freebsd'
Examples:
$ ntpq -p | jc --ntpq -p
[
{
"remote": "44.190.6.254",
"refid": "127.67.113.92",
"st": 2,
"t": "u",
"when": 1,
"poll": 64,
"reach": 1,
"delay": 23.399,
"offset": -2.805,
"jitter": 2.131,
"state": null
},
{
"remote": "ntp.wdc1.us.lea",
"refid": "130.133.1.10",
"st": 2,
"t": "u",
"when": null,
"poll": 64,
"reach": 1,
"delay": 93.053,
"offset": -0.807,
"jitter": 2.839,
"state": null
},
{
"remote": "clock.team-cymr",
"refid": "204.9.54.119",
"st": 2,
"t": "u",
"when": null,
"poll": 64,
"reach": 1,
"delay": 70.337,
"offset": -2.909,
"jitter": 2.6,
"state": null
},
{
"remote": "mirror1.sjc02.s",
"refid": "216.218.254.202",
"st": 2,
"t": "u",
"when": 2,
"poll": 64,
"reach": 1,
"delay": 29.325,
"offset": 1.044,
"jitter": 4.069,
"state": null,
}
]
$ ntpq -pn| jc --ntpq -p
[
{
"remote": "44.190.6.254",
"refid": "127.67.113.92",
"st": 2,
"t": "u",
"when": 66,
"poll": 64,
"reach": 377,
"delay": 22.69,
"offset": -0.392,
"jitter": 2.085,
"state": "+"
},
{
"remote": "108.59.2.24",
"refid": "130.133.1.10",
"st": 2,
"t": "u",
"when": 63,
"poll": 64,
"reach": 377,
"delay": 90.805,
"offset": 2.84,
"jitter": 1.908,
"state": "-"
},
{
"remote": "38.229.71.1",
"refid": "204.9.54.119",
"st": 2,
"t": "u",
"when": 64,
"poll": 64,
"reach": 377,
"delay": 68.699,
"offset": -0.61,
"jitter": 2.576,
"state": "+"
},
{
"remote": "72.5.72.15",
"refid": "216.218.254.202",
"st": 2,
"t": "u",
"when": 63,
"poll": 64,
"reach": 377,
"delay": 22.654,
"offset": 0.231,
"jitter": 1.964,
"state": "*"
}
]
$ ntpq -pn| jc --ntpq -p -r
[
{
"s": "+",
"remote": "44.190.6.254",
"refid": "127.67.113.92",
"st": "2",
"t": "u",
"when": "66",
"poll": "64",
"reach": "377",
"delay": "22.690",
"offset": "-0.392",
"jitter": "2.085"
},
{
"s": "-",
"remote": "108.59.2.24",
"refid": "130.133.1.10",
"st": "2",
"t": "u",
"when": "63",
"poll": "64",
"reach": "377",
"delay": "90.805",
"offset": "2.840",
"jitter": "1.908"
},
{
"s": "+",
"remote": "38.229.71.1",
"refid": "204.9.54.119",
"st": "2",
"t": "u",
"when": "64",
"poll": "64",
"reach": "377",
"delay": "68.699",
"offset": "-0.610",
"jitter": "2.576"
},
{
"s": "*",
"remote": "72.5.72.15",
"refid": "216.218.254.202",
"st": "2",
"t": "u",
"when": "63",
"poll": "64",
"reach": "377",
"delay": "22.654",
"offset": "0.231",
"jitter": "1.964"
}
]
## info
```python
info()
```
## process
```python
process(proc_data)
```
Final processing to conform to the schema.
Parameters:
proc_data: (dictionary) raw structured data to process
Returns:
List of dictionaries. Structured data with the following schema:
[
{
"state": string, # space/~ converted to null
"remote": string,
"refid": string,
"st": integer,
"t": string,
"when": integer, # - converted to null
"poll": integer,
"reach": integer,
"delay": float,
"offset": float,
"jitter": float
},
]
## parse
```python
parse(data, raw=False, quiet=False)
```
Main text parsing function
Parameters:
data: (string) text data to parse
raw: (boolean) output preprocessed JSON if True
quiet: (boolean) suppress warning messages if True
Returns:
List of dictionaries. Raw or processed structured data.

130
docs/parsers/passwd.md Normal file
View File

@@ -0,0 +1,130 @@
# jc.parsers.passwd
jc - JSON CLI output utility /etc/passwd file Parser
Usage:
specify --passwd as the first argument if the piped input is coming from /etc/passwd
Compatibility:
'linux', 'darwin', 'aix', 'freebsd'
Examples:
$ cat /etc/passwd | jc --passwd -p
[
{
"username": "nobody",
"password": "*",
"uid": -2,
"gid": -2,
"comment": "Unprivileged User",
"home": "/var/empty",
"shell": "/usr/bin/false"
},
{
"username": "root",
"password": "*",
"uid": 0,
"gid": 0,
"comment": "System Administrator",
"home": "/var/root",
"shell": "/bin/sh"
},
{
"username": "daemon",
"password": "*",
"uid": 1,
"gid": 1,
"comment": "System Services",
"home": "/var/root",
"shell": "/usr/bin/false"
},
...
]
$ cat /etc/passwd | jc --passwd -p -r
[
{
"username": "nobody",
"password": "*",
"uid": "-2",
"gid": "-2",
"comment": "Unprivileged User",
"home": "/var/empty",
"shell": "/usr/bin/false"
},
{
"username": "root",
"password": "*",
"uid": "0",
"gid": "0",
"comment": "System Administrator",
"home": "/var/root",
"shell": "/bin/sh"
},
{
"username": "daemon",
"password": "*",
"uid": "1",
"gid": "1",
"comment": "System Services",
"home": "/var/root",
"shell": "/usr/bin/false"
},
...
]
## info
```python
info()
```
## process
```python
process(proc_data)
```
Final processing to conform to the schema.
Parameters:
proc_data: (dictionary) raw structured data to process
Returns:
List of dictionaries. Structured data with the following schema:
[
{
"username": string,
"password": string,
"uid": integer,
"gid": integer,
"comment": string,
"home": string,
"shell": string
}
]
## parse
```python
parse(data, raw=False, quiet=False)
```
Main text parsing function
Parameters:
data: (string) text data to parse
raw: (boolean) output preprocessed JSON if True
quiet: (boolean) suppress warning messages if True
Returns:
List of dictionaries. Raw or processed structured data.

175
docs/parsers/ping.md Normal file
View File

@@ -0,0 +1,175 @@
# jc.parsers.ping
jc - JSON CLI output utility ping Parser
Usage:
specify --ping as the first argument if the piped input is coming from ping
Note: Use the ping -c (count) option, otherwise data will not be piped to jc.
Compatibility:
'linux', 'darwin', 'freebsd'
Examples:
$ ping -c 3 -p ff cnn.com | jc --ping -p
{
"destination_ip": "151.101.1.67",
"data_bytes": 56,
"pattern": "0xff",
"destination": "cnn.com",
"packets_transmitted": 3,
"packets_received": 3,
"packet_loss_percent": 0.0,
"duplicates": 0,
"round_trip_ms_min": 28.015,
"round_trip_ms_avg": 32.848,
"round_trip_ms_max": 39.376,
"round_trip_ms_stddev": 4.79,
"responses": [
{
"type": "reply",
"bytes": 64,
"response_ip": "151.101.1.67",
"icmp_seq": 0,
"ttl": 59,
"time_ms": 28.015,
"duplicate": false
},
{
"type": "reply",
"bytes": 64,
"response_ip": "151.101.1.67",
"icmp_seq": 1,
"ttl": 59,
"time_ms": 39.376,
"duplicate": false
},
{
"type": "reply",
"bytes": 64,
"response_ip": "151.101.1.67",
"icmp_seq": 2,
"ttl": 59,
"time_ms": 31.153,
"duplicate": false
}
]
}
$ ping -c 3 -p ff cnn.com | jc --ping -p -r
{
"destination_ip": "151.101.129.67",
"data_bytes": "56",
"pattern": "0xff",
"destination": "cnn.com",
"packets_transmitted": "3",
"packets_received": "3",
"packet_loss_percent": "0.0",
"duplicates": "0",
"round_trip_ms_min": "25.078",
"round_trip_ms_avg": "29.543",
"round_trip_ms_max": "32.553",
"round_trip_ms_stddev": "3.221",
"responses": [
{
"type": "reply",
"bytes": "64",
"response_ip": "151.101.129.67",
"icmp_seq": "0",
"ttl": "59",
"time_ms": "25.078",
"duplicate": false
},
{
"type": "reply",
"bytes": "64",
"response_ip": "151.101.129.67",
"icmp_seq": "1",
"ttl": "59",
"time_ms": "30.999",
"duplicate": false
},
{
"type": "reply",
"bytes": "64",
"response_ip": "151.101.129.67",
"icmp_seq": "2",
"ttl": "59",
"time_ms": "32.553",
"duplicate": false
}
]
}
## info
```python
info()
```
## process
```python
process(proc_data)
```
Final processing to conform to the schema.
Parameters:
proc_data: (dictionary) raw structured data to process
Returns:
Dictionary. Structured data with the following schema:
{
"source_ip": string,
"destination_ip": string,
"data_bytes": integer,
"pattern": string, (null if not set)
"destination": string,
"packets_transmitted": integer,
"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,
"responses": [
{
"type": string, ('reply' or 'timeout')
"timestamp": float,
"bytes": integer,
"response_ip": string,
"icmp_seq": integer,
"ttl": integer,
"time_ms": float,
"duplicate": boolean
}
]
}
## parse
```python
parse(data, raw=False, quiet=False)
```
Main text parsing function
Parameters:
data: (string) text data to parse
raw: (boolean) output preprocessed JSON if True
quiet: (boolean) suppress warning messages if True
Returns:
Dictionary. Raw or processed structured data.

View File

@@ -1,3 +1,4 @@
# jc.parsers.pip_list
jc - JSON CLI output utility pip-list Parser
@@ -28,11 +29,13 @@ Examples:
...
]
## info
```python
info(self, /, *args, **kwargs)
info()
```
## process
```python
process(proc_data)
@@ -56,6 +59,7 @@ Returns:
}
]
## parse
```python
parse(data, raw=False, quiet=False)

View File

@@ -1,3 +1,4 @@
# jc.parsers.pip_show
jc - JSON CLI output utility pip-show Parser
@@ -39,11 +40,13 @@ Examples:
}
]
## info
```python
info(self, /, *args, **kwargs)
info()
```
## process
```python
process(proc_data)
@@ -75,6 +78,7 @@ Returns:
]
## parse
```python
parse(data, raw=False, quiet=False)

View File

@@ -1,3 +1,4 @@
# jc.parsers.ps
jc - JSON CLI output utility ps Parser
@@ -173,11 +174,13 @@ Examples:
...
]
## info
```python
info(self, /, *args, **kwargs)
info()
```
## process
```python
process(proc_data)
@@ -215,6 +218,7 @@ Returns:
}
]
## parse
```python
parse(data, raw=False, quiet=False)

View File

@@ -1,3 +1,4 @@
# jc.parsers.route
jc - JSON CLI output utility route Parser
@@ -15,53 +16,48 @@ Examples:
[
{
"destination": "default",
"gateway": "gateway",
"gateway": "_gateway",
"genmask": "0.0.0.0",
"flags": "UG",
"metric": 100,
"metric": 202,
"ref": 0,
"use": 0,
"iface": "ens33",
"mss": 0,
"window": 0,
"irtt": 0
},
{
"destination": "172.17.0.0",
"gateway": "0.0.0.0",
"genmask": "255.255.0.0",
"flags": "U",
"metric": 0,
"ref": 0,
"use": 0,
"iface": "docker",
"mss": 0,
"window": 0,
"irtt": 0
"irtt": 0,
"flags_pretty": [
"UP",
"GATEWAY"
]
},
{
"destination": "192.168.71.0",
"gateway": "0.0.0.0",
"genmask": "255.255.255.0",
"flags": "U",
"metric": 100,
"metric": 202,
"ref": 0,
"use": 0,
"iface": "ens33",
"mss": 0,
"window": 0,
"irtt": 0
"irtt": 0,
"flags_pretty": [
"UP"
]
}
]
$ route -ee | jc --route -p -r
[
{
"destination": "default",
"gateway": "gateway",
"gateway": "_gateway",
"genmask": "0.0.0.0",
"flags": "UG",
"metric": "100",
"metric": "202",
"ref": "0",
"use": "0",
"iface": "ens33",
@@ -69,25 +65,12 @@ Examples:
"window": "0",
"irtt": "0"
},
{
"destination": "172.17.0.0",
"gateway": "0.0.0.0",
"genmask": "255.255.0.0",
"flags": "U",
"metric": "0",
"ref": "0",
"use": "0",
"iface": "docker",
"mss": "0",
"window": "0",
"irtt": "0"
},
{
"destination": "192.168.71.0",
"gateway": "0.0.0.0",
"genmask": "255.255.255.0",
"flags": "U",
"metric": "100",
"metric": "202",
"ref": "0",
"use": "0",
"iface": "ens33",
@@ -97,11 +80,14 @@ Examples:
}
]
## info
```python
info(self, /, *args, **kwargs)
info()
```
## process
```python
process(proc_data)
@@ -119,20 +105,24 @@ Returns:
[
{
"destination": string,
"gateway": string,
"genmask": string,
"flags": string,
"metric": integer,
"ref": integer,
"use": integer,
"mss": integer,
"window": integer,
"irtt": integer,
"iface": string
"destination": string,
"gateway": string,
"genmask": string,
"flags": string,
"flags_pretty": [
string,
]
"metric": integer,
"ref": integer,
"use": integer,
"mss": integer,
"window": integer,
"irtt": integer,
"iface": string
}
]
## parse
```python
parse(data, raw=False, quiet=False)

137
docs/parsers/shadow.md Normal file
View File

@@ -0,0 +1,137 @@
# jc.parsers.shadow
jc - JSON CLI output utility /etc/shadow file Parser
Usage:
specify --shadow as the first argument if the piped input is coming from /etc/shadow
Compatibility:
'linux', 'darwin', 'aix', 'freebsd'
Examples:
$ sudo cat /etc/shadow | jc --shadow -p
[
{
"username": "root",
"password": "*",
"last_changed": 18113,
"minimum": 0,
"maximum": 99999,
"warn": 7,
"inactive": null,
"expire": null
},
{
"username": "daemon",
"password": "*",
"last_changed": 18113,
"minimum": 0,
"maximum": 99999,
"warn": 7,
"inactive": null,
"expire": null
},
{
"username": "bin",
"password": "*",
"last_changed": 18113,
"minimum": 0,
"maximum": 99999,
"warn": 7,
"inactive": null,
"expire": null
},
...
]
$ sudo cat /etc/shadow | jc --shadow -p -r
[
{
"username": "root",
"password": "*",
"last_changed": "18113",
"minimum": "0",
"maximum": "99999",
"warn": "7",
"inactive": "",
"expire": ""
},
{
"username": "daemon",
"password": "*",
"last_changed": "18113",
"minimum": "0",
"maximum": "99999",
"warn": "7",
"inactive": "",
"expire": ""
},
{
"username": "bin",
"password": "*",
"last_changed": "18113",
"minimum": "0",
"maximum": "99999",
"warn": "7",
"inactive": "",
"expire": ""
},
...
]
## info
```python
info()
```
## process
```python
process(proc_data)
```
Final processing to conform to the schema.
Parameters:
proc_data: (dictionary) raw structured data to process
Returns:
List of dictionaries. Structured data with the following schema:
[
{
"username": string,
"password": string,
"last_changed": integer,
"minimum": integer,
"maximum": integer,
"warn": integer,
"inactive": integer,
"expire": integer
}
]
## parse
```python
parse(data, raw=False, quiet=False)
```
Main text parsing function
Parameters:
data: (string) text data to parse
raw: (boolean) output preprocessed JSON if True
quiet: (boolean) suppress warning messages if True
Returns:
List of dictionaries. Raw or processed structured data.

View File

@@ -1,3 +1,4 @@
# jc.parsers.ss
jc - JSON CLI output utility ss Parser
@@ -247,11 +248,13 @@ Examples:
}
]
## info
```python
info(self, /, *args, **kwargs)
info()
```
## process
```python
process(proc_data)
@@ -289,6 +292,7 @@ Returns:
Information from https://www.cyberciti.biz/files/ss.html used to define field names
## parse
```python
parse(data, raw=False, quiet=False)

View File

@@ -1,3 +1,4 @@
# jc.parsers.stat
jc - JSON CLI output utility stat Parser
@@ -7,7 +8,7 @@ Usage:
Compatibility:
'linux'
'linux', 'darwin', 'freebsd'
Examples:
@@ -101,11 +102,13 @@ Examples:
..
]
## info
```python
info(self, /, *args, **kwargs)
info()
```
## process
```python
process(proc_data)
@@ -141,10 +144,15 @@ Returns:
"access_time": string, # - = null
"modify_time": string, # - = null
"change_time": string, # - = null
"birth_time": string # - = null
"birth_time": string, # - = null
"unix_device": integer,
"rdev": integer,
"block_size": integer,
"unix_flags": string
}
]
## parse
```python
parse(data, raw=False, quiet=False)

88
docs/parsers/sysctl.md Normal file
View File

@@ -0,0 +1,88 @@
# jc.parsers.sysctl
jc - JSON CLI output utility sysctl -a Parser
Usage:
specify --sysctl as the first argument if the piped input is coming from sysctl -a
Note: since sysctl output is not easily parsable only a very simple key/value object
will be output. An attempt is made to convert obvious integers and floats. If no
conversion is desired, use the -r (raw) option.
Compatibility:
'linux', 'darwin', 'freebsd'
Examples:
$ sysctl | jc --sysctl -p
{
"user.cs_path": "/usr/bin:/bin:/usr/sbin:/sbin",
"user.bc_base_max": 99,
"user.bc_dim_max": 2048,
"user.bc_scale_max": 99,
"user.bc_string_max": 1000,
"user.coll_weights_max": 2,
"user.expr_nest_max": 32
...
}
$ sysctl | jc --sysctl -p -r
{
"user.cs_path": "/usr/bin:/bin:/usr/sbin:/sbin",
"user.bc_base_max": "99",
"user.bc_dim_max": "2048",
"user.bc_scale_max": "99",
"user.bc_string_max": "1000",
"user.coll_weights_max": "2",
"user.expr_nest_max": "32",
...
}
## info
```python
info()
```
## process
```python
process(proc_data)
```
Final processing to conform to the schema.
Parameters:
proc_data: (dictionary) raw structured data to process
Returns:
Dictionary. Structured data with the following schema:
{
"foo": string/integer/float, # best guess based on value
"bar": string/integer/float,
"baz": string/integer/float
}
## parse
```python
parse(data, raw=False, quiet=False)
```
Main text parsing function
Parameters:
data: (string) text data to parse
raw: (boolean) output preprocessed JSON if True
quiet: (boolean) suppress warning messages if True
Returns:
Dictionary. Raw or processed structured data.

View File

@@ -1,3 +1,4 @@
# jc.parsers.systemctl
jc - JSON CLI output utility systemctl Parser
@@ -37,11 +38,13 @@ Examples:
...
]
## info
```python
info(self, /, *args, **kwargs)
info()
```
## process
```python
process(proc_data)
@@ -67,6 +70,7 @@ Returns:
}
]
## parse
```python
parse(data, raw=False, quiet=False)

View File

@@ -1,3 +1,4 @@
# jc.parsers.systemctl_lj
jc - JSON CLI output utility systemctl-lj Parser
@@ -56,11 +57,13 @@ Examples:
]
## info
```python
info(self, /, *args, **kwargs)
info()
```
## process
```python
process(proc_data)
@@ -85,6 +88,7 @@ Returns:
}
]
## parse
```python
parse(data, raw=False, quiet=False)

View File

@@ -1,3 +1,4 @@
# jc.parsers.systemctl_ls
jc - JSON CLI output utility systemctl-ls Parser
@@ -31,11 +32,13 @@ Examples:
...
]
## info
```python
info(self, /, *args, **kwargs)
info()
```
## process
```python
process(proc_data)
@@ -59,6 +62,7 @@ Returns:
}
]
## parse
```python
parse(data, raw=False, quiet=False)

View File

@@ -1,3 +1,4 @@
# jc.parsers.systemctl_luf
jc - JSON CLI output utility systemctl-luf Parser
@@ -28,11 +29,13 @@ Examples:
...
]
## info
```python
info(self, /, *args, **kwargs)
info()
```
## process
```python
process(proc_data)
@@ -55,6 +58,7 @@ Returns:
}
]
## parse
```python
parse(data, raw=False, quiet=False)

View File

@@ -0,0 +1,91 @@
# jc.parsers.timedatectl
jc - JSON CLI output utility timedatectl Parser
Usage:
specify --timedatectl as the first argument if the piped input is coming from timedatectl or timedatectl status
Compatibility:
'linux'
Examples:
$ timedatectl | jc --timedatectl -p
{
"local_time": "Tue 2020-03-10 17:53:21 PDT",
"universal_time": "Wed 2020-03-11 00:53:21 UTC",
"rtc_time": "Wed 2020-03-11 00:53:21",
"time_zone": "America/Los_Angeles (PDT, -0700)",
"ntp_enabled": true,
"ntp_synchronized": true,
"rtc_in_local_tz": false,
"dst_active": true
}
$ timedatectl | jc --timedatectl -p -r
{
"local_time": "Tue 2020-03-10 17:53:21 PDT",
"universal_time": "Wed 2020-03-11 00:53:21 UTC",
"rtc_time": "Wed 2020-03-11 00:53:21",
"time_zone": "America/Los_Angeles (PDT, -0700)",
"ntp_enabled": "yes",
"ntp_synchronized": "yes",
"rtc_in_local_tz": "no",
"dst_active": "yes"
}
## info
```python
info()
```
## process
```python
process(proc_data)
```
Final processing to conform to the schema.
Parameters:
proc_data: (dictionary) raw structured data to process
Returns:
Dictionary. Structured data with the following schema:
{
"local_time": string,
"universal_time": string,
"rtc_time": string,
"time_zone": string,
"ntp_enabled": boolean,
"ntp_synchronized": boolean,
"system_clock_synchronized": boolean,
"systemd-timesyncd.service_active": boolean,
"rtc_in_local_tz": boolean,
"dst_active": boolean
}
## parse
```python
parse(data, raw=False, quiet=False)
```
Main text parsing function
Parameters:
data: (string) text data to parse
raw: (boolean) output preprocessed JSON if True
quiet: (boolean) suppress warning messages if True
Returns:
Dictionary. Raw or processed structured data.

162
docs/parsers/tracepath.md Normal file
View File

@@ -0,0 +1,162 @@
# jc.parsers.tracepath
jc - JSON CLI output utility tracepath Parser
Usage:
specify --tracepath as the first argument if the piped input is coming from tracepath
Compatibility:
'linux'
Examples:
$ tracepath6 3ffe:2400:0:109::2 | jc --tracepath -p
{
"pmtu": 1480,
"forward_hops": 2,
"return_hops": 2,
"hops": [
{
"ttl": 1,
"guess": true,
"host": "[LOCALHOST]",
"reply_ms": null,
"pmtu": 1500,
"asymmetric_difference": null,
"reached": false
},
{
"ttl": 1,
"guess": false,
"host": "dust.inr.ac.ru",
"reply_ms": 0.411,
"pmtu": null,
"asymmetric_difference": null,
"reached": false
},
{
"ttl": 2,
"guess": false,
"host": "dust.inr.ac.ru",
"reply_ms": 0.39,
"pmtu": 1480,
"asymmetric_difference": 1,
"reached": false
},
{
"ttl": 2,
"guess": false,
"host": "3ffe:2400:0:109::2",
"reply_ms": 463.514,
"pmtu": null,
"asymmetric_difference": null,
"reached": true
}
]
}
$ tracepath6 3ffe:2400:0:109::2 | jc --tracepath -p -r
{
"pmtu": "1480",
"forward_hops": "2",
"return_hops": "2",
"hops": [
{
"ttl": "1",
"guess": true,
"host": "[LOCALHOST]",
"reply_ms": null,
"pmtu": "1500",
"asymmetric_difference": null,
"reached": false
},
{
"ttl": "1",
"guess": false,
"host": "dust.inr.ac.ru",
"reply_ms": "0.411",
"pmtu": null,
"asymmetric_difference": null,
"reached": false
},
{
"ttl": "2",
"guess": false,
"host": "dust.inr.ac.ru",
"reply_ms": "0.390",
"pmtu": "1480",
"asymmetric_difference": "1",
"reached": false
},
{
"ttl": "2",
"guess": false,
"host": "3ffe:2400:0:109::2",
"reply_ms": "463.514",
"pmtu": null,
"asymmetric_difference": null,
"reached": true
}
]
}
## info
```python
info()
```
## process
```python
process(proc_data)
```
Final processing to conform to the schema.
Parameters:
proc_data: (dictionary) raw structured data to process
Returns:
Dictionary. Structured data with the following schema:
{
"pmtu": integer,
"forward_hops": integer,
"return_hops": integer,
"hops": [
{
"ttl": integer,
"guess": boolean,
"host": string,
"reply_ms": float,
"pmtu": integer,
"asymmetric_difference": integer,
"reached": boolean
}
]
}
## parse
```python
parse(data, raw=False, quiet=False)
```
Main text parsing function
Parameters:
data: (string) text data to parse
raw: (boolean) output preprocessed JSON if True
quiet: (boolean) suppress warning messages if True
Returns:
Dictionary. Raw or processed structured data.

155
docs/parsers/traceroute.md Normal file
View File

@@ -0,0 +1,155 @@
# jc.parsers.traceroute
jc - JSON CLI output utility traceroute Parser
Usage:
specify --traceroute as the first argument if the piped input is coming from traceroute
Note: On some operating systems you will need to redirect STDERR to STDOUT for destination
info since the header line is sent to STDERR. A warning message will be printed to
STDERR if the header row is not found.
e.g. $ traceroute 8.8.8.8 2>&1 | jc --traceroute
Compatibility:
'linux', 'darwin', 'freebsd'
Examples:
$ traceroute google.com | jc --traceroute -p
{
"destination_ip": "216.58.194.46",
"destination_name": "google.com",
"hops": [
{
"hop": 1,
"probes": [
{
"annotation": null,
"asn": null,
"ip": "216.230.231.141",
"name": "216-230-231-141.static.houston.tx.oplink.net",
"rtt": 198.574
},
{
"annotation": null,
"asn": null,
"ip": "216.230.231.141",
"name": "216-230-231-141.static.houston.tx.oplink.net",
"rtt": null
},
{
"annotation": null,
"asn": null,
"ip": "216.230.231.141",
"name": "216-230-231-141.static.houston.tx.oplink.net",
"rtt": 198.65
}
]
},
...
]
}
$ traceroute google.com | jc --traceroute -p -r
{
"destination_ip": "216.58.194.46",
"destination_name": "google.com",
"hops": [
{
"hop": "1",
"probes": [
{
"annotation": null,
"asn": null,
"ip": "216.230.231.141",
"name": "216-230-231-141.static.houston.tx.oplink.net",
"rtt": "198.574"
},
{
"annotation": null,
"asn": null,
"ip": "216.230.231.141",
"name": "216-230-231-141.static.houston.tx.oplink.net",
"rtt": null
},
{
"annotation": null,
"asn": null,
"ip": "216.230.231.141",
"name": "216-230-231-141.static.houston.tx.oplink.net",
"rtt": "198.650"
}
]
},
...
]
}
## info
```python
info()
```
## Hop
```python
Hop(idx)
```
## process
```python
process(proc_data)
```
Final processing to conform to the schema.
Parameters:
proc_data: (dictionary) raw structured data to process
Returns:
Dictionary. Structured data with the following schema:
{
"destination_ip": string,
"destination_name": string,
"hops": [
{
"hop": integer,
"probes": [
{
"annotation": string,
"asn": integer,
"ip": string,
"name": string,
"rtt": float
}
]
}
]
}
## parse
```python
parse(data, raw=False, quiet=False)
```
Main text parsing function
Parameters:
data: (string) text data to parse
raw: (boolean) output preprocessed JSON if True
quiet: (boolean) suppress warning messages if True
Returns:
Dictionary. Raw or processed structured data.

View File

@@ -1,3 +1,4 @@
# jc.parsers.uname
jc - JSON CLI output utility uname Parser
@@ -11,7 +12,7 @@ Limitations:
Compatibility:
'linux', 'darwin'
'linux', 'darwin', 'freebsd'
Example:
@@ -27,11 +28,13 @@ Example:
"kernel_version": "#74-Ubuntu SMP Tue Sep 17 17:06:04 UTC 2019"
}
## info
```python
info(self, /, *args, **kwargs)
info()
```
## process
```python
process(proc_data)
@@ -58,6 +61,7 @@ Returns:
"kernel_version": string
}
## parse
```python
parse(data, raw=False, quiet=False)

View File

@@ -1,3 +1,4 @@
# jc.parsers.uptime
jc - JSON CLI output utility uptime Parser
@@ -31,11 +32,13 @@ Example:
"load_15m": "0.05"
}
## info
```python
info(self, /, *args, **kwargs)
info()
```
## process
```python
process(proc_data)
@@ -60,6 +63,7 @@ Returns:
"load_15m": float
}
## parse
```python
parse(data, raw=False, quiet=False)

View File

@@ -1,3 +1,4 @@
# jc.parsers.w
jc - JSON CLI output utility w Parser
@@ -79,11 +80,13 @@ Examples:
}
]
## info
```python
info(self, /, *args, **kwargs)
info()
```
## process
```python
process(proc_data)
@@ -112,6 +115,7 @@ Returns:
}
]
## parse
```python
parse(data, raw=False, quiet=False)

156
docs/parsers/who.md Normal file
View File

@@ -0,0 +1,156 @@
# jc.parsers.who
jc - JSON CLI output utility who Parser
Usage:
specify --who as the first argument if the piped input is coming from who
accepts any of the following who options (or no options): -aTH
Compatibility:
'linux', 'darwin', 'cygwin', 'aix', 'freebsd'
Examples:
$ who -a | jc --who -p
[
{
"event": "reboot",
"time": "Feb 7 23:31",
"pid": 1
},
{
"user": "joeuser",
"writeable_tty": "-",
"tty": "console",
"time": "Feb 7 23:32",
"idle": "old",
"pid": 105
},
{
"user": "joeuser",
"writeable_tty": "+",
"tty": "ttys000",
"time": "Feb 13 16:44",
"idle": ".",
"pid": 51217,
"comment": "term=0 exit=0"
},
{
"user": "joeuser",
"writeable_tty": "?",
"tty": "ttys003",
"time": "Feb 28 08:59",
"idle": "01:36",
"pid": 41402
},
{
"user": "joeuser",
"writeable_tty": "+",
"tty": "ttys004",
"time": "Mar 1 16:35",
"idle": ".",
"pid": 15679,
"from": "192.168.1.5"
}
]
$ who -a | jc --who -p -r
[
{
"event": "reboot",
"time": "Feb 7 23:31",
"pid": "1"
},
{
"user": "joeuser",
"writeable_tty": "-",
"tty": "console",
"time": "Feb 7 23:32",
"idle": "old",
"pid": "105"
},
{
"user": "joeuser",
"writeable_tty": "+",
"tty": "ttys000",
"time": "Feb 13 16:44",
"idle": ".",
"pid": "51217",
"comment": "term=0 exit=0"
},
{
"user": "joeuser",
"writeable_tty": "?",
"tty": "ttys003",
"time": "Feb 28 08:59",
"idle": "01:36",
"pid": "41402"
},
{
"user": "joeuser",
"writeable_tty": "+",
"tty": "ttys004",
"time": "Mar 1 16:35",
"idle": ".",
"pid": "15679",
"from": "192.168.1.5"
}
]
## info
```python
info()
```
## process
```python
process(proc_data)
```
Final processing to conform to the schema.
Parameters:
proc_data: (dictionary) raw structured data to process
Returns:
List of dictionaries. Structured data with the following schema:
[
{
"user": string,
"event": string,
"writeable_tty": string,
"tty": string,
"time": string,
"idle": string,
"pid": integer,
"from": string,
"comment": string
}
]
## parse
```python
parse(data, raw=False, quiet=False)
```
Main text parsing function
Parameters:
data: (string) text data to parse
raw: (boolean) output preprocessed JSON if True
quiet: (boolean) suppress warning messages if True
Returns:
List of dictionaries. Raw or processed structured data.

View File

@@ -1,3 +1,4 @@
# jc.parsers.xml
jc - JSON CLI output utility XML Parser
@@ -55,11 +56,13 @@ Examples:
...
}
## info
```python
info(self, /, *args, **kwargs)
info()
```
## process
```python
process(proc_data)
@@ -80,6 +83,7 @@ Returns:
See https://github.com/martinblech/xmltodict for details
}
## parse
```python
parse(data, raw=False, quiet=False)

View File

@@ -1,3 +1,4 @@
# jc.parsers.yaml
jc - JSON CLI output utility YAML Parser
@@ -67,11 +68,13 @@ Examples:
}
]
## info
```python
info(self, /, *args, **kwargs)
info()
```
## process
```python
process(proc_data)
@@ -94,6 +97,7 @@ Returns:
}
]
## parse
```python
parse(data, raw=False, quiet=False)

View File

@@ -1,3 +1,4 @@
# jc
JC - JSON CLI output utility

View File

@@ -1,5 +1,7 @@
# utils
jc - JSON CLI output utility utils
## warning_message
```python
warning_message(message)
@@ -15,6 +17,7 @@ Returns:
no return, just prints output to STDERR
## error_message
```python
error_message(message)
@@ -30,6 +33,7 @@ Returns:
no return, just prints output to STDERR
## compatibility
```python
compatibility(mod_name, compatible)
@@ -48,3 +52,19 @@ Returns:
no return, just prints output to STDERR
## has_data
```python
has_data(data)
```
Checks if the input contains data. If there are any non-whitespace characters then return True, else return False
Parameters:
data: (string) input to check whether it contains data
Returns:
Boolean True if input string (data) contains non-whitespace characters, otherwise False

611
jc/appdirs.py Normal file
View File

@@ -0,0 +1,611 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2005-2010 ActiveState Software Inc.
# Copyright (c) 2013 Eddy Petrișor
'''
# This is the MIT license
Copyright (c) 2010 ActiveState Software Inc.
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.
'''
"""Utilities for determining application-specific dirs.
See <https://github.com/ActiveState/appdirs> for details and usage.
"""
# Dev Notes:
# - MSDN on where to store app data files:
# http://support.microsoft.com/default.aspx?scid=kb;en-us;310294#XSLTH3194121123120121120120
# - Mac OS X: http://developer.apple.com/documentation/MacOSX/Conceptual/BPFileSystem/index.html
# - XDG spec for Un*x: https://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html
__version__ = "1.4.4"
__version_info__ = tuple(int(segment) for segment in __version__.split("."))
import sys
import os
PY3 = sys.version_info[0] == 3
if PY3:
unicode = str
if sys.platform.startswith('java'):
import platform
os_name = platform.java_ver()[3][0]
if os_name.startswith('Windows'): # "Windows XP", "Windows 7", etc.
system = 'win32'
elif os_name.startswith('Mac'): # "Mac OS X", etc.
system = 'darwin'
else: # "Linux", "SunOS", "FreeBSD", etc.
# Setting this to "linux2" is not ideal, but only Windows or Mac
# are actually checked for and the rest of the module expects
# *sys.platform* style strings.
system = 'linux2'
else:
system = sys.platform
def user_data_dir(appname=None, appauthor=None, version=None, roaming=False):
r"""Return full path to the user-specific data dir for this application.
"appname" is the name of application.
If None, just the system directory is returned.
"appauthor" (only used on Windows) is the name of the
appauthor or distributing body for this application. Typically
it is the owning company name. This falls back to appname. You may
pass False to disable it.
"version" is an optional version path element to append to the
path. You might want to use this if you want multiple versions
of your app to be able to run independently. If used, this
would typically be "<major>.<minor>".
Only applied when appname is present.
"roaming" (boolean, default False) can be set True to use the Windows
roaming appdata directory. That means that for users on a Windows
network setup for roaming profiles, this user data will be
sync'd on login. See
<http://technet.microsoft.com/en-us/library/cc766489(WS.10).aspx>
for a discussion of issues.
Typical user data directories are:
Mac OS X: ~/Library/Application Support/<AppName>
Unix: ~/.local/share/<AppName> # or in $XDG_DATA_HOME, if defined
Win XP (not roaming): C:\Documents and Settings\<username>\Application Data\<AppAuthor>\<AppName>
Win XP (roaming): C:\Documents and Settings\<username>\Local Settings\Application Data\<AppAuthor>\<AppName>
Win 7 (not roaming): C:\Users\<username>\AppData\Local\<AppAuthor>\<AppName>
Win 7 (roaming): C:\Users\<username>\AppData\Roaming\<AppAuthor>\<AppName>
For Unix, we follow the XDG spec and support $XDG_DATA_HOME.
That means, by default "~/.local/share/<AppName>".
"""
if system == "win32":
if appauthor is None:
appauthor = appname
const = roaming and "CSIDL_APPDATA" or "CSIDL_LOCAL_APPDATA"
path = os.path.normpath(_get_win_folder(const))
if appname:
if appauthor is not False:
path = os.path.join(path, appauthor, appname)
else:
path = os.path.join(path, appname)
elif system == 'darwin':
path = os.path.expanduser('~/Library/Application Support/')
if appname:
path = os.path.join(path, appname)
else:
path = os.getenv('XDG_DATA_HOME', os.path.expanduser("~/.local/share"))
if appname:
path = os.path.join(path, appname)
if appname and version:
path = os.path.join(path, version)
return path
def site_data_dir(appname=None, appauthor=None, version=None, multipath=False):
r"""Return full path to the user-shared data dir for this application.
"appname" is the name of application.
If None, just the system directory is returned.
"appauthor" (only used on Windows) is the name of the
appauthor or distributing body for this application. Typically
it is the owning company name. This falls back to appname. You may
pass False to disable it.
"version" is an optional version path element to append to the
path. You might want to use this if you want multiple versions
of your app to be able to run independently. If used, this
would typically be "<major>.<minor>".
Only applied when appname is present.
"multipath" is an optional parameter only applicable to *nix
which indicates that the entire list of data dirs should be
returned. By default, the first item from XDG_DATA_DIRS is
returned, or '/usr/local/share/<AppName>',
if XDG_DATA_DIRS is not set
Typical site data directories are:
Mac OS X: /Library/Application Support/<AppName>
Unix: /usr/local/share/<AppName> or /usr/share/<AppName>
Win XP: C:\Documents and Settings\All Users\Application Data\<AppAuthor>\<AppName>
Vista: (Fail! "C:\ProgramData" is a hidden *system* directory on Vista.)
Win 7: C:\ProgramData\<AppAuthor>\<AppName> # Hidden, but writeable on Win 7.
For Unix, this is using the $XDG_DATA_DIRS[0] default.
WARNING: Do not use this on Windows. See the Vista-Fail note above for why.
"""
if system == "win32":
if appauthor is None:
appauthor = appname
path = os.path.normpath(_get_win_folder("CSIDL_COMMON_APPDATA"))
if appname:
if appauthor is not False:
path = os.path.join(path, appauthor, appname)
else:
path = os.path.join(path, appname)
elif system == 'darwin':
path = os.path.expanduser('/Library/Application Support')
if appname:
path = os.path.join(path, appname)
else:
# XDG default for $XDG_DATA_DIRS
# only first, if multipath is False
path = os.getenv('XDG_DATA_DIRS',
os.pathsep.join(['/usr/local/share', '/usr/share']))
pathlist = [os.path.expanduser(x.rstrip(os.sep)) for x in path.split(os.pathsep)]
if appname:
if version:
appname = os.path.join(appname, version)
pathlist = [os.sep.join([x, appname]) for x in pathlist]
if multipath:
path = os.pathsep.join(pathlist)
else:
path = pathlist[0]
return path
if appname and version:
path = os.path.join(path, version)
return path
def user_config_dir(appname=None, appauthor=None, version=None, roaming=False):
r"""Return full path to the user-specific config dir for this application.
"appname" is the name of application.
If None, just the system directory is returned.
"appauthor" (only used on Windows) is the name of the
appauthor or distributing body for this application. Typically
it is the owning company name. This falls back to appname. You may
pass False to disable it.
"version" is an optional version path element to append to the
path. You might want to use this if you want multiple versions
of your app to be able to run independently. If used, this
would typically be "<major>.<minor>".
Only applied when appname is present.
"roaming" (boolean, default False) can be set True to use the Windows
roaming appdata directory. That means that for users on a Windows
network setup for roaming profiles, this user data will be
sync'd on login. See
<http://technet.microsoft.com/en-us/library/cc766489(WS.10).aspx>
for a discussion of issues.
Typical user config directories are:
Mac OS X: ~/Library/Preferences/<AppName>
Unix: ~/.config/<AppName> # or in $XDG_CONFIG_HOME, if defined
Win *: same as user_data_dir
For Unix, we follow the XDG spec and support $XDG_CONFIG_HOME.
That means, by default "~/.config/<AppName>".
"""
if system == "win32":
path = user_data_dir(appname, appauthor, None, roaming)
elif system == 'darwin':
path = os.path.expanduser('~/Library/Preferences/')
if appname:
path = os.path.join(path, appname)
else:
path = os.getenv('XDG_CONFIG_HOME', os.path.expanduser("~/.config"))
if appname:
path = os.path.join(path, appname)
if appname and version:
path = os.path.join(path, version)
return path
def site_config_dir(appname=None, appauthor=None, version=None, multipath=False):
r"""Return full path to the user-shared data dir for this application.
"appname" is the name of application.
If None, just the system directory is returned.
"appauthor" (only used on Windows) is the name of the
appauthor or distributing body for this application. Typically
it is the owning company name. This falls back to appname. You may
pass False to disable it.
"version" is an optional version path element to append to the
path. You might want to use this if you want multiple versions
of your app to be able to run independently. If used, this
would typically be "<major>.<minor>".
Only applied when appname is present.
"multipath" is an optional parameter only applicable to *nix
which indicates that the entire list of config dirs should be
returned. By default, the first item from XDG_CONFIG_DIRS is
returned, or '/etc/xdg/<AppName>', if XDG_CONFIG_DIRS is not set
Typical site config directories are:
Mac OS X: same as site_data_dir
Unix: /etc/xdg/<AppName> or $XDG_CONFIG_DIRS[i]/<AppName> for each value in
$XDG_CONFIG_DIRS
Win *: same as site_data_dir
Vista: (Fail! "C:\ProgramData" is a hidden *system* directory on Vista.)
For Unix, this is using the $XDG_CONFIG_DIRS[0] default, if multipath=False
WARNING: Do not use this on Windows. See the Vista-Fail note above for why.
"""
if system == 'win32':
path = site_data_dir(appname, appauthor)
if appname and version:
path = os.path.join(path, version)
elif system == 'darwin':
path = os.path.expanduser('/Library/Preferences')
if appname:
path = os.path.join(path, appname)
else:
# XDG default for $XDG_CONFIG_DIRS
# only first, if multipath is False
path = os.getenv('XDG_CONFIG_DIRS', '/etc/xdg')
pathlist = [os.path.expanduser(x.rstrip(os.sep)) for x in path.split(os.pathsep)]
if appname:
if version:
appname = os.path.join(appname, version)
pathlist = [os.sep.join([x, appname]) for x in pathlist]
if multipath:
path = os.pathsep.join(pathlist)
else:
path = pathlist[0]
return path
def user_cache_dir(appname=None, appauthor=None, version=None, opinion=True):
r"""Return full path to the user-specific cache dir for this application.
"appname" is the name of application.
If None, just the system directory is returned.
"appauthor" (only used on Windows) is the name of the
appauthor or distributing body for this application. Typically
it is the owning company name. This falls back to appname. You may
pass False to disable it.
"version" is an optional version path element to append to the
path. You might want to use this if you want multiple versions
of your app to be able to run independently. If used, this
would typically be "<major>.<minor>".
Only applied when appname is present.
"opinion" (boolean) can be False to disable the appending of
"Cache" to the base app data dir for Windows. See
discussion below.
Typical user cache directories are:
Mac OS X: ~/Library/Caches/<AppName>
Unix: ~/.cache/<AppName> (XDG default)
Win XP: C:\Documents and Settings\<username>\Local Settings\Application Data\<AppAuthor>\<AppName>\Cache
Vista: C:\Users\<username>\AppData\Local\<AppAuthor>\<AppName>\Cache
On Windows the only suggestion in the MSDN docs is that local settings go in
the `CSIDL_LOCAL_APPDATA` directory. This is identical to the non-roaming
app data dir (the default returned by `user_data_dir` above). Apps typically
put cache data somewhere *under* the given dir here. Some examples:
...\Mozilla\Firefox\Profiles\<ProfileName>\Cache
...\Acme\SuperApp\Cache\1.0
OPINION: This function appends "Cache" to the `CSIDL_LOCAL_APPDATA` value.
This can be disabled with the `opinion=False` option.
"""
if system == "win32":
if appauthor is None:
appauthor = appname
path = os.path.normpath(_get_win_folder("CSIDL_LOCAL_APPDATA"))
if appname:
if appauthor is not False:
path = os.path.join(path, appauthor, appname)
else:
path = os.path.join(path, appname)
if opinion:
path = os.path.join(path, "Cache")
elif system == 'darwin':
path = os.path.expanduser('~/Library/Caches')
if appname:
path = os.path.join(path, appname)
else:
path = os.getenv('XDG_CACHE_HOME', os.path.expanduser('~/.cache'))
if appname:
path = os.path.join(path, appname)
if appname and version:
path = os.path.join(path, version)
return path
def user_state_dir(appname=None, appauthor=None, version=None, roaming=False):
r"""Return full path to the user-specific state dir for this application.
"appname" is the name of application.
If None, just the system directory is returned.
"appauthor" (only used on Windows) is the name of the
appauthor or distributing body for this application. Typically
it is the owning company name. This falls back to appname. You may
pass False to disable it.
"version" is an optional version path element to append to the
path. You might want to use this if you want multiple versions
of your app to be able to run independently. If used, this
would typically be "<major>.<minor>".
Only applied when appname is present.
"roaming" (boolean, default False) can be set True to use the Windows
roaming appdata directory. That means that for users on a Windows
network setup for roaming profiles, this user data will be
sync'd on login. See
<http://technet.microsoft.com/en-us/library/cc766489(WS.10).aspx>
for a discussion of issues.
Typical user state directories are:
Mac OS X: same as user_data_dir
Unix: ~/.local/state/<AppName> # or in $XDG_STATE_HOME, if defined
Win *: same as user_data_dir
For Unix, we follow this Debian proposal <https://wiki.debian.org/XDGBaseDirectorySpecification#state>
to extend the XDG spec and support $XDG_STATE_HOME.
That means, by default "~/.local/state/<AppName>".
"""
if system in ["win32", "darwin"]:
path = user_data_dir(appname, appauthor, None, roaming)
else:
path = os.getenv('XDG_STATE_HOME', os.path.expanduser("~/.local/state"))
if appname:
path = os.path.join(path, appname)
if appname and version:
path = os.path.join(path, version)
return path
def user_log_dir(appname=None, appauthor=None, version=None, opinion=True):
r"""Return full path to the user-specific log dir for this application.
"appname" is the name of application.
If None, just the system directory is returned.
"appauthor" (only used on Windows) is the name of the
appauthor or distributing body for this application. Typically
it is the owning company name. This falls back to appname. You may
pass False to disable it.
"version" is an optional version path element to append to the
path. You might want to use this if you want multiple versions
of your app to be able to run independently. If used, this
would typically be "<major>.<minor>".
Only applied when appname is present.
"opinion" (boolean) can be False to disable the appending of
"Logs" to the base app data dir for Windows, and "log" to the
base cache dir for Unix. See discussion below.
Typical user log directories are:
Mac OS X: ~/Library/Logs/<AppName>
Unix: ~/.cache/<AppName>/log # or under $XDG_CACHE_HOME if defined
Win XP: C:\Documents and Settings\<username>\Local Settings\Application Data\<AppAuthor>\<AppName>\Logs
Vista: C:\Users\<username>\AppData\Local\<AppAuthor>\<AppName>\Logs
On Windows the only suggestion in the MSDN docs is that local settings
go in the `CSIDL_LOCAL_APPDATA` directory. (Note: I'm interested in
examples of what some windows apps use for a logs dir.)
OPINION: This function appends "Logs" to the `CSIDL_LOCAL_APPDATA`
value for Windows and appends "log" to the user cache dir for Unix.
This can be disabled with the `opinion=False` option.
"""
if system == "darwin":
path = os.path.join(
os.path.expanduser('~/Library/Logs'),
appname)
elif system == "win32":
path = user_data_dir(appname, appauthor, version)
version = False
if opinion:
path = os.path.join(path, "Logs")
else:
path = user_cache_dir(appname, appauthor, version)
version = False
if opinion:
path = os.path.join(path, "log")
if appname and version:
path = os.path.join(path, version)
return path
class AppDirs(object):
"""Convenience wrapper for getting application dirs."""
def __init__(self, appname=None, appauthor=None, version=None,
roaming=False, multipath=False):
self.appname = appname
self.appauthor = appauthor
self.version = version
self.roaming = roaming
self.multipath = multipath
@property
def user_data_dir(self):
return user_data_dir(self.appname, self.appauthor,
version=self.version, roaming=self.roaming)
@property
def site_data_dir(self):
return site_data_dir(self.appname, self.appauthor,
version=self.version, multipath=self.multipath)
@property
def user_config_dir(self):
return user_config_dir(self.appname, self.appauthor,
version=self.version, roaming=self.roaming)
@property
def site_config_dir(self):
return site_config_dir(self.appname, self.appauthor,
version=self.version, multipath=self.multipath)
@property
def user_cache_dir(self):
return user_cache_dir(self.appname, self.appauthor,
version=self.version)
@property
def user_state_dir(self):
return user_state_dir(self.appname, self.appauthor,
version=self.version)
@property
def user_log_dir(self):
return user_log_dir(self.appname, self.appauthor,
version=self.version)
#---- internal support stuff
def _get_win_folder_from_registry(csidl_name):
"""This is a fallback technique at best. I'm not sure if using the
registry for this guarantees us the correct answer for all CSIDL_*
names.
"""
if PY3:
import winreg as _winreg
else:
import _winreg
shell_folder_name = {
"CSIDL_APPDATA": "AppData",
"CSIDL_COMMON_APPDATA": "Common AppData",
"CSIDL_LOCAL_APPDATA": "Local AppData",
}[csidl_name]
key = _winreg.OpenKey(
_winreg.HKEY_CURRENT_USER,
r"Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders"
)
dir, type = _winreg.QueryValueEx(key, shell_folder_name)
return dir
def _get_win_folder_with_ctypes(csidl_name):
import ctypes
csidl_const = {
"CSIDL_APPDATA": 26,
"CSIDL_COMMON_APPDATA": 35,
"CSIDL_LOCAL_APPDATA": 28,
}[csidl_name]
buf = ctypes.create_unicode_buffer(1024)
ctypes.windll.shell32.SHGetFolderPathW(None, csidl_const, None, 0, buf)
# Downgrade to short path name if have highbit chars. See
# <http://bugs.activestate.com/show_bug.cgi?id=85099>.
has_high_char = False
for c in buf:
if ord(c) > 255:
has_high_char = True
break
if has_high_char:
buf2 = ctypes.create_unicode_buffer(1024)
if ctypes.windll.kernel32.GetShortPathNameW(buf.value, buf2, 1024):
buf = buf2
return buf.value
def _get_win_folder_with_jna(csidl_name):
import array
from com.sun import jna
from com.sun.jna.platform import win32
buf_size = win32.WinDef.MAX_PATH * 2
buf = array.zeros('c', buf_size)
shell = win32.Shell32.INSTANCE
shell.SHGetFolderPath(None, getattr(win32.ShlObj, csidl_name), None, win32.ShlObj.SHGFP_TYPE_CURRENT, buf)
dir = jna.Native.toString(buf.tostring()).rstrip("\0")
# Downgrade to short path name if have highbit chars. See
# <http://bugs.activestate.com/show_bug.cgi?id=85099>.
has_high_char = False
for c in dir:
if ord(c) > 255:
has_high_char = True
break
if has_high_char:
buf = array.zeros('c', buf_size)
kernel = win32.Kernel32.INSTANCE
if kernel.GetShortPathName(dir, buf, buf_size):
dir = jna.Native.toString(buf.tostring()).rstrip("\0")
return dir
if system == "win32":
try:
from ctypes import windll
except ImportError:
try:
import com.sun.jna
except ImportError:
_get_win_folder = _get_win_folder_from_registry
else:
_get_win_folder = _get_win_folder_with_jna
else:
_get_win_folder = _get_win_folder_with_ctypes
#---- self test code
if __name__ == "__main__":
appname = "MyApp"
appauthor = "MyCompany"
props = ("user_data_dir",
"user_config_dir",
"user_cache_dir",
"user_state_dir",
"user_log_dir",
"site_data_dir",
"site_config_dir")
print("-- app dirs %s --" % __version__)
print("-- app dirs (with optional 'version')")
dirs = AppDirs(appname, appauthor, version="1.0")
for prop in props:
print("%s: %s" % (prop, getattr(dirs, prop)))
print("\n-- app dirs (without optional 'version')")
dirs = AppDirs(appname, appauthor)
for prop in props:
print("%s: %s" % (prop, getattr(dirs, prop)))
print("\n-- app dirs (without optional 'appauthor')")
dirs = AppDirs(appname)
for prop in props:
print("%s: %s" % (prop, getattr(dirs, prop)))
print("\n-- app dirs (with disabled 'appauthor')")
dirs = AppDirs(appname, appauthor=False)
for prop in props:
print("%s: %s" % (prop, getattr(dirs, prop)))

399
jc/cli.py
View File

@@ -1,20 +1,28 @@
#!/usr/bin/env python3
"""jc - JSON CLI output utility
JC cli module
"""
import sys
import os
import os.path
import re
import shlex
import importlib
import textwrap
import signal
import json
import jc.utils
import pygments
from pygments import highlight
from pygments.style import Style
from pygments.token import (Name, Number, String, Keyword)
from pygments.lexers import JsonLexer
from pygments.formatters import Terminal256Formatter
import jc.appdirs as appdirs
class info():
version = '1.7.4'
description = 'jc cli output JSON conversion tool'
version = '1.13.4'
description = 'JSON CLI output utility'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
@@ -22,15 +30,24 @@ class info():
__version__ = info.version
parsers = [
'airport',
'airport-s',
'arp',
'blkid',
'crontab',
'crontab-u',
'csv',
'date',
'df',
'dig',
'dmidecode',
'du',
'env',
'file',
'free',
'fstab',
'group',
'gshadow',
'history',
'hosts',
'id',
@@ -38,58 +55,181 @@ parsers = [
'ini',
'iptables',
'jobs',
'kv',
'last',
'ls',
'lsblk',
'lsmod',
'lsof',
'mount',
'netstat',
'ntpq',
'passwd',
'ping',
'pip-list',
'pip-show',
'ps',
'route',
'shadow',
'ss',
'stat',
'sysctl',
'systemctl',
'systemctl-lj',
'systemctl-ls',
'systemctl-luf',
'timedatectl',
'tracepath',
'traceroute',
'uname',
'uptime',
'w',
'who',
'xml',
'yaml'
]
# List of custom or override parsers.
# Allow any <user_data_dir>/jc/jcparsers/*.py
local_parsers = []
data_dir = appdirs.user_data_dir('jc', 'jc')
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)):
plugin_name = name[0:-3]
local_parsers.append(plugin_name)
if plugin_name not in parsers:
parsers.append(plugin_name)
# We only support 2.3.0+, pygments changed color names in 2.4.0.
# startswith is sufficient and avoids potential exceptions from split and int.
if pygments.__version__.startswith('2.3.'):
PYGMENT_COLOR = {
'black': '#ansiblack',
'red': '#ansidarkred',
'green': '#ansidarkgreen',
'yellow': '#ansibrown',
'blue': '#ansidarkblue',
'magenta': '#ansipurple',
'cyan': '#ansiteal',
'gray': '#ansilightgray',
'brightblack': '#ansidarkgray',
'brightred': '#ansired',
'brightgreen': '#ansigreen',
'brightyellow': '#ansiyellow',
'brightblue': '#ansiblue',
'brightmagenta': '#ansifuchsia',
'brightcyan': '#ansiturquoise',
'white': '#ansiwhite',
}
else:
PYGMENT_COLOR = {
'black': 'ansiblack',
'red': 'ansired',
'green': 'ansigreen',
'yellow': 'ansiyellow',
'blue': 'ansiblue',
'magenta': 'ansimagenta',
'cyan': 'ansicyan',
'gray': 'ansigray',
'brightblack': 'ansibrightblack',
'brightred': 'ansibrightred',
'brightgreen': 'ansibrightgreen',
'brightyellow': 'ansibrightyellow',
'brightblue': 'ansibrightblue',
'brightmagenta': 'ansibrightmagenta',
'brightcyan': 'ansibrightcyan',
'white': 'ansiwhite',
}
def set_env_colors(env_colors=None):
"""
Return a dictionary to be used in Pygments custom style class.
Grab custom colors from JC_COLORS environment variable. JC_COLORS env variable takes 4 comma
separated string values and should be in the format of:
JC_COLORS=<keyname_color>,<keyword_color>,<number_color>,<string_color>
Where colors are: black, red, green, yellow, blue, magenta, cyan, gray, brightblack, brightred,
brightgreen, brightyellow, brightblue, brightmagenta, brightcyan, white, default
Default colors:
JC_COLORS=blue,brightblack,magenta,green
or
JC_COLORS=default,default,default,default
"""
input_error = False
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:
input_error = True
# if there is an issue with the env variable, just set all colors to default and move on
if input_error:
print('jc: Warning: could not parse JC_COLORS environment variable\n', file=sys.stderr)
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
return {
Name.Tag: f'bold {PYGMENT_COLOR[color_list[0]]}' if not color_list[0] == 'default' else f"bold {PYGMENT_COLOR['blue']}", # key names
Keyword: PYGMENT_COLOR[color_list[1]] if not color_list[1] == 'default' else PYGMENT_COLOR['brightblack'], # true, false, null
Number: PYGMENT_COLOR[color_list[2]] if not color_list[2] == 'default' else PYGMENT_COLOR['magenta'], # numbers
String: PYGMENT_COLOR[color_list[3]] if not color_list[3] == 'default' else PYGMENT_COLOR['green'] # strings
}
def piped_output():
"""Return False if stdout is a TTY. True if output is being piped to another program"""
if sys.stdout.isatty():
return False
else:
return True
def ctrlc(signum, frame):
"""exit with error on SIGINT"""
"""Exit with error on SIGINT"""
sys.exit(1)
def parser_shortname(parser_argument):
"""short name of the parser with dashes and no -- prefix"""
"""Return short name of the parser with dashes and no -- prefix"""
return parser_argument[2:]
def parser_argument(parser):
"""short name of the parser with dashes and with -- prefix"""
"""Return short name of the parser with dashes and with -- prefix"""
return f'--{parser}'
def parser_mod_shortname(parser):
"""short name of the parser's module name (no -- prefix and dashes converted to underscores)"""
"""Return short name of the parser's module name (no -- prefix and dashes converted to underscores)"""
return parser.replace('--', '').replace('-', '_')
def parser_module(parser):
"""import the module just in time and return the module object"""
importlib.import_module('jc.parsers.' + parser_mod_shortname(parser))
return getattr(jc.parsers, parser_mod_shortname(parser))
"""Import the module just in time and return the module object"""
shortname = parser_mod_shortname(parser)
path = ('jcparsers.' if shortname in local_parsers else 'jc.parsers.')
return importlib.import_module(path + shortname)
def parsers_text(indent=0, pad=0):
"""return the argument and description information from each parser"""
"""Return the argument and description information from each parser"""
ptext = ''
for parser in parsers:
parser_arg = parser_argument(parser)
@@ -107,7 +247,7 @@ def parsers_text(indent=0, pad=0):
def about_jc():
"""return jc info and the contents of each parser.info as a dictionary"""
"""Return jc info and the contents of each parser.info as a dictionary"""
parser_list = []
for parser in parsers:
@@ -137,7 +277,7 @@ def about_jc():
def helptext(message):
"""return the help text with the list of parsers"""
"""Return the help text with the list of parsers"""
parsers_string = parsers_text(indent=12, pad=17)
helptext_string = f'''
@@ -145,10 +285,6 @@ def helptext(message):
Usage: COMMAND | jc PARSER [OPTIONS]
or
COMMAND | jc [OPTIONS] PARSER
or magic syntax:
jc [OPTIONS] COMMAND
@@ -157,7 +293,8 @@ def helptext(message):
{parsers_string}
Options:
-a about jc
-d debug - show trace messages
-d debug - show traceback (-dd for verbose traceback)
-m monochrome output
-p pretty print output
-q quiet - suppress warnings
-r raw JSON output
@@ -169,153 +306,167 @@ def helptext(message):
jc -p ls -al
'''
print(textwrap.dedent(helptext_string), file=sys.stderr)
return textwrap.dedent(helptext_string)
def json_out(data, pretty=False):
if pretty:
print(json.dumps(data, indent=2))
def json_out(data, pretty=False, env_colors=None, mono=False, piped_out=False):
"""Return a JSON formatted string. String may include color codes or be pretty printed."""
if not mono and not piped_out:
# set colors
class JcStyle(Style):
styles = set_env_colors(env_colors)
if pretty:
return str(highlight(json.dumps(data, indent=2), JsonLexer(), Terminal256Formatter(style=JcStyle))[0:-1])
else:
return str(highlight(json.dumps(data), JsonLexer(), Terminal256Formatter(style=JcStyle))[0:-1])
else:
print(json.dumps(data))
if pretty:
return json.dumps(data, indent=2)
else:
return json.dumps(data)
def generate_magic_command(args):
"""
Return a tuple with a boolean and a command, where the boolean signifies that
the command is valid, and the command is either a command string or None.
"""
# Parse with magic syntax: jc -p ls -al
if len(args) <= 1 or args[1].startswith('--'):
return False, None
# correctly parse escape characters and spaces with shlex
args_given = ' '.join(map(shlex.quote, args[1:])).split()
options = []
# find the options
for arg in list(args_given):
# parser found - use standard syntax
if arg.startswith('--'):
return False, None
# option found - populate option list
elif arg.startswith('-'):
options.extend(args_given.pop(0)[1:])
# command found if iterator didn't already stop - stop iterating
else:
break
# all options popped and no command found - for case like 'jc -a'
if len(args_given) == 0:
return False, None
magic_dict = {}
parser_info = about_jc()['parsers']
# create a dictionary of magic_commands to their respective parsers.
for entry in parser_info:
# Update the dict with all of the magic commands for this parser, if they exist.
magic_dict.update({mc: entry['argument'] for mc in entry.get('magic_commands', [])})
# find the command and parser
one_word_command = args_given[0]
two_word_command = ' '.join(args_given[0:2])
# try to get a parser for two_word_command, otherwise get one for one_word_command
found_parser = magic_dict.get(two_word_command, magic_dict.get(one_word_command))
# construct a new command line using the standard syntax: COMMAND | jc --PARSER -OPTIONS
run_command = ' '.join(args_given)
if found_parser:
cmd_options = ('-' + ''.join(options)) if options else ''
return True, ' '.join([run_command, '|', 'jc', found_parser, cmd_options])
else:
return False, run_command
def magic():
"""Parse with magic syntax: jc -p ls -al"""
if len(sys.argv) > 1 and not sys.argv[1].startswith('--'):
parser_info = about_jc()['parsers']
# correctly parse escape characters and spaces with shlex
args_given = " ".join(map(shlex.quote, sys.argv[1:])).split()
options = []
found_parser = None
# find the options
if args_given[0].startswith('-'):
p = 0
for i, arg in list(enumerate(args_given)):
# parser found - use standard syntax
if arg.startswith('--'):
return
# option found - populate option list
elif arg.startswith('-'):
options.append(args_given.pop(i - p)[1:])
p = p + 1
# command found if iterator didn't already stop - stop iterating
else:
break
# find the command and parser
for parser in parser_info:
if 'magic_commands' in parser:
# first pass for two word commands: e.g. 'pip list'
for magic_command in parser['magic_commands']:
try:
if ' '.join(args_given[0:2]) == magic_command:
found_parser = parser['argument']
break
# No command found - go to next loop (for cases like 'jc -a')
except Exception:
break
# second pass for one word commands: e.g. 'ls'
if not found_parser:
for magic_command in parser['magic_commands']:
try:
if args_given[0] == magic_command:
found_parser = parser['argument']
break
# No command found - use standard syntax (for cases like 'jc -a')
except Exception:
return
# construct a new command line using the standard syntax: COMMAND | jc --PARSER -OPTIONS
run_command = ' '.join(args_given)
if found_parser:
if options:
cmd_options = '-' + ''.join(options)
else:
cmd_options = ''
whole_command = ' '.join([run_command, '|', 'jc', found_parser, cmd_options])
os.system(whole_command)
exit()
else:
helptext(f'parser not found for "{run_command}"')
sys.exit(1)
"""Runs the command generated by generate_magic_command() to support magic syntax"""
valid_command, run_command = generate_magic_command(sys.argv)
if valid_command:
os.system(run_command)
sys.exit(0)
elif run_command is None:
return
else:
print(helptext(f'parser not found for "{run_command}"'), file=sys.stderr)
sys.exit(1)
def main():
# break on ctrl-c keyboard interrupt
signal.signal(signal.SIGINT, ctrlc)
# break on pipe error. need try/except for windows compatibility
try:
signal.signal(signal.SIGPIPE, signal.SIG_DFL)
except AttributeError:
pass
jc_colors = os.getenv('JC_COLORS')
# try magic syntax first: e.g. jc -p ls -al
magic()
options = []
debug = False
pretty = False
quiet = False
raw = False
# options
for opt in sys.argv:
if opt.startswith('-') and not opt.startswith('--'):
for flag in opt[1:]:
options.append(flag)
options.extend(opt[1:])
if 'd' in options:
debug = True
debug = 'd' in options
verbose_debug = True if options.count('d') > 1 else False
mono = 'm' in options
pretty = 'p' in options
quiet = 'q' in options
raw = 'r' in options
if 'p' in options:
pretty = True
if 'q' in options:
quiet = True
if 'r' in options:
raw = True
if verbose_debug:
import jc.tracebackplus
jc.tracebackplus.enable(context=11)
if 'a' in options:
json_out(about_jc(), pretty=pretty)
exit()
print(json_out(about_jc(), pretty=pretty, env_colors=jc_colors, mono=mono, piped_out=piped_output()))
sys.exit(0)
if sys.stdin.isatty():
helptext('missing piped data')
print(helptext('missing piped data'), file=sys.stderr)
sys.exit(1)
data = sys.stdin.read()
found = False
if debug:
for arg in sys.argv:
parser_name = parser_shortname(arg)
for arg in sys.argv:
parser_name = parser_shortname(arg)
if parser_name in parsers:
# load parser module just in time so we don't need to load all modules
parser = parser_module(arg)
if parser_name in parsers:
# load parser module just in time so we don't need to load all modules
parser = parser_module(arg)
try:
result = parser.parse(data, raw=raw, quiet=quiet)
found = True
break
else:
for arg in sys.argv:
parser_name = parser_shortname(arg)
if parser_name in parsers:
# load parser module just in time so we don't need to load all modules
parser = parser_module(arg)
try:
result = parser.parse(data, raw=raw, quiet=quiet)
found = True
break
except Exception:
jc.utils.error_message(f'{parser_name} parser could not parse the input data. Did you use the correct parser?\n For details use the -d option.')
except Exception:
if debug:
raise
else:
import jc.utils
jc.utils.error_message(
f'{parser_name} parser could not parse the input data. Did you use the correct parser?\n'
' For details use the -d or -dd option.')
sys.exit(1)
if not found:
helptext('missing or incorrect arguments')
print(helptext('missing or incorrect arguments'), file=sys.stderr)
sys.exit(1)
json_out(result, pretty=pretty)
print(json_out(result, pretty=pretty, env_colors=jc_colors, mono=mono, piped_out=piped_output()))
if __name__ == '__main__':

143
jc/parsers/airport.py Normal file
View File

@@ -0,0 +1,143 @@
"""jc - JSON CLI output utility airport -I Parser
Usage:
specify --airport as the first argument if the piped input is coming from airport -I (OSX)
This program can be found at:
/System/Library/PrivateFrameworks/Apple80211.framework/Versions/Current/Resources/airport
Compatibility:
'darwin'
Examples:
$ airport -I | jc --airport -p
{
"agrctlrssi": -66,
"agrextrssi": 0,
"agrctlnoise": -90,
"agrextnoise": 0,
"state": "running",
"op_mode": "station",
"lasttxrate": 195,
"maxrate": 867,
"lastassocstatus": 0,
"802_11_auth": "open",
"link_auth": "wpa2-psk",
"bssid": "3c:37:86:15:ad:f9",
"ssid": "SnazzleDazzle",
"mcs": 0,
"channel": "48,80"
}
$ airport -I | jc --airport -p -r
{
"agrctlrssi": "-66",
"agrextrssi": "0",
"agrctlnoise": "-90",
"agrextnoise": "0",
"state": "running",
"op_mode": "station",
"lasttxrate": "195",
"maxrate": "867",
"lastassocstatus": "0",
"802_11_auth": "open",
"link_auth": "wpa2-psk",
"bssid": "3c:37:86:15:ad:f9",
"ssid": "SnazzleDazzle",
"mcs": "0",
"channel": "48,80"
}
"""
import jc.utils
class info():
version = '1.1'
description = 'airport -I command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
# details = 'enter any other details here'
# compatible options: linux, darwin, cygwin, win32, aix, freebsd
compatible = ['darwin']
magic_commands = ['airport -I']
__version__ = info.version
def process(proc_data):
"""
Final processing to conform to the schema.
Parameters:
proc_data: (dictionary) raw structured data to process
Returns:
Dictionary. Structured data with the following schema:
{
"agrctlrssi": integer,
"agrextrssi": integer,
"agrctlnoise": integer,
"agrextnoise": integer,
"state": string,
"op_mode": string,
"lasttxrate": integer,
"maxrate": integer,
"lastassocstatus": integer,
"802_11_auth": string,
"link_auth": string,
"bssid": string,
"ssid": string,
"mcs": integer,
"channel": string
}
"""
# integer changes
int_list = ['agrctlrssi', 'agrextrssi', 'agrctlnoise', 'agrextnoise',
'lasttxrate', 'maxrate', 'lastassocstatus', 'mcs']
for key in proc_data:
if key in int_list:
try:
proc_data[key] = int(proc_data[key])
except (ValueError):
proc_data[key] = None
return proc_data
def parse(data, raw=False, quiet=False):
"""
Main text parsing function
Parameters:
data: (string) text data to parse
raw: (boolean) output preprocessed JSON if True
quiet: (boolean) suppress warning messages if True
Returns:
Dictionary. Raw or processed structured data.
"""
if not quiet:
jc.utils.compatibility(__name__, info.compatible)
raw_output = {}
if jc.utils.has_data(data):
for line in filter(None, data.splitlines()):
linedata = line.split(':', maxsplit=1)
raw_output[linedata[0].strip().lower().replace(' ', '_').replace('.', '_')] = linedata[1].strip()
if raw:
return raw_output
else:
return process(raw_output)

188
jc/parsers/airport_s.py Normal file
View File

@@ -0,0 +1,188 @@
"""jc - JSON CLI output utility airport -s Parser
Usage:
specify --airport as the first argument if the piped input is coming from airport -s (OSX)
This program can be found at:
/System/Library/PrivateFrameworks/Apple80211.framework/Versions/Current/Resources/airport
Compatibility:
'darwin'
Examples:
$ airport -s | jc --airport-s -p
[
{
"ssid": "DIRECT-4A-HP OfficeJet 3830",
"bssid": "00:67:eb:2a:a7:3b",
"rssi": -90,
"channel": "6",
"ht": true,
"cc": "--",
"security": [
"WPA2(PSK/AES/AES)"
]
},
{
"ssid": "Latitude38",
"bssid": "c0:ff:d5:d2:7a:f3",
"rssi": -85,
"channel": "11",
"ht": true,
"cc": "US",
"security": [
"WPA2(PSK/AES/AES)"
]
},
{
"ssid": "xfinitywifi",
"bssid": "6e:e3:0e:b8:45:99",
"rssi": -83,
"channel": "11",
"ht": true,
"cc": "US",
"security": [
"NONE"
]
},
...
]
$ airport -s | jc --airport -p -r
[
{
"ssid": "DIRECT-F3-HP ENVY 5660 series",
"bssid": "b0:5a:da:6f:0a:d4",
"rssi": "-93",
"channel": "1",
"ht": "Y",
"cc": "--",
"security": "WPA2(PSK/AES/AES)"
},
{
"ssid": "YouAreInfected-5",
"bssid": "5c:e3:0e:c2:85:da",
"rssi": "-85",
"channel": "36",
"ht": "Y",
"cc": "US",
"security": "WPA(PSK/AES,TKIP/TKIP) WPA2(PSK/AES,TKIP/TKIP)"
},
{
"ssid": "YuanFamily",
"bssid": "5c:e3:0e:b8:5f:9a",
"rssi": "-84",
"channel": "11",
"ht": "Y",
"cc": "US",
"security": "WPA(PSK/AES,TKIP/TKIP) WPA2(PSK/AES,TKIP/TKIP)"
},
...
]
"""
import jc.utils
import jc.parsers.universal
class info():
version = '1.2'
description = 'airport -s command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
# details = 'enter any other details here'
# compatible options: linux, darwin, cygwin, win32, aix, freebsd
compatible = ['darwin']
magic_commands = ['airport -s']
__version__ = info.version
def process(proc_data):
"""
Final processing to conform to the schema.
Parameters:
proc_data: (dictionary) raw structured data to process
Returns:
List of dictionaries. Structured data with the following schema:
[
{
"ssid": string,
"bssid": string,
"rssi": integer,
"channel": string,
"ht": boolean,
"cc": string,
"security": [
string,
]
}
]
"""
for entry in proc_data:
# integers
int_list = ['rssi']
for key in int_list:
if key in entry:
try:
entry[key] = int(entry[key])
except (ValueError):
entry[key] = None
# booleans
bool_list = ['ht']
for key in entry:
if key in bool_list:
try:
entry[key] = True if entry[key] == 'Y' else False
except (ValueError):
entry[key] = None
if 'security' in entry:
entry['security'] = entry['security'].split()
return proc_data
def parse(data, raw=False, quiet=False):
"""
Main text parsing function
Parameters:
data: (string) text data to parse
raw: (boolean) output preprocessed JSON if True
quiet: (boolean) suppress warning messages if True
Returns:
List of dictionaries. Raw or processed structured data.
"""
if not quiet:
jc.utils.compatibility(__name__, info.compatible)
raw_output = []
cleandata = list(filter(None, data.splitlines()))
if jc.utils.has_data(data):
# fix headers
cleandata[0] = cleandata[0].lower()
cleandata[0] = cleandata[0].replace('-', '_')
cleandata[0] = cleandata[0].replace('security (auth/unicast/group)', 'security')
# parse the data
raw_output = jc.parsers.universal.sparse_table_parse(cleandata)
if raw:
return raw_output
else:
return process(raw_output)

View File

@@ -58,6 +58,8 @@ Examples:
"hwtype": "ether",
"hwaddress": "00:50:56:f0:98:26",
"iface": "ens33"
"permanent": false,
"expires": 1182
},
{
"name": "gateway",
@@ -65,6 +67,8 @@ Examples:
"hwtype": "ether",
"hwaddress": "00:50:56:f7:4a:fc",
"iface": "ens33"
"permanent": false,
"expires": 110
}
]
@@ -76,6 +80,8 @@ Examples:
"hwtype": "ether",
"hwaddress": "00:50:56:fe:7a:b4",
"iface": "ens33"
"permanent": false,
"expires": "1182"
},
{
"name": "_gateway",
@@ -83,6 +89,8 @@ Examples:
"hwtype": "ether",
"hwaddress": "00:50:56:f7:4a:fc",
"iface": "ens33"
"permanent": false,
"expires": "110"
}
]
"""
@@ -91,7 +99,7 @@ import jc.parsers.universal
class info():
version = '1.1'
version = '1.6'
description = 'arp command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
@@ -123,7 +131,9 @@ def process(proc_data):
"hwtype": string,
"hwaddress": string,
"flags_mask": string,
"iface": string
"iface": string,
"permanent": boolean,
"expires": integer
}
]
"""
@@ -133,6 +143,14 @@ def process(proc_data):
if 'name' in entry and entry['name'] == '?':
entry['name'] = None
int_list = ['expires']
for key in int_list:
if key in entry:
try:
entry[key] = int(entry[key])
except (ValueError):
entry[key] = None
return proc_data
@@ -153,58 +171,65 @@ def parse(data, raw=False, quiet=False):
if not quiet:
jc.utils.compatibility(__name__, info.compatible)
cleandata = data.splitlines()
raw_output = []
cleandata = list(filter(None, data.splitlines()))
# remove final Entries row if -v was used
if cleandata[-1].find('Entries:') == 0:
cleandata.pop(-1)
if jc.utils.has_data(data):
# detect if osx style was used
if cleandata[0].find(' ifscope ') != -1:
raw_output = []
for line in cleandata:
line = line.split()
output_line = {}
output_line['name'] = line[0]
output_line['address'] = line[1].lstrip('(').rstrip(')')
output_line['hwtype'] = line[-1].lstrip('[').rstrip(']')
output_line['hwaddress'] = line[3]
output_line['iface'] = line[5]
raw_output.append(output_line)
# remove final Entries row if -v was used
if cleandata[-1].startswith('Entries:'):
cleandata.pop(-1)
if raw:
return raw_output
# detect if freebsd/osx style was used
if cleandata[0][-1] == ']':
for line in cleandata:
splitline = line.split()
output_line = {
'name': splitline[0],
'address': splitline[1].lstrip('(').rstrip(')'),
'hwtype': splitline[-1].lstrip('[').rstrip(']'),
'hwaddress': splitline[3],
'iface': splitline[5]
}
if 'permanent' in splitline:
output_line['permanent'] = True
else:
output_line['permanent'] = False
if 'expires' in splitline:
output_line['expires'] = splitline[-3]
raw_output.append(output_line)
if raw:
return raw_output
else:
return process(raw_output)
# detect if linux style was used
elif cleandata[0].startswith('Address'):
# fix header row to change Flags Mask to flags_mask
cleandata[0] = cleandata[0].replace('Flags Mask', 'flags_mask')
cleandata[0] = cleandata[0].lower()
raw_output = jc.parsers.universal.simple_table_parse(cleandata)
# otherwise, try bsd style
else:
return process(raw_output)
for line in cleandata:
line = line.split()
output_line = {
'name': line[0],
'address': line[1].lstrip('(').rstrip(')'),
'hwtype': line[4].lstrip('[').rstrip(']'),
'hwaddress': line[3],
'iface': line[6],
}
raw_output.append(output_line)
# detect if linux style was used
elif cleandata[0].find('Address') == 0:
# fix header row to change Flags Mask to flags_mask
cleandata[0] = cleandata[0].replace('Flags Mask', 'flags_mask')
cleandata[0] = cleandata[0].lower()
raw_output = jc.parsers.universal.simple_table_parse(cleandata)
if raw:
return raw_output
else:
return process(raw_output)
# otherwise, try bsd style
if raw:
return raw_output
else:
raw_output = []
for line in cleandata:
line = line.split()
output_line = {}
output_line['name'] = line[0]
output_line['address'] = line[1].lstrip('(').rstrip(')')
output_line['hwtype'] = line[4].lstrip('[').rstrip(']')
output_line['hwaddress'] = line[3]
output_line['iface'] = line[6]
raw_output.append(output_line)
if raw:
return raw_output
else:
return process(raw_output)
return process(raw_output)

219
jc/parsers/blkid.py Normal file
View File

@@ -0,0 +1,219 @@
"""jc - JSON CLI output utility blkid Parser
Usage:
specify --blkid as the first argument if the piped input is coming from blkid
Compatibility:
'linux'
Examples:
$ blkid | jc --blkid -p
[
{
"device": "/dev/sda1",
"uuid": "05d927ab-5875-49e4-ada1-7f46cb32c932",
"type": "xfs"
},
{
"device": "/dev/sda2",
"uuid": "3klkIj-w1kk-DkJi-0XBJ-y3i7-i2Ac-vHqWBM",
"type": "LVM2_member"
},
{
"device": "/dev/mapper/centos-root",
"uuid": "07d718ff-950c-4e5b-98f0-42a1147c77d9",
"type": "xfs"
},
{
"device": "/dev/mapper/centos-swap",
"uuid": "615eb89a-bcbf-46fd-80e3-c483ff5c931f",
"type": "swap"
}
]
$ sudo blkid -o udev -ip /dev/sda2 | jc --blkid -p
[
{
"id_fs_uuid": "3klkIj-w1kk-DkJi-0XBJ-y3i7-i2Ac-vHqWBM",
"id_fs_uuid_enc": "3klkIj-w1kk-DkJi-0XBJ-y3i7-i2Ac-vHqWBM",
"id_fs_version": "LVM2\\x20001",
"id_fs_type": "LVM2_member",
"id_fs_usage": "raid",
"id_iolimit_minimum_io_size": 512,
"id_iolimit_physical_sector_size": 512,
"id_iolimit_logical_sector_size": 512,
"id_part_entry_scheme": "dos",
"id_part_entry_type": "0x8e",
"id_part_entry_number": 2,
"id_part_entry_offset": 2099200,
"id_part_entry_size": 39843840,
"id_part_entry_disk": "8:0"
}
]
$ sudo blkid -ip /dev/sda1 | jc --blkid -p -r
[
{
"devname": "/dev/sda1",
"uuid": "05d927bb-5875-49e3-ada1-7f46cb31c932",
"type": "xfs",
"usage": "filesystem",
"minimum_io_size": "512",
"physical_sector_size": "512",
"logical_sector_size": "512",
"part_entry_scheme": "dos",
"part_entry_type": "0x83",
"part_entry_flags": "0x80",
"part_entry_number": "1",
"part_entry_offset": "2048",
"part_entry_size": "2097152",
"part_entry_disk": "8:0"
}
]
"""
import shlex
import jc.utils
class info():
version = '1.2'
description = 'blkid command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
# details = 'enter any other details here'
# compatible options: linux, darwin, cygwin, win32, aix, freebsd
compatible = ['linux']
magic_commands = ['blkid']
__version__ = info.version
def process(proc_data):
"""
Final processing to conform to the schema.
Parameters:
proc_data: (dictionary) raw structured data to process
Returns:
List of dictionaries. Structured data with the following schema:
[
{
"device": string,
"uuid": string,
"type": string,
"usage": string,
"part_entry_scheme": string,
"part_entry_type": string,
"part_entry_flags": string,
"part_entry_number": integer,
"part_entry_offset": integer,
"part_entry_size": integer,
"part_entry_disk": string,
"id_fs_uuid": string,
"id_fs_uuid_enc": string,
"id_fs_version": string,
"id_fs_type": string,
"id_fs_usage": string,
"id_part_entry_scheme": string,
"id_part_entry_type": string,
"id_part_entry_flags": string,
"id_part_entry_number": integer,
"id_part_entry_offset": integer,
"id_part_entry_size": integer,
"id_iolimit_minimum_io_size": integer,
"id_iolimit_physical_sector_size": integer,
"id_iolimit_logical_sector_size": integer,
"id_part_entry_disk": string,
"minimum_io_size": integer,
"physical_sector_size": integer,
"logical_sector_size": integer
}
]
"""
for entry in proc_data:
if 'devname' in entry:
entry['device'] = entry.pop('devname')
int_list = ['part_entry_number', 'part_entry_offset', 'part_entry_size', 'id_part_entry_number',
'id_part_entry_offset', 'id_part_entry_size', 'minimum_io_size', 'physical_sector_size',
'logical_sector_size', 'id_iolimit_minimum_io_size', 'id_iolimit_physical_sector_size',
'id_iolimit_logical_sector_size']
for key in int_list:
if key in entry:
try:
entry[key] = int(entry[key])
except (ValueError):
entry[key] = None
return proc_data
def parse(data, raw=False, quiet=False):
"""
Main text parsing function
Parameters:
data: (string) text data to parse
raw: (boolean) output preprocessed JSON if True
quiet: (boolean) suppress warning messages if True
Returns:
List of dictionaries. Raw or processed structured data.
"""
if not quiet:
jc.utils.compatibility(__name__, info.compatible)
raw_output = []
if jc.utils.has_data(data):
# if the first field is a device, use normal parsing:
if data.split(maxsplit=1)[0][-1] == ':':
linedata = data.splitlines()
for line in linedata:
output_line = {}
entries = shlex.split(line)
output_line['device'] = entries.pop(0)[:-1]
for entry in entries:
key = entry.split('=', maxsplit=1)[0].lower()
value = entry.split('=', maxsplit=1)[1]
output_line[key] = value
raw_output.append(output_line)
# else use key/value per line parsing
else:
linedata = data.splitlines()
output_line = {}
for line in linedata:
if line == '':
if output_line:
raw_output.append(output_line)
output_line = {}
continue
continue
key = line.split('=', maxsplit=1)[0].lower()
value = line.split('=', maxsplit=1)[1]
output_line[key] = value
if output_line:
raw_output.append(output_line)
if raw:
return raw_output
else:
return process(raw_output)

View File

@@ -132,7 +132,7 @@ import jc.parsers.universal
class info():
version = '1.1'
version = '1.4'
description = 'crontab command and file parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
@@ -225,44 +225,46 @@ def parse(data, raw=False, quiet=False):
# Clear any blank lines
cleandata = list(filter(None, cleandata))
# Clear any commented lines
for i, line in reversed(list(enumerate(cleandata))):
if line.strip().find('#') == 0:
cleandata.pop(i)
if jc.utils.has_data(data):
# Pop any variable assignment lines
cron_var = []
for i, line in reversed(list(enumerate(cleandata))):
if line.find('=') != -1:
var_line = cleandata.pop(i)
var_name = var_line.split('=', maxsplit=1)[0].strip()
var_value = var_line.split('=', maxsplit=1)[1].strip()
cron_var.append({'name': var_name,
'value': var_value})
# Clear any commented lines
for i, line in reversed(list(enumerate(cleandata))):
if line.strip().startswith('#'):
cleandata.pop(i)
raw_output['variables'] = cron_var
# 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] == '@':
var_line = cleandata.pop(i)
var_name = var_line.split('=', maxsplit=1)[0].strip()
var_value = var_line.split('=', maxsplit=1)[1].strip()
cron_var.append({'name': var_name,
'value': var_value})
# Pop any shortcut lines
shortcut_list = []
for i, line in reversed(list(enumerate(cleandata))):
if line.strip().startswith('@'):
shortcut_line = cleandata.pop(i)
occurrence = shortcut_line.split(maxsplit=1)[0].strip().lstrip('@')
cmd = shortcut_line.split(maxsplit=1)[1].strip()
shortcut_list.append({'occurrence': occurrence,
'command': cmd})
raw_output['variables'] = cron_var
# Add header row for parsing
cleandata[:0] = ['minute hour day_of_month month day_of_week command']
# Pop any shortcut lines
shortcut_list = []
for i, line in reversed(list(enumerate(cleandata))):
if line.strip().startswith('@'):
shortcut_line = cleandata.pop(i)
occurrence = shortcut_line.split(maxsplit=1)[0].strip().lstrip('@')
cmd = shortcut_line.split(maxsplit=1)[1].strip()
shortcut_list.append({'occurrence': occurrence,
'command': cmd})
if len(cleandata) > 1:
cron_list = jc.parsers.universal.simple_table_parse(cleandata)
# Add header row for parsing
cleandata[:0] = ['minute hour day_of_month month day_of_week command']
raw_output['schedule'] = cron_list
if len(cleandata) > 1:
cron_list = jc.parsers.universal.simple_table_parse(cleandata)
# Add shortcut entries back in
for item in shortcut_list:
raw_output['schedule'].append(item)
raw_output['schedule'] = cron_list
# Add shortcut entries back in
for item in shortcut_list:
raw_output['schedule'].append(item)
if raw:
return raw_output

View File

@@ -133,7 +133,7 @@ import jc.parsers.universal
class info():
version = '1.0'
version = '1.5'
description = 'crontab file parser with user support'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
@@ -226,46 +226,48 @@ def parse(data, raw=False, quiet=False):
# Clear any blank lines
cleandata = list(filter(None, cleandata))
# Clear any commented lines
for i, line in reversed(list(enumerate(cleandata))):
if line.strip().find('#') == 0:
cleandata.pop(i)
if jc.utils.has_data(data):
# Pop any variable assignment lines
cron_var = []
for i, line in reversed(list(enumerate(cleandata))):
if line.find('=') != -1:
var_line = cleandata.pop(i)
var_name = var_line.split('=', maxsplit=1)[0].strip()
var_value = var_line.split('=', maxsplit=1)[1].strip()
cron_var.append({'name': var_name,
'value': var_value})
# Clear any commented lines
for i, line in reversed(list(enumerate(cleandata))):
if line.strip().startswith('#'):
cleandata.pop(i)
raw_output['variables'] = cron_var
# 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] == '@':
var_line = cleandata.pop(i)
var_name = var_line.split('=', maxsplit=1)[0].strip()
var_value = var_line.split('=', maxsplit=1)[1].strip()
cron_var.append({'name': var_name,
'value': var_value})
# Pop any shortcut lines
shortcut_list = []
for i, line in reversed(list(enumerate(cleandata))):
if line.strip().startswith('@'):
shortcut_line = cleandata.pop(i)
occurrence = shortcut_line.split(maxsplit=1)[0].strip().lstrip('@')
usr = shortcut_line.split(maxsplit=2)[1].strip()
cmd = shortcut_line.split(maxsplit=2)[2].strip()
shortcut_list.append({'occurrence': occurrence,
'user': usr,
'command': cmd})
raw_output['variables'] = cron_var
# Add header row for parsing
cleandata[:0] = ['minute hour day_of_month month day_of_week user command']
# Pop any shortcut lines
shortcut_list = []
for i, line in reversed(list(enumerate(cleandata))):
if line.strip().startswith('@'):
shortcut_line = cleandata.pop(i)
occurrence = shortcut_line.split(maxsplit=1)[0].strip().lstrip('@')
usr = shortcut_line.split(maxsplit=2)[1].strip()
cmd = shortcut_line.split(maxsplit=2)[2].strip()
shortcut_list.append({'occurrence': occurrence,
'user': usr,
'command': cmd})
if len(cleandata) > 1:
cron_list = jc.parsers.universal.simple_table_parse(cleandata)
# Add header row for parsing
cleandata[:0] = ['minute hour day_of_month month day_of_week user command']
raw_output['schedule'] = cron_list
if len(cleandata) > 1:
cron_list = jc.parsers.universal.simple_table_parse(cleandata)
# Add shortcut entries back in
for item in shortcut_list:
raw_output['schedule'].append(item)
raw_output['schedule'] = cron_list
# Add shortcut entries back in
for item in shortcut_list:
raw_output['schedule'].append(item)
if raw:
return raw_output

142
jc/parsers/csv.py Normal file
View File

@@ -0,0 +1,142 @@
"""jc - JSON CLI output utility csv Parser
Usage:
specify --csv as the first argument if the piped input is coming from a csv file.
the csv parser will attempt to automatically detect the delimiter character.
if the delimiter cannot be detected it will default to comma.
the first row of the file must be a header row.
Compatibility:
'linux', 'darwin', 'cygwin', 'win32', 'aix', 'freebsd'
Examples:
$ cat homes.csv
"Sell", "List", "Living", "Rooms", "Beds", "Baths", "Age", "Acres", "Taxes"
142, 160, 28, 10, 5, 3, 60, 0.28, 3167
175, 180, 18, 8, 4, 1, 12, 0.43, 4033
129, 132, 13, 6, 3, 1, 41, 0.33, 1471
...
$ cat homes.csv | jc --csv -p
[
{
"Sell": "142",
"List": "160",
"Living": "28",
"Rooms": "10",
"Beds": "5",
"Baths": "3",
"Age": "60",
"Acres": "0.28",
"Taxes": "3167"
},
{
"Sell": "175",
"List": "180",
"Living": "18",
"Rooms": "8",
"Beds": "4",
"Baths": "1",
"Age": "12",
"Acres": "0.43",
"Taxes": "4033"
},
{
"Sell": "129",
"List": "132",
"Living": "13",
"Rooms": "6",
"Beds": "3",
"Baths": "1",
"Age": "41",
"Acres": "0.33",
"Taxes": "1471"
},
...
]
"""
import jc.utils
import csv
class info():
version = '1.1'
description = 'CSV file parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
details = 'Using the python standard csv library'
# compatible options: linux, darwin, cygwin, win32, aix, freebsd
compatible = ['linux', 'darwin', 'cygwin', 'win32', 'aix', 'freebsd']
__version__ = info.version
def process(proc_data):
"""
Final processing to conform to the schema.
Parameters:
proc_data: (dictionary) raw structured data to process
Returns:
List of dictionaries. Each dictionary represents a row in the csv file:
[
{
csv file converted to a Dictionary
https://docs.python.org/3/library/csv.html
}
]
"""
# No further processing
return proc_data
def parse(data, raw=False, quiet=False):
"""
Main text parsing function
Parameters:
data: (string) text data to parse
raw: (boolean) output preprocessed JSON if True
quiet: (boolean) suppress warning messages if True
Returns:
List of dictionaries. Raw or processed structured data.
"""
if not quiet:
jc.utils.compatibility(__name__, info.compatible)
raw_output = []
cleandata = data.splitlines()
# Clear any blank lines
cleandata = list(filter(None, cleandata))
if jc.utils.has_data(data):
dialect = None
try:
dialect = csv.Sniffer().sniff(data[:1024])
except Exception:
pass
reader = csv.DictReader(cleandata, dialect=dialect)
for row in reader:
raw_output.append(row)
if raw:
return raw_output
else:
return process(raw_output)

160
jc/parsers/date.py Normal file
View File

@@ -0,0 +1,160 @@
"""jc - JSON CLI output utility date Parser
Usage:
specify --date as the first argument if the piped input is coming from date
Compatibility:
'linux', 'darwin', 'freebsd'
Examples:
$ date | jc --date -p
{
"year": 2020,
"month_num": 7,
"day": 31,
"hour": 16,
"minute": 48,
"second": 11,
"month": "Jul",
"weekday": "Fri",
"weekday_num": 6,
"timezone": "PDT"
}
$ date | jc --date -p -r
{
"year": "2020",
"month": "Jul",
"day": "31",
"weekday": "Fri",
"hour": "16",
"minute": "50",
"second": "01",
"timezone": "PDT"
}
"""
import jc.utils
class info():
version = '1.0'
description = 'date command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
# compatible options: linux, darwin, cygwin, win32, aix, freebsd
compatible = ['linux', 'darwin', 'freebsd']
magic_commands = ['date']
__version__ = info.version
def process(proc_data):
"""
Final processing to conform to the schema.
Parameters:
proc_data: (dictionary) raw structured data to process
Returns:
Dictionary. Structured data with the following schema:
{
"year": integer,
"month_num": integer,
"day": integer,
"hour": integer,
"minute": integer,
"second": integer,
"month": string,
"weekday": string,
"weekday_num": integer,
"timezone": string
}
"""
month_map = {
"Jan": 1,
"Feb": 2,
"Mar": 3,
"Apr": 4,
"May": 5,
"Jun": 6,
"Jul": 7,
"Aug": 8,
"Sep": 9,
"Oct": 10,
"Nov": 11,
"Dec": 12
}
weekday_map = {
"Sun": 1,
"Mon": 2,
"Tue": 3,
"Wed": 4,
"Thu": 5,
"Fri": 6,
"Sat": 7
}
if proc_data:
return {
"year": int(proc_data['year']),
'month_num': month_map[proc_data['month']],
"day": int(proc_data['day']),
"hour": int(proc_data['hour']),
"minute": int(proc_data['minute']),
"second": int(proc_data['second']),
"month": proc_data['month'],
"weekday": proc_data['weekday'],
"weekday_num": weekday_map[proc_data['weekday']],
"timezone": proc_data['timezone']
}
else:
return {}
def parse(data, raw=False, quiet=False):
"""
Main text parsing function
Parameters:
data: (string) text data to parse
raw: (boolean) output preprocessed JSON if True
quiet: (boolean) suppress warning messages if True
Returns:
Dictionary. Raw or processed structured data.
"""
if not quiet:
jc.utils.compatibility(__name__, info.compatible)
raw_output = {}
if jc.utils.has_data(data):
data = data.replace(':', ' ')
split_data = data.split()
raw_output = {
"year": split_data[7],
"month": split_data[1],
"day": split_data[2],
"weekday": split_data[0],
"hour": split_data[3],
"minute": split_data[4],
"second": split_data[5],
"timezone": split_data[6]
}
if raw:
return raw_output
else:
return process(raw_output)

View File

@@ -6,7 +6,7 @@ Usage:
Compatibility:
'linux', 'darwin'
'linux', 'darwin', 'freebsd'
Examples:
@@ -73,13 +73,13 @@ import jc.parsers.universal
class info():
version = '1.1'
version = '1.5'
description = 'df command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
# compatible options: linux, darwin, cygwin, win32, aix, freebsd
compatible = ['linux', 'darwin']
compatible = ['linux', 'darwin', 'freebsd']
magic_commands = ['df']
@@ -135,7 +135,7 @@ def process(proc_data):
# change any entry for key with '_blocks' in the name to int
for k in entry:
if str(k).find('_blocks') != -1:
if '_blocks' in str(k):
try:
blocks_int = int(entry[k])
entry[k] = blocks_int
@@ -184,14 +184,17 @@ def parse(data, raw=False, quiet=False):
jc.utils.compatibility(__name__, info.compatible)
cleandata = data.splitlines()
raw_output = []
# fix headers
cleandata[0] = cleandata[0].lower()
cleandata[0] = cleandata[0].replace('-', '_')
cleandata[0] = cleandata[0].replace('mounted on', 'mounted_on')
if jc.utils.has_data(data):
# parse the data
raw_output = jc.parsers.universal.sparse_table_parse(cleandata)
# fix headers
cleandata[0] = cleandata[0].lower()
cleandata[0] = cleandata[0].replace('-', '_')
cleandata[0] = cleandata[0].replace('mounted on', 'mounted_on')
# parse the data
raw_output = jc.parsers.universal.sparse_table_parse(cleandata)
if raw:
return raw_output

View File

@@ -324,7 +324,7 @@ import jc.utils
class info():
version = '1.0'
version = '1.3'
description = 'dig command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
@@ -361,6 +361,15 @@ def process(proc_data):
"answer_num": integer,
"authority_num": integer,
"additional_num": integer,
"axfr": [
{
"name": string,
"class": string,
"type": string,
"ttl": integer,
"data": string
}
],
"question": {
"name": string,
"class": string,
@@ -388,6 +397,7 @@ def process(proc_data):
"server": string,
"when": string,
"rcvd": integer
"size": string
}
]
"""
@@ -402,6 +412,14 @@ def process(proc_data):
except (ValueError):
entry[key] = None
if 'axfr' in entry:
for ax in entry['axfr']:
try:
ttl_int = int(ax['ttl'])
ax['ttl'] = ttl_int
except (ValueError):
ax['ttl'] = None
if 'answer' in entry:
for ans in entry['answer']:
try:
@@ -508,6 +526,25 @@ def parse_answer(answer):
'data': answer_data}
def parse_axfr(axfr):
# ; <<>> DiG 9.11.14-3-Debian <<>> @81.4.108.41 axfr zonetransfer.me +nocookie
# ; (1 server found)
# ;; global options: +cmd
# zonetransfer.me. 7200 IN A 5.196.105.14
axfr = axfr.split(maxsplit=4)
axfr_name = axfr[0]
axfr_ttl = axfr[1]
axfr_class = axfr[2]
axfr_type = axfr[3]
axfr_data = axfr[4]
return {'name': axfr_name,
'ttl': axfr_ttl,
'class': axfr_class,
'type': axfr_type,
'data': axfr_data}
def parse(data, raw=False, quiet=False):
"""
Main text parsing function
@@ -534,80 +571,106 @@ def parse(data, raw=False, quiet=False):
question = False
authority = False
answer = False
axfr = False
output_entry = {}
for line in cleandata:
if line.find(';; ->>HEADER<<-') == 0:
output_entry = {}
output_entry.update(parse_header(line))
continue
if jc.utils.has_data(data):
for line in cleandata:
if line.find(';; flags:') == 0:
output_entry.update(parse_flags_line(line))
continue
if line.startswith('; <<>> ') and ' axfr ' in line.lower():
question = False
authority = False
answer = False
axfr = True
axfr_list = []
continue
if line.find(';; QUESTION SECTION:') == 0:
question = True
authority = False
answer = False
continue
if ';' not in line and axfr:
axfr_list.append(parse_axfr(line))
output_entry.update({'axfr': axfr_list})
continue
if question:
output_entry['question'] = parse_question(line)
question = False
authority = False
answer = False
continue
if line.startswith(';; ->>HEADER<<-'):
output_entry = {}
output_entry.update(parse_header(line))
continue
if line.find(';; AUTHORITY SECTION:') == 0:
question = False
authority = True
answer = False
authority_list = []
continue
if line.startswith(';; flags:'):
output_entry.update(parse_flags_line(line))
continue
if line.find(';') == -1 and authority:
authority_list.append(parse_authority(line))
output_entry.update({'authority': authority_list})
continue
if line.startswith(';; QUESTION SECTION:'):
question = True
authority = False
answer = False
axfr = False
continue
if line.find(';; ANSWER SECTION:') == 0:
question = False
authority = False
answer = True
answer_list = []
continue
if question:
output_entry['question'] = parse_question(line)
question = False
authority = False
answer = False
axfr = False
continue
if line.find(';') == -1 and answer:
answer_list.append(parse_answer(line))
output_entry.update({'answer': answer_list})
continue
if line.startswith(';; AUTHORITY SECTION:'):
question = False
authority = True
answer = False
axfr = False
authority_list = []
continue
# footer consists of 4 lines
# footer line 1
if line.find(';; Query time:') == 0:
output_entry.update({'query_time': line.split(':')[1].lstrip()})
continue
if ';' not in line and authority:
authority_list.append(parse_authority(line))
output_entry.update({'authority': authority_list})
continue
# footer line 2
if line.find(';; SERVER:') == 0:
output_entry.update({'server': line.split(':')[1].lstrip()})
continue
if line.startswith(';; ANSWER SECTION:'):
question = False
authority = False
answer = True
axfr = False
answer_list = []
continue
# footer line 3
if line.find(';; WHEN:') == 0:
output_entry.update({'when': line.split(':', maxsplit=1)[1].lstrip()})
continue
if ';' not in line and answer:
answer_list.append(parse_answer(line))
output_entry.update({'answer': answer_list})
continue
# footer line 4 (last line)
if line.find(';; MSG SIZE rcvd:') == 0:
output_entry.update({'rcvd': line.split(':')[1].lstrip()})
# footer consists of 4 lines
# footer line 1
if line.startswith(';; Query time:'):
output_entry.update({'query_time': line.split(':')[1].lstrip()})
continue
if output_entry:
raw_output.append(output_entry)
# footer line 2
if line.startswith(';; SERVER:'):
output_entry.update({'server': line.split(':')[1].lstrip()})
continue
# footer line 3
if line.startswith(';; WHEN:'):
output_entry.update({'when': line.split(':', maxsplit=1)[1].lstrip()})
continue
# footer line 4 (last line)
if line.startswith(';; MSG SIZE rcvd:'):
output_entry.update({'rcvd': line.split(':')[1].lstrip()})
if output_entry:
raw_output.append(output_entry)
elif line.startswith(';; XFR size:'):
output_entry.update({'size': line.split(':')[1].lstrip()})
if output_entry:
raw_output.append(output_entry)
raw_output = list(filter(None, raw_output))
raw_output = list(filter(None, raw_output))
if raw:
return raw_output
else:

341
jc/parsers/dmidecode.py Normal file
View File

@@ -0,0 +1,341 @@
"""jc - JSON CLI output utility dmidecode Parser
Usage:
specify --dmidecode as the first argument if the piped input is coming from dmidecode
Compatibility:
'linux'
Examples:
# dmidecode | jc --dmidecode -p
[
{
"handle": "0x0000",
"type": 0,
"bytes": 24,
"description": "BIOS Information",
"values": {
"vendor": "Phoenix Technologies LTD",
"version": "6.00",
"release_date": "04/13/2018",
"address": "0xEA490",
"runtime_size": "88944 bytes",
"rom_size": "64 kB",
"characteristics": [
"ISA is supported",
"PCI is supported",
"PC Card (PCMCIA) is supported",
"PNP is supported",
"APM is supported",
"BIOS is upgradeable",
"BIOS shadowing is allowed",
"ESCD support is available",
"Boot from CD is supported",
"Selectable boot is supported",
"EDD is supported",
"Print screen service is supported (int 5h)",
"8042 keyboard services are supported (int 9h)",
"Serial services are supported (int 14h)",
"Printer services are supported (int 17h)",
"CGA/mono video services are supported (int 10h)",
"ACPI is supported",
"Smart battery is supported",
"BIOS boot specification is supported",
"Function key-initiated network boot is supported",
"Targeted content distribution is supported"
],
"bios_revision": "4.6",
"firmware_revision": "0.0"
}
},
...
]
# dmidecode | jc --dmidecode -p -r
[
{
"handle": "0x0000",
"type": "0",
"bytes": "24",
"description": "BIOS Information",
"values": {
"vendor": "Phoenix Technologies LTD",
"version": "6.00",
"release_date": "04/13/2018",
"address": "0xEA490",
"runtime_size": "88944 bytes",
"rom_size": "64 kB",
"characteristics": [
"ISA is supported",
"PCI is supported",
"PC Card (PCMCIA) is supported",
"PNP is supported",
"APM is supported",
"BIOS is upgradeable",
"BIOS shadowing is allowed",
"ESCD support is available",
"Boot from CD is supported",
"Selectable boot is supported",
"EDD is supported",
"Print screen service is supported (int 5h)",
"8042 keyboard services are supported (int 9h)",
"Serial services are supported (int 14h)",
"Printer services are supported (int 17h)",
"CGA/mono video services are supported (int 10h)",
"ACPI is supported",
"Smart battery is supported",
"BIOS boot specification is supported",
"Function key-initiated network boot is supported",
"Targeted content distribution is supported"
],
"bios_revision": "4.6",
"firmware_revision": "0.0"
}
},
...
]
"""
import jc.utils
class info():
version = '1.1'
description = 'dmidecode command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
# details = 'enter any other details here'
# compatible options: linux, darwin, cygwin, win32, aix, freebsd
compatible = ['linux']
magic_commands = ['dmidecode']
__version__ = info.version
def process(proc_data):
"""
Final processing to conform to the schema.
Parameters:
proc_data: (dictionary) raw structured data to process
Returns:
List of dictionaries. Structured data with the following schema:
[
{
"handle": string,
"type": integer,
"bytes": integer,
"description": string,
"values": { (null if empty)
"lowercase_no_spaces_keys": string,
"multiline_key_values": [
string,
]
}
}
]
"""
for entry in proc_data:
int_list = ['type', 'bytes']
for key in int_list:
if key in entry:
try:
key_int = int(entry[key])
entry[key] = key_int
except (ValueError):
entry[key] = None
if not entry['values']:
entry['values'] = None
return proc_data
def parse(data, raw=False, quiet=False):
"""
Main text parsing function
Parameters:
data: (string) text data to parse
raw: (boolean) output preprocessed JSON if True
quiet: (boolean) suppress warning messages if True
Returns:
List of dictionaries. Raw or processed structured data.
"""
if not quiet:
jc.utils.compatibility(__name__, info.compatible)
item_header = False
item_values = False
value_list = False
item = None
header = None
key = None
val = None
attribute = None
values = None
key_data = None
raw_output = []
if jc.utils.has_data(data):
data = data.splitlines()
# remove header rows
for row in data.copy():
if row:
data.pop(0)
else:
break
# main parsing loop
for line in data:
# new item
if not line:
item_header = True
item_values = False
value_list = False
if item:
if values:
item['values'][attribute] = values
if key_data:
item['values'][f'{key}_data'] = key_data
raw_output.append(item)
item = {}
header = None
key = None
val = None
attribute = None
values = []
key_data = []
continue
# header
if line.startswith('Handle ') and line.endswith('bytes'):
# Handle 0x0000, DMI type 0, 24 bytes
header = line.replace(',', ' ').split()
item = {
'handle': header[1],
'type': header[4],
'bytes': header[5]
}
continue
# description
if item_header:
item_header = False
item_values = True
value_list = False
item['description'] = line
item['values'] = {}
continue
# new item if multiple descriptions in handle
if not item_header and not line.startswith('\t'):
item_header = False
item_values = True
value_list = False
if item:
if values:
item['values'][attribute] = values
if key_data:
item['values'][f'{key}_data'] = key_data
raw_output.append(item)
item = {
'handle': header[1],
'type': header[4],
'bytes': header[5],
'description': line,
'values': {}
}
key = None
val = None
attribute = None
values = []
key_data = []
continue
# keys and values
if item_values \
and len(line.split(':', maxsplit=1)) == 2 \
and line.startswith('\t') \
and not line.startswith('\t\t') \
and not line.strip().endswith(':'):
item_header = False
item_values = True
value_list = False
if values:
item['values'][attribute] = values
values = []
if key_data:
item['values'][f'{key}_data'] = key_data
key_data = []
key = line.split(':', maxsplit=1)[0].strip().lower().replace(' ', '_')
val = line.split(':', maxsplit=1)[1].strip()
item['values'].update({key: val})
continue
# multi-line key
if item_values \
and line.startswith('\t') \
and not line.startswith('\t\t') \
and line.strip().endswith(':'):
item_header = False
item_values = True
value_list = True
if values:
item['values'][attribute] = values
values = []
if key_data:
item['values'][f'{key}_data'] = key_data
key_data = []
attribute = line[:-1].strip().lower().replace(' ', '_')
values = []
continue
# multi-line values
if value_list \
and line.startswith('\t\t'):
values.append(line.strip())
continue
# data for hybrid multi-line objects
if item_values \
and not value_list \
and line.startswith('\t\t'):
if f'{key}_data' not in item['values']:
item['values'][f'{key}_data'] = []
key_data.append(line.strip())
continue
if item:
raw_output.append(item)
if raw:
return raw_output
else:
return process(raw_output)

View File

@@ -73,7 +73,7 @@ import jc.parsers.universal
class info():
version = '1.1'
version = '1.2'
description = 'du command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
@@ -137,12 +137,12 @@ def parse(data, raw=False, quiet=False):
jc.utils.compatibility(__name__, info.compatible)
raw_output = []
cleandata = data.splitlines()
# Clear any blank lines
cleandata = list(filter(None, cleandata))
cleandata = list(filter(None, data.splitlines()))
if jc.utils.has_data(data):
if cleandata:
cleandata.insert(0, 'size name')
raw_output = jc.parsers.universal.simple_table_parse(cleandata)

View File

@@ -52,7 +52,7 @@ import jc.utils
class info():
version = '1.1'
version = '1.2'
description = 'env command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
@@ -116,12 +116,10 @@ def parse(data, raw=False, quiet=False):
raw_output = {}
linedata = data.splitlines()
# Clear any blank lines
cleandata = list(filter(None, linedata))
cleandata = list(filter(None, data.splitlines()))
if cleandata:
if jc.utils.has_data(data):
for entry in cleandata:
parsed_line = entry.split('=', maxsplit=1)

131
jc/parsers/file.py Normal file
View File

@@ -0,0 +1,131 @@
"""jc - JSON CLI output utility file command Parser
Usage:
specify --file as the first argument if the piped input is coming from file.
Compatibility:
'linux', 'aix', 'freebsd', 'darwin'
Examples:
$ file * | jc --file -p
[
{
"filename": "Applications",
"type": "directory"
},
{
"filename": "another file with spaces",
"type": "empty"
},
{
"filename": "argstest.py",
"type": "Python script text executable, ASCII text"
},
{
"filename": "blkid-p.out",
"type": "ASCII text"
},
{
"filename": "blkid-pi.out",
"type": "ASCII text, with very long lines"
},
{
"filename": "cd_catalog.xml",
"type": "XML 1.0 document text, ASCII text, with CRLF line terminators"
},
{
"filename": "centosserial.sh",
"type": "Bourne-Again shell script text executable, UTF-8 Unicode text"
},
...
]
"""
import jc.utils
import jc.parsers.universal
class info():
version = '1.2'
description = 'file command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
# compatible options: linux, darwin, cygwin, win32, aix, freebsd
compatible = ['linux', 'aix', 'freebsd', 'darwin']
magic_commands = ['file']
__version__ = info.version
def process(proc_data):
"""
Final processing to conform to the schema.
Parameters:
proc_data: (dictionary) raw structured data to process
Returns:
List of dictionaries. Structured data with the following schema:
[
{
"filename": string,
"type ": string
}
]
"""
# No further processing
return proc_data
def parse(data, raw=False, quiet=False):
"""
Main text parsing function
Parameters:
data: (string) text data to parse
raw: (boolean) output preprocessed JSON if True
quiet: (boolean) suppress warning messages if True
Returns:
List of dictionaries. Raw or processed structured data.
"""
if not quiet:
jc.utils.compatibility(__name__, info.compatible)
raw_output = []
warned = False
if jc.utils.has_data(data):
for line in filter(None, data.splitlines()):
linedata = line.rsplit(': ', maxsplit=1)
try:
filename = linedata[0].strip()
filetype = linedata[1].strip()
raw_output.append(
{
'filename': filename,
'type': filetype
}
)
except IndexError:
if not warned:
jc.utils.warning_message('Filenames with newline characters detected. Some filenames may be truncated.')
warned = True
if raw:
return raw_output
else:
return process(raw_output)

View File

@@ -77,14 +77,12 @@ def parse(data, raw=False, quiet=False):
jc.utils.compatibility(__name__, info.compatible)
raw_output = []
cleandata = data.splitlines()
# Clear any blank lines
cleandata = list(filter(None, cleandata))
if jc.utils.has_data(data):
if cleandata:
# parse the content
pass
for line in filter(None, data.splitlines()):
# parse the content
pass
if raw:
return raw_output

View File

@@ -53,7 +53,7 @@ import jc.parsers.universal
class info():
version = '1.0'
version = '1.2'
description = 'free command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
@@ -122,14 +122,18 @@ def parse(data, raw=False, quiet=False):
jc.utils.compatibility(__name__, info.compatible)
cleandata = data.splitlines()
cleandata[0] = cleandata[0].lower()
cleandata[0] = cleandata[0].replace('buff/cache', 'buff_cache')
cleandata[0] = 'type ' + cleandata[0]
raw_output = []
raw_output = jc.parsers.universal.simple_table_parse(cleandata)
if jc.utils.has_data(data):
for entry in raw_output:
entry['type'] = entry['type'].rstrip(':')
cleandata[0] = cleandata[0].lower()
cleandata[0] = cleandata[0].replace('buff/cache', 'buff_cache')
cleandata[0] = 'type ' + cleandata[0]
raw_output = jc.parsers.universal.simple_table_parse(cleandata)
for entry in raw_output:
entry['type'] = entry['type'].rstrip(':')
if raw:
return raw_output

View File

@@ -6,7 +6,7 @@ Usage:
Compatibility:
'linux'
'linux', 'freebsd'
Examples:
@@ -70,13 +70,13 @@ import jc.utils
class info():
version = '1.0'
version = '1.3'
description = 'fstab file parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
# compatible options: linux, darwin, cygwin, win32, aix, freebsd
compatible = ['linux']
compatible = ['linux', 'freebsd']
__version__ = info.version
@@ -141,11 +141,12 @@ def parse(data, raw=False, quiet=False):
# Clear any blank lines
cleandata = list(filter(None, cleandata))
if cleandata:
if jc.utils.has_data(data):
for line in cleandata:
output_line = {}
# ignore commented lines
if line.strip().find('#') == 0:
if line.strip().startswith('#'):
continue
line_list = line.split(maxsplit=6)

191
jc/parsers/group.py Normal file
View File

@@ -0,0 +1,191 @@
"""jc - JSON CLI output utility /etc/group file Parser
Usage:
specify --group as the first argument if the piped input is coming from /etc/group
Compatibility:
'linux', 'darwin', 'aix', 'freebsd'
Examples:
$ cat /etc/group | jc --group -p
[
{
"group_name": "nobody",
"password": "*",
"gid": -2,
"members": []
},
{
"group_name": "nogroup",
"password": "*",
"gid": -1,
"members": []
},
{
"group_name": "wheel",
"password": "*",
"gid": 0,
"members": [
"root"
]
},
{
"group_name": "certusers",
"password": "*",
"gid": 29,
"members": [
"root",
"_jabber",
"_postfix",
"_cyrus",
"_calendar",
"_dovecot"
]
},
...
]
$ cat /etc/group | jc --group -p -r
[
{
"group_name": "nobody",
"password": "*",
"gid": "-2",
"members": [
""
]
},
{
"group_name": "nogroup",
"password": "*",
"gid": "-1",
"members": [
""
]
},
{
"group_name": "wheel",
"password": "*",
"gid": "0",
"members": [
"root"
]
},
{
"group_name": "certusers",
"password": "*",
"gid": "29",
"members": [
"root",
"_jabber",
"_postfix",
"_cyrus",
"_calendar",
"_dovecot"
]
},
...
]
"""
import jc.utils
class info():
version = '1.1'
description = '/etc/group file parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
# details = 'enter any other details here'
# compatible options: linux, darwin, cygwin, win32, aix, freebsd
compatible = ['linux', 'darwin', 'aix', 'freebsd']
__version__ = info.version
def process(proc_data):
"""
Final processing to conform to the schema.
Parameters:
proc_data: (dictionary) raw structured data to process
Returns:
List of dictionaries. Structured data with the following schema:
[
{
"group_name": string,
"password": string,
"gid": integer,
"members": [
string
]
}
]
"""
for entry in proc_data:
int_list = ['gid']
for key in int_list:
if key in entry:
try:
key_int = int(entry[key])
entry[key] = key_int
except (ValueError):
entry[key] = None
if entry['members'] == ['']:
entry['members'] = []
return proc_data
def parse(data, raw=False, quiet=False):
"""
Main text parsing function
Parameters:
data: (string) text data to parse
raw: (boolean) output preprocessed JSON if True
quiet: (boolean) suppress warning messages if True
Returns:
List of dictionaries. Raw or processed structured data.
"""
if not quiet:
jc.utils.compatibility(__name__, info.compatible)
raw_output = []
cleandata = data.splitlines()
# Clear any blank lines
cleandata = list(filter(None, cleandata))
if jc.utils.has_data(data):
for entry in cleandata:
if entry.startswith('#'):
continue
output_line = {}
fields = entry.split(':')
output_line['group_name'] = fields[0]
output_line['password'] = fields[1]
output_line['gid'] = fields[2]
output_line['members'] = fields[3].split(',')
raw_output.append(output_line)
if raw:
return raw_output
else:
return process(raw_output)

153
jc/parsers/gshadow.py Normal file
View File

@@ -0,0 +1,153 @@
"""jc - JSON CLI output utility /etc/gshadow file Parser
Usage:
specify --gshadow as the first argument if the piped input is coming from /etc/gshadow
Compatibility:
'linux', 'aix', 'freebsd'
Examples:
$ cat /etc/gshadow | jc --gshadow -p
[
{
"group_name": "root",
"password": "*",
"administrators": [],
"members": []
},
{
"group_name": "adm",
"password": "*",
"administrators": [],
"members": [
"syslog",
"joeuser"
]
},
...
]
$ cat /etc/gshadow | jc --gshadow -p -r
[
{
"group_name": "root",
"password": "*",
"administrators": [
""
],
"members": [
""
]
},
{
"group_name": "adm",
"password": "*",
"administrators": [
""
],
"members": [
"syslog",
"joeuser"
]
},
...
]
"""
import jc.utils
class info():
version = '1.1'
description = '/etc/gshadow file parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
# details = 'enter any other details here'
# compatible options: linux, darwin, cygwin, win32, aix, freebsd
compatible = ['linux', 'aix', 'freebsd']
__version__ = info.version
def process(proc_data):
"""
Final processing to conform to the schema.
Parameters:
proc_data: (dictionary) raw structured data to process
Returns:
List of dictionaries. Structured data with the following schema:
[
{
"group_name": string,
"password": string,
"administrators": [
string
],
"members": [
string
]
}
]
"""
for entry in proc_data:
if entry['administrators'] == ['']:
entry['administrators'] = []
if entry['members'] == ['']:
entry['members'] = []
return proc_data
def parse(data, raw=False, quiet=False):
"""
Main text parsing function
Parameters:
data: (string) text data to parse
raw: (boolean) output preprocessed JSON if True
quiet: (boolean) suppress warning messages if True
Returns:
List of dictionaries. Raw or processed structured data.
"""
if not quiet:
jc.utils.compatibility(__name__, info.compatible)
raw_output = []
cleandata = data.splitlines()
# Clear any blank lines
cleandata = list(filter(None, cleandata))
if jc.utils.has_data(data):
for entry in cleandata:
if entry.startswith('#'):
continue
output_line = {}
fields = entry.split(':')
output_line['group_name'] = fields[0]
output_line['password'] = fields[1]
output_line['administrators'] = fields[2].split(',')
output_line['members'] = fields[3].split(',')
raw_output.append(output_line)
if raw:
return raw_output
else:
return process(raw_output)

View File

@@ -40,14 +40,15 @@ Examples:
...
}
"""
import jc
import jc.utils
class info():
version = '1.1'
version = '1.3'
description = 'history command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
details = 'Optimizations by https://github.com/philippeitis'
# compatible options: linux, darwin, cygwin, win32, aix, freebsd
compatible = ['linux', 'darwin', 'cygwin', 'aix', 'freebsd']
@@ -79,21 +80,11 @@ def process(proc_data):
# rebuild output for added semantic information
processed = []
for k, v in proc_data.items():
proc_line = {}
proc_line['line'] = k
proc_line['command'] = v
proc_line = {
'line': int(k) if k.isdigit() else None,
'command': v,
}
processed.append(proc_line)
for entry in processed:
int_list = ['line']
for key in int_list:
if key in entry:
try:
key_int = int(entry[key])
entry[key] = key_int
except (ValueError):
entry[key] = None
return processed
@@ -117,14 +108,13 @@ def parse(data, raw=False, quiet=False):
raw_output = {}
# split lines and clear out any non-ascii chars
linedata = data.encode('ascii', errors='ignore').decode().splitlines()
if jc.utils.has_data(data):
# Clear any blank lines
cleandata = list(filter(None, linedata))
# split lines and clear out any non-ascii chars
linedata = data.encode('ascii', errors='ignore').decode().splitlines()
if cleandata:
for entry in cleandata:
# Skip any blank lines
for entry in filter(None, linedata):
try:
parsed_line = entry.split(maxsplit=1)
raw_output[parsed_line[0]] = parsed_line[1]

View File

@@ -61,7 +61,7 @@ import jc.utils
class info():
version = '1.0'
version = '1.2'
description = '/etc/hosts file parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
@@ -117,16 +117,16 @@ def parse(data, raw=False, quiet=False):
jc.utils.compatibility(__name__, info.compatible)
raw_output = []
cleandata = data.splitlines()
# Clear any blank lines
cleandata = list(filter(None, cleandata))
cleandata = list(filter(None, data.splitlines()))
if jc.utils.has_data(data):
if cleandata:
for line in cleandata:
output_line = {}
# ignore commented lines
if line.strip().find('#') == 0:
if line.strip().startswith('#'):
continue
line_list = line.split(maxsplit=1)
@@ -136,7 +136,7 @@ def parse(data, raw=False, quiet=False):
comment_found = False
for i, item in enumerate(hosts_list):
if item.find('#') != -1:
if '#' in item:
comment_found = True
comment_item = i
break

View File

@@ -70,7 +70,7 @@ import jc.utils
class info():
version = '1.0'
version = '1.1'
description = 'id command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
@@ -166,12 +166,12 @@ def parse(data, raw=False, quiet=False):
jc.utils.compatibility(__name__, info.compatible)
raw_output = {}
cleandata = data.split()
# Clear any blank lines
cleandata = list(filter(None, cleandata))
cleandata = list(filter(None, data.split()))
if jc.utils.has_data(data):
if cleandata:
for section in cleandata:
if section.startswith('uid'):
uid_parsed = section.replace('(', '=').replace(')', '=')

View File

@@ -141,16 +141,17 @@ Examples:
}
]
"""
import re
from collections import namedtuple
import jc.utils
from ifconfigparser import IfconfigParser
class info():
version = '1.5'
version = '1.8'
description = 'ifconfig command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
details = 'Using ifconfig-parser package from https://github.com/KnightWhoSayNi/ifconfig-parser'
details = 'Using ifconfig-parser from https://github.com/KnightWhoSayNi/ifconfig-parser'
# compatible options: linux, darwin, cygwin, win32, aix, freebsd
compatible = ['linux', 'aix', 'freebsd', 'darwin']
@@ -160,6 +161,220 @@ class info():
__version__ = info.version
class IfconfigParser(object):
# Author: threeheadedknight@protonmail.com
# Date created: 30.06.2018 17:03
# Python Version: 3.7
# MIT License
# Copyright (c) 2018 threeheadedknight@protonmail.com
# 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.
attributes = ['name', 'type', 'mac_addr', 'ipv4_addr', 'ipv4_bcast', 'ipv4_mask', 'ipv6_addr', 'ipv6_mask',
'ipv6_scope', 'state', 'mtu', 'metric', 'rx_packets', 'rx_errors', 'rx_dropped', 'rx_overruns',
'rx_frame', 'tx_packets', 'tx_errors', 'tx_dropped', 'tx_overruns', 'tx_carrier', 'tx_collisions',
'rx_bytes', 'tx_bytes']
def __init__(self, console_output):
"""
:param console_output:
"""
if isinstance(console_output, list):
source_data = " ".join(console_output)
else:
source_data = console_output.replace("\n", " ")
self.interfaces = self.parser(source_data=source_data)
def list_interfaces(self):
"""
:return:
"""
return sorted(self.interfaces.keys())
def count_interfaces(self):
"""
:return:
"""
return len(self.interfaces.keys())
def filter_interfaces(self, **kwargs):
"""
:param kwargs:
:return:
"""
for attr in kwargs.keys():
if attr not in IfconfigParser.attributes:
raise ValueError("Attribute [{}] not supported.".format(attr))
filtered_interfaces = []
for name, details in self.interfaces.items():
if all(getattr(details, attr) == kwargs[attr] for attr in kwargs.keys()):
filtered_interfaces.append(name)
return sorted(filtered_interfaces)
def get_interface(self, name):
"""
:param name:
:return:
"""
if name in self.list_interfaces():
return self.interfaces[name]
else:
raise InterfaceNotFound("Interface [{}] not found.".format(name))
def get_interfaces(self):
"""
:return:
"""
return self.interfaces
def is_available(self, name):
"""
:param name:
:return:
"""
return name in self.interfaces
def parser(self, source_data):
"""
:param source_data:
:return:
"""
# Linux syntax
re_linux_interface = re.compile(
r"(?P<name>[a-zA-Z0-9:._-]+)\s+Link encap:(?P<type>\S+\s?\S+)(\s+HWaddr\s+\b"
r"(?P<mac_addr>[0-9A-Fa-f:?]+))?",
re.I)
re_linux_ipv4 = re.compile(
r"inet addr:(?P<ipv4_addr>(?:[0-9]{1,3}\.){3}[0-9]{1,3})(\s+Bcast:"
r"(?P<ipv4_bcast>(?:[0-9]{1,3}\.){3}[0-9]{1,3}))?\s+Mask:(?P<ipv4_mask>(?:[0-9]{1,3}\.){3}[0-9]{1,3})",
re.I)
re_linux_ipv6 = re.compile(
r"inet6 addr:\s+(?P<ipv6_addr>\S+)/(?P<ipv6_mask>[0-9]+)\s+Scope:(?P<ipv6_scope>Link|Host)",
re.I)
re_linux_state = re.compile(
r"\W+(?P<state>(?:\w+\s)+)(?:\s+)?MTU:(?P<mtu>[0-9]+)\s+Metric:(?P<metric>[0-9]+)", re.I)
re_linux_rx = re.compile(
r"RX packets:(?P<rx_packets>[0-9]+)\s+errors:(?P<rx_errors>[0-9]+)\s+dropped:"
r"(?P<rx_dropped>[0-9]+)\s+overruns:(?P<rx_overruns>[0-9]+)\s+frame:(?P<rx_frame>[0-9]+)",
re.I)
re_linux_tx = re.compile(
r"TX packets:(?P<tx_packets>[0-9]+)\s+errors:(?P<tx_errors>[0-9]+)\s+dropped:"
r"(?P<tx_dropped>[0-9]+)\s+overruns:(?P<tx_overruns>[0-9]+)\s+carrier:(?P<tx_carrier>[0-9]+)",
re.I)
re_linux_bytes = re.compile(r"\W+RX bytes:(?P<rx_bytes>\d+)\s+\(.*\)\s+TX bytes:(?P<tx_bytes>\d+)\s+\(.*\)", re.I)
re_linux_tx_stats = re.compile(r"collisions:(?P<tx_collisions>[0-9]+)\s+txqueuelen:[0-9]+", re.I)
re_linux = [re_linux_interface, re_linux_ipv4, re_linux_ipv6, re_linux_state, re_linux_rx, re_linux_tx,
re_linux_bytes, re_linux_tx_stats]
# OpenBSD syntax
re_openbsd_interface = re.compile(
r"(?P<name>[a-zA-Z0-9:._-]+):\s+flags=(?P<flags>[0-9]+)<(?P<state>\S+)?>\s+mtu\s+(?P<mtu>[0-9]+)",
re.I)
re_openbsd_ipv4 = re.compile(
r"inet (?P<ipv4_addr>(?:[0-9]{1,3}\.){3}[0-9]{1,3})\s+netmask\s+"
r"(?P<ipv4_mask>(?:[0-9]{1,3}\.){3}[0-9]{1,3})(\s+broadcast\s+"
r"(?P<ipv4_bcast>(?:[0-9]{1,3}\.){3}[0-9]{1,3}))?",
re.I)
re_openbsd_ipv6 = re.compile(
r"inet6\s+(?P<ipv6_addr>\S+)\s+prefixlen\s+(?P<ipv6_mask>[0-9]+)\s+scopeid\s+(?P<ipv6_scope>\w+x\w+)<"
r"(?:link|host)>",
re.I)
re_openbsd_details = re.compile(
r"\S+\s+(?:(?P<mac_addr>[0-9A-Fa-f:?]+)\s+)?txqueuelen\s+[0-9]+\s+\((?P<type>\S+\s?\S+)\)", re.I)
re_openbsd_rx = re.compile(r"RX packets (?P<rx_packets>[0-9]+)\s+bytes\s+(?P<rx_bytes>\d+)\s+.*", re.I)
re_openbsd_rx_stats = re.compile(
r"RX errors (?P<rx_errors>[0-9]+)\s+dropped\s+(?P<rx_dropped>[0-9]+)\s+overruns\s+"
r"(?P<rx_overruns>[0-9]+)\s+frame\s+(?P<rx_frame>[0-9]+)",
re.I)
re_openbsd_tx = re.compile(r"TX packets (?P<tx_packets>[0-9]+)\s+bytes\s+(?P<tx_bytes>\d+)\s+.*", re.I)
re_openbsd_tx_stats = re.compile(
r"TX errors (?P<tx_errors>[0-9]+)\s+dropped\s+(?P<tx_dropped>[0-9]+)\s+overruns\s+"
r"(?P<tx_overruns>[0-9]+)\s+carrier\s+(?P<tx_carrier>[0-9]+)\s+collisions\s+(?P<tx_collisions>[0-9]+)",
re.I)
re_openbsd = [re_openbsd_interface, re_openbsd_ipv4, re_openbsd_ipv6, re_openbsd_details, re_openbsd_rx,
re_openbsd_rx_stats, re_openbsd_tx, re_openbsd_tx_stats]
# FreeBSD syntax
re_freebsd_interface = re.compile(
r"(?P<name>[a-zA-Z0-9:._-]+):\s+flags=(?P<flags>[0-9]+)<(?P<state>\S+)>\s+metric\s+"
r"(?P<metric>[0-9]+)\s+mtu\s+(?P<mtu>[0-9]+)",
re.I)
re_freebsd_ipv4 = re.compile(
r"inet (?P<ipv4_addr>(?:[0-9]{1,3}\.){3}[0-9]{1,3})\s+netmask\s+(?P<ipv4_mask>0x\S+)(\s+broadcast\s+"
r"(?P<ipv4_bcast>(?:[0-9]{1,3}\.){3}[0-9]{1,3}))?",
re.I)
re_freebsd_ipv6 = re.compile(r"\s?inet6\s(?P<ipv6_addr>.*)(?:\%\w+\d+)\sprefixlen\s(?P<ipv6_mask>\d+)(?:\s\w+)?\sscopeid\s(?P<ipv6_scope>\w+x\w+)", re.I)
re_freebsd_details = re.compile(r"ether\s+(?P<mac_addr>[0-9A-Fa-f:?]+)", re.I)
re_freebsd = [re_freebsd_interface, re_freebsd_ipv4, re_freebsd_ipv6, re_freebsd_details]
available_interfaces = dict()
for pattern in [re_linux_interface, re_openbsd_interface, re_freebsd_interface]:
network_interfaces = re.finditer(pattern, source_data)
positions = []
while True:
try:
pos = next(network_interfaces)
positions.append(max(pos.start() - 1, 0))
except StopIteration:
break
if positions:
positions.append(len(source_data))
break
if not positions:
return available_interfaces
for l, r in zip(positions, positions[1:]):
chunk = source_data[l:r]
_interface = dict()
for pattern in re_linux + re_openbsd + re_freebsd:
match = re.search(pattern, chunk.replace('\t', '\n'))
if match:
details = match.groupdict()
for k, v in details.items():
if isinstance(v, str): details[k] = v.strip()
_interface.update(details)
if _interface is not None:
available_interfaces[_interface['name']] = self.update_interface_details(_interface)
return available_interfaces
@staticmethod
def update_interface_details(interface):
for attr in IfconfigParser.attributes:
if attr not in interface:
interface[attr] = None
return namedtuple('Interface', interface.keys())(**interface)
class InterfaceNotFound(Exception):
pass
def process(proc_data):
"""
Final processing to conform to the schema.
@@ -220,7 +435,7 @@ def process(proc_data):
# convert OSX-style subnet mask to dotted quad
if 'ipv4_mask' in entry:
try:
if entry['ipv4_mask'].find('0x') == 0:
if entry['ipv4_mask'].startswith('0x'):
new_mask = entry['ipv4_mask']
new_mask = new_mask.lstrip('0x')
new_mask = '.'.join(str(int(i, 16)) for i in [new_mask[i:i + 2] for i in range(0, len(new_mask), 2)])
@@ -258,14 +473,16 @@ def parse(data, raw=False, quiet=False):
raw_output = []
parsed = IfconfigParser(console_output=data)
interfaces = parsed.get_interfaces()
if jc.utils.has_data(data):
# convert ifconfigparser output to a dictionary
for iface in interfaces:
d = interfaces[iface]._asdict()
dct = dict(d)
raw_output.append(dct)
parsed = IfconfigParser(console_output=data)
interfaces = parsed.get_interfaces()
# convert ifconfigparser output to a dictionary
for iface in interfaces:
d = interfaces[iface]._asdict()
dct = dict(d)
raw_output.append(dct)
if raw:
return raw_output

View File

@@ -2,7 +2,9 @@
Usage:
specify --ini as the first argument if the piped input is coming from an INI file
Specify --ini as the first argument if the piped input is coming from an INI file or any
simple key/value pair file. Delimiter can be '=' or ':'. Missing values are supported.
Comment prefix can be '#' or ';'. Comments must be on their own line.
Compatibility:
@@ -47,7 +49,7 @@ import configparser
class info():
version = '1.0'
version = '1.3'
description = 'INI file parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
@@ -70,15 +72,33 @@ def process(proc_data):
Returns:
Dictionary representing an ini document:
Dictionary representing an ini or simple key/value pair document:
{
ini document converted to a dictionary
see configparser standard library documentation for more details
ini or key/value document converted to a dictionary - see configparser standard
library documentation for more details.
Note: Values starting and ending with quotation marks will have the marks removed.
If you would like to keep the quotation marks, use the -r or raw=True argument.
}
"""
# remove quotation marks from beginning and end of values
for heading in proc_data:
# standard ini files with headers
if isinstance(proc_data[heading], dict):
for key, value in proc_data[heading].items():
if value is not None and value.startswith('"') and value.endswith('"'):
proc_data[heading][key] = value.lstrip('"').rstrip('"')
elif value is None:
proc_data[heading][key] = ''
# simple key/value files with no headers
else:
if proc_data[heading] is not None and proc_data[heading].startswith('"') and proc_data[heading].endswith('"'):
proc_data[heading] = proc_data[heading].lstrip('"').rstrip('"')
elif proc_data[heading] is None:
proc_data[heading] = ''
# No further processing
return proc_data
@@ -101,10 +121,19 @@ def parse(data, raw=False, quiet=False):
raw_output = {}
if data:
ini = configparser.ConfigParser()
ini.read_string(data)
raw_output = {s: dict(ini.items(s)) for s in ini.sections()}
if jc.utils.has_data(data):
ini = configparser.ConfigParser(allow_no_value=True, interpolation=None)
try:
ini.read_string(data)
raw_output = {s: dict(ini.items(s)) for s in ini.sections()}
except configparser.MissingSectionHeaderError:
data = '[data]\n' + data
ini.read_string(data)
output_dict = {s: dict(ini.items(s)) for s in ini.sections()}
for key, value in output_dict['data'].items():
raw_output[key] = value
if raw:
return raw_output

View File

@@ -134,7 +134,7 @@ import jc.utils
class info():
version = '1.1'
version = '1.4'
description = 'iptables command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
@@ -194,19 +194,19 @@ def process(proc_data):
if 'bytes' in rule:
multiplier = 1
if rule['bytes'][-1] == 'K':
multiplier = 1000
multiplier = 10 ** 3
rule['bytes'] = rule['bytes'].rstrip('K')
elif rule['bytes'][-1] == 'M':
multiplier = 1000000
multiplier = 10 ** 6
rule['bytes'] = rule['bytes'].rstrip('M')
elif rule['bytes'][-1] == 'G':
multiplier = 1000000000
multiplier = 10 ** 9
rule['bytes'] = rule['bytes'].rstrip('G')
elif rule['bytes'][-1] == 'T':
multiplier = 1000000000000
multiplier = 10 ** 12
rule['bytes'] = rule['bytes'].rstrip('T')
elif rule['bytes'][-1] == 'P':
multiplier = 1000000000000000
multiplier = 10 ** 15
rule['bytes'] = rule['bytes'].rstrip('P')
try:
@@ -243,36 +243,39 @@ def parse(data, raw=False, quiet=False):
chain = {}
headers = []
cleandata = data.splitlines()
if jc.utils.has_data(data):
for line in cleandata:
for line in list(filter(None, data.splitlines())):
if line.find('Chain') == 0:
if line.startswith('Chain'):
if chain:
raw_output.append(chain)
chain = {}
headers = []
parsed_line = line.split()
chain['chain'] = parsed_line[1]
chain['rules'] = []
continue
elif line.startswith('target') or line.find('pkts') == 1 or line.startswith('num'):
headers = []
headers = [h for h in ' '.join(line.lower().strip().split()).split() if h]
headers.append("options")
continue
else:
rule = line.split(maxsplit=len(headers) - 1)
temp_rule = dict(zip(headers, rule))
if temp_rule:
chain['rules'].append(temp_rule)
if chain:
raw_output.append(chain)
chain = {}
headers = []
parsed_line = line.split()
chain['chain'] = parsed_line[1]
chain['rules'] = []
continue
elif line.find('target') == 0 or line.find('pkts') == 1 or line.find('num') == 0:
headers = []
headers = [h for h in ' '.join(line.lower().strip().split()).split() if h]
headers.append("options")
continue
else:
rule = line.split(maxsplit=len(headers) - 1)
temp_rule = dict(zip(headers, rule))
if temp_rule:
chain['rules'].append(temp_rule)
raw_output = list(filter(None, raw_output))
if raw:
return raw_output

View File

@@ -77,7 +77,7 @@ import jc.utils
class info():
version = '1.0'
version = '1.2'
description = 'jobs command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
@@ -144,12 +144,10 @@ def parse(data, raw=False, quiet=False):
raw_output = []
linedata = data.splitlines()
# Clear any blank lines
cleandata = list(filter(None, linedata))
cleandata = list(filter(None, data.splitlines()))
if cleandata:
if jc.utils.has_data(data):
for entry in cleandata:
output_line = {}
@@ -176,11 +174,11 @@ def parse(data, raw=False, quiet=False):
parsed_line.insert(0, job_number)
# check for + or - in first field
if parsed_line[0].find('+') != -1:
if '+' in parsed_line[0]:
job_history = 'current'
parsed_line[0] = parsed_line[0].rstrip('+')
if parsed_line[0].find('-') != -1:
if '-' in parsed_line[0]:
job_history = 'previous'
parsed_line[0] = parsed_line[0].rstrip('-')

67
jc/parsers/kv.py Normal file
View File

@@ -0,0 +1,67 @@
"""jc - JSON CLI output utility Key/Value File Parser
Usage:
Specify --kv as the first argument if the piped input is coming from a simple
key/value pair file. Delimiter can be '=' or ':'. Missing values are supported.
Comment prefix can be '#' or ';'. Comments must be on their own line.
Compatibility:
'linux', 'darwin', 'cygwin', 'win32', 'aix', 'freebsd'
Examples:
$ cat keyvalue.txt
# this file contains key/value pairs
name = John Doe
address=555 California Drive
age: 34
; comments can include # or ;
# delimiter can be = or :
# quoted values have quotation marks stripped by default
# but can be preserved with the -r argument
occupation:"Engineer"
$ cat keyvalue.txt | jc --ini -p
{
"name": "John Doe",
"address": "555 California Drive",
"age": "34",
"occupation": "Engineer"
}
"""
class info():
version = '1.0'
description = 'Key/Value file parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
details = 'This is a wrapper for the INI parser'
# compatible options: linux, darwin, cygwin, win32, aix, freebsd
compatible = ['linux', 'darwin', 'cygwin', 'win32', 'aix', 'freebsd']
__version__ = info.version
def parse(data, raw=False, quiet=False):
"""
Main text parsing function
Note: this is just a wrapper for jc.parsers.ini
Parameters:
data: (string) text data to parse
raw: (boolean) output preprocessed JSON if True
quiet: (boolean) suppress warning messages if True
Returns:
Dictionary representing the key/value file
"""
import jc.parsers.ini
return jc.parsers.ini.parse(data, raw=raw, quiet=quiet)

194
jc/parsers/last.py Normal file
View File

@@ -0,0 +1,194 @@
"""jc - JSON CLI output utility last Parser
Usage:
specify --last as the first argument if the piped input is coming from last or lastb
Compatibility:
'linux', 'darwin', 'aix', 'freebsd'
Examples:
$ last | jc --last -p
[
{
"user": "kbrazil",
"tty": "ttys002",
"hostname": null,
"login": "Thu Feb 27 14:31",
"logout": "still logged in"
},
{
"user": "kbrazil",
"tty": "ttys003",
"hostname": null,
"login": "Thu Feb 27 10:38",
"logout": "10:38",
"duration": "00:00"
},
{
"user": "kbrazil",
"tty": "ttys003",
"hostname": null,
"login": "Thu Feb 27 10:18",
"logout": "10:18",
"duration": "00:00"
},
...
]
$ last | jc --last -p -r
[
{
"user": "kbrazil",
"tty": "ttys002",
"hostname": "-",
"login": "Thu Feb 27 14:31",
"logout": "still_logged_in"
},
{
"user": "kbrazil",
"tty": "ttys003",
"hostname": "-",
"login": "Thu Feb 27 10:38",
"logout": "10:38",
"duration": "00:00"
},
{
"user": "kbrazil",
"tty": "ttys003",
"hostname": "-",
"login": "Thu Feb 27 10:18",
"logout": "10:18",
"duration": "00:00"
},
...
]
"""
import re
import jc.utils
class info():
version = '1.3'
description = 'last and lastb command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
# details = 'enter any other details here'
# compatible options: linux, darwin, cygwin, win32, aix, freebsd
compatible = ['linux', 'darwin', 'aix', 'freebsd']
magic_commands = ['last', 'lastb']
__version__ = info.version
def process(proc_data):
"""
Final processing to conform to the schema.
Parameters:
proc_data: (dictionary) raw structured data to process
Returns:
List of dictionaries. Structured data with the following schema:
[
{
"user": string,
"tty": string,
"hostname": string,
"login": string,
"logout": string,
"duration": string
}
]
"""
for entry in proc_data:
if 'user' in entry and entry['user'] == 'boot_time':
entry['user'] = 'boot time'
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
if 'logout' in entry and entry['logout'] == 'still_logged_in':
entry['logout'] = 'still logged in'
if 'logout' in entry and entry['logout'] == 'gone_-_no_logout':
entry['logout'] = 'gone - no logout'
return proc_data
def parse(data, raw=False, quiet=False):
"""
Main text parsing function
Parameters:
data: (string) text data to parse
raw: (boolean) output preprocessed JSON if True
quiet: (boolean) suppress warning messages if True
Returns:
List of dictionaries. Raw or processed structured data.
"""
if not quiet:
jc.utils.compatibility(__name__, info.compatible)
raw_output = []
# Clear any blank lines
cleandata = list(filter(None, data.splitlines()))
if jc.utils.has_data(data):
for entry in cleandata:
output_line = {}
if entry.startswith('wtmp begins ') or entry.startswith('btmp begins ') or entry.startswith('utx.log begins '):
continue
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()
if re.match(r'[MTWFS][ouerha][nedritnu] [JFMASOND][aepuco][nbrynlgptvc]', ' '.join(linedata[2:4])):
linedata.insert(2, '-')
# 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['login'] = ' '.join(linedata[3:7])
if len(linedata) > 8:
output_line['logout'] = linedata[8]
if len(linedata) > 9:
output_line['duration'] = linedata[9].replace('(', '').replace(')', '')
raw_output.append(output_line)
if raw:
return raw_output
else:
return process(raw_output)

View File

@@ -1,16 +1,20 @@
"""jc - JSON CLI output utility ls Parser
Note: The -l or -b option of ls should be used to correctly parse filenames that include newline characters.
Since ls does not encode newlines in filenames when outputting to a pipe it will cause jc to see
multiple files instead of a single file if -l or -b is not used.
Usage:
specify --ls as the first argument if the piped input is coming from ls
ls options supported:
- None
- laR
-lbaR
--time-style=full-iso
- h file sizes will be available in text form with -r but larger file sizes
with human readable suffixes will be converted to Null in default view
since the parser attempts to convert this field to an integer.
-h file sizes will be available in text form with -r but larger file sizes
with human readable suffixes will be converted to Null in default view
since the parser attempts to convert this field to an integer.
Compatibility:
@@ -145,7 +149,7 @@ import jc.utils
class info():
version = '1.1'
version = '1.6'
description = 'ls command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
@@ -215,45 +219,64 @@ def parse(data, raw=False, quiet=False):
jc.utils.compatibility(__name__, info.compatible)
raw_output = []
warned = False
parent = ''
next_is_parent = False
new_section = False
linedata = data.splitlines()
# Delete first line if it starts with 'total 1234'
if linedata:
if re.match('^total [0-9]+', linedata[0]):
if jc.utils.has_data(data):
# Delete first line if it starts with 'total 1234'
if re.match(r'total [0-9]+', linedata[0]):
linedata.pop(0)
parent = ''
next_is_parent = False
# Look for parent line if glob or -R is used
if not re.match(r'[-dclpsbDCMnP?]([-r][-w][-xsS]){2}([-r][-w][-xtT])[+]?', linedata[0]) \
and linedata[0].endswith(':'):
parent = linedata.pop(0)[:-1]
# Pop following total line if it exists
if re.match(r'total [0-9]+', linedata[0]):
linedata.pop(0)
# Look for parent line if glob or -R is used
if not re.match('^[-dclpsbDCMnP?]([-r][-w][-xsS]){2}([-r][-w][-xtT])[+]?', linedata[0]) \
and linedata[0].endswith(':'):
parent = linedata.pop(0)[:-1]
# Pop following total line
linedata.pop(0)
if linedata:
# Check if -l was used to parse extra data
if re.match('^[-dclpsbDCMnP?]([-r][-w][-xsS]){2}([-r][-w][-xtT])[+]?', linedata[0]):
if re.match(r'[-dclpsbDCMnP?]([-r][-w][-xsS]){2}([-r][-w][-xtT])[+]?', linedata[0]):
for entry in linedata:
output_line = {}
parsed_line = entry.split(maxsplit=8)
if not re.match('^[-dclpsbDCMnP?]([-r][-w][-xsS]){2}([-r][-w][-xtT])[+]?', entry) \
if not re.match(r'[-dclpsbDCMnP?]([-r][-w][-xsS]){2}([-r][-w][-xtT])[+]?', entry) \
and entry.endswith(':'):
parent = entry[:-1]
new_section = True
# fixup to remove trailing \n in previous entry
raw_output[-1]['filename'] = raw_output[-1]['filename'][:-1]
continue
if re.match('^total [0-9]+', entry):
if re.match(r'total [0-9]+', entry):
new_section = False
continue
if entry == '':
# fix for OSX - doesn't print 'total xx' line if empty directory
if new_section and entry == '':
new_section = False
continue
# fixup for filenames with newlines
if not new_section \
and not re.match(r'[-dclpsbDCMnP?]([-r][-w][-xsS]){2}([-r][-w][-xtT])[+]?', entry):
raw_output[-1]['filename'] = raw_output[-1]['filename'] + '\n' + entry
continue
# split filenames and links
filename_field = parsed_line[8].split(' -> ')
if len(parsed_line) == 9:
filename_field = parsed_line[8].split(' -> ')
else:
# in case of filenames starting with a newline character
filename_field = ['']
# create list of dictionaries
output_line['filename'] = filename_field[0]
@@ -279,11 +302,15 @@ def parse(data, raw=False, quiet=False):
next_is_parent = True
continue
if next_is_parent:
if next_is_parent and entry.endswith(':'):
parent = entry[:-1]
next_is_parent = False
continue
if not quiet and next_is_parent and not entry.endswith(':') and not warned:
jc.utils.warning_message('Newline characters detected. Filenames probably corrupted. Use ls -l or -b instead.')
warned = True
output_line['filename'] = entry
if parent:

View File

@@ -216,7 +216,7 @@ import jc.parsers.universal
class info():
version = '1.3'
version = '1.5'
description = 'lsblk command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
@@ -327,20 +327,23 @@ def parse(data, raw=False, quiet=False):
if not quiet:
jc.utils.compatibility(__name__, info.compatible)
linedata = data.splitlines()
# Clear any blank lines
cleandata = list(filter(None, linedata))
cleandata = data.splitlines()
cleandata = list(filter(None, data.splitlines()))
raw_output = []
cleandata[0] = cleandata[0].lower()
cleandata[0] = cleandata[0].replace(':', '_')
cleandata[0] = cleandata[0].replace('-', '_')
if jc.utils.has_data(data):
raw_output = jc.parsers.universal.sparse_table_parse(cleandata)
cleandata = data.splitlines()
# clean up non-ascii characters, if any
for entry in raw_output:
entry['name'] = entry['name'].encode('ascii', errors='ignore').decode()
cleandata[0] = cleandata[0].lower()
cleandata[0] = cleandata[0].replace(':', '_')
cleandata[0] = cleandata[0].replace('-', '_')
raw_output = jc.parsers.universal.sparse_table_parse(cleandata)
# clean up non-ascii characters, if any
for entry in raw_output:
entry['name'] = entry['name'].encode('ascii', errors='ignore').decode()
if raw:
return raw_output

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