mirror of
https://github.com/kellyjonbrazil/jc.git
synced 2026-04-03 17:44:07 +02:00
Compare commits
383 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bed694fcf5 | ||
|
|
4b4af69fa1 | ||
|
|
9d96190a5b | ||
|
|
fa44d48c09 | ||
|
|
4ef961c278 | ||
|
|
292a837d5c | ||
|
|
aa7b915d84 | ||
|
|
c46fe73236 | ||
|
|
039b2c129c | ||
|
|
8f2e5e4808 | ||
|
|
c4da8e4f78 | ||
|
|
bcab9078a4 | ||
|
|
b3c6c1ea92 | ||
|
|
a3af8662bd | ||
|
|
35940d0bc8 | ||
|
|
26994cdcb7 | ||
|
|
017159a829 | ||
|
|
b4e9c85e08 | ||
|
|
189146cd84 | ||
|
|
af34153ffa | ||
|
|
bf2ff3ffbb | ||
|
|
6423c9efd6 | ||
|
|
58ab0d4ece | ||
|
|
83a738bf4d | ||
|
|
3640671fc6 | ||
|
|
1da623b30e | ||
|
|
b10ca64646 | ||
|
|
2128763ee6 | ||
|
|
a27e7ed39c | ||
|
|
f07b7eaa47 | ||
|
|
6ce18de84c | ||
|
|
8631b756e7 | ||
|
|
7414d98412 | ||
|
|
d7b19892e8 | ||
|
|
96df396eaf | ||
|
|
2f6f640317 | ||
|
|
c4a0a50f3a | ||
|
|
658f8a3842 | ||
|
|
bfb876a1e3 | ||
|
|
90c34b1f4e | ||
|
|
3f9164ea77 | ||
|
|
7fd6fecbf5 | ||
|
|
8029f72363 | ||
|
|
c7fdce5d3b | ||
|
|
84f48aa369 | ||
|
|
2e9a0a9c12 | ||
|
|
c1f6f2b950 | ||
|
|
ede21bca13 | ||
|
|
8dd9a9f9cb | ||
|
|
04f92cd133 | ||
|
|
8be8d2393b | ||
|
|
0a879681be | ||
|
|
2ca1587a49 | ||
|
|
ec2cd2d708 | ||
|
|
5d0dbece93 | ||
|
|
df1e4b414b | ||
|
|
40760991e7 | ||
|
|
464f5f86cf | ||
|
|
7b09e9fccd | ||
|
|
6cba7d4298 | ||
|
|
9730f62e49 | ||
|
|
e0c1c87f54 | ||
|
|
931b3d2b83 | ||
|
|
e5d561baee | ||
|
|
2867593e7a | ||
|
|
dd52fee563 | ||
|
|
8e1f885827 | ||
|
|
2d39a58f90 | ||
|
|
9c4fa2ae26 | ||
|
|
de52d84e82 | ||
|
|
ce9b55059a | ||
|
|
bcd370a6a0 | ||
|
|
c8216850ab | ||
|
|
f5feedb90b | ||
|
|
a4371cd187 | ||
|
|
9d5ba4c834 | ||
|
|
1639dee1bb | ||
|
|
9363f430f2 | ||
|
|
9192a09073 | ||
|
|
b915eb9755 | ||
|
|
1cfcc2b592 | ||
|
|
7138dd02b7 | ||
|
|
b4276643b7 | ||
|
|
2ef00763bf | ||
|
|
54364928fc | ||
|
|
09b3b4932b | ||
|
|
29d6670119 | ||
|
|
2f654b5f1a | ||
|
|
e53b9f5992 | ||
|
|
addb234e61 | ||
|
|
76eca3b659 | ||
|
|
f90dec4c0e | ||
|
|
8900a59d4c | ||
|
|
6685138200 | ||
|
|
4d3e65b980 | ||
|
|
e9282bb546 | ||
|
|
f5627a4594 | ||
|
|
81ffdb2510 | ||
|
|
4c00a99850 | ||
|
|
2bfcb45b28 | ||
|
|
ab0c10e791 | ||
|
|
2c1935115d | ||
|
|
d98e43dc78 | ||
|
|
9348988d64 | ||
|
|
1285c66467 | ||
|
|
b7191bbc13 | ||
|
|
98b97509f7 | ||
|
|
2b2b570490 | ||
|
|
cce2d1ff29 | ||
|
|
b79600c572 | ||
|
|
140f1a8543 | ||
|
|
e34657cfde | ||
|
|
99070fa607 | ||
|
|
2b46785b1f | ||
|
|
c72562524b | ||
|
|
b7dd6441c7 | ||
|
|
31fcc2f755 | ||
|
|
b391aa14bc | ||
|
|
d3c45debbb | ||
|
|
5b08469b87 | ||
|
|
4a77ec63a4 | ||
|
|
d13606b6dc | ||
|
|
05291c93bb | ||
|
|
8cf00a208e | ||
|
|
06d73c8876 | ||
|
|
649c646ea2 | ||
|
|
b7756d9250 | ||
|
|
1cd2cd954c | ||
|
|
72020b8da9 | ||
|
|
cf9720b749 | ||
|
|
967b9db7f9 | ||
|
|
bb3acb1182 | ||
|
|
560c7f7e6d | ||
|
|
79b2841764 | ||
|
|
a06a89cbd1 | ||
|
|
431bd969eb | ||
|
|
c87b722aec | ||
|
|
3688b8b014 | ||
|
|
07b8d9e0c0 | ||
|
|
7454b53e39 | ||
|
|
3d6a76024d | ||
|
|
421b980957 | ||
|
|
4a22e27d6a | ||
|
|
99f7842dee | ||
|
|
7f869b4b18 | ||
|
|
9665f4ee84 | ||
|
|
606904d48b | ||
|
|
3f5279b97c | ||
|
|
f5ec21e6ac | ||
|
|
578a284465 | ||
|
|
422e392d9d | ||
|
|
54dfffd34a | ||
|
|
cffba64d2b | ||
|
|
56a0c12a59 | ||
|
|
c174d3de18 | ||
|
|
a9c59ef9fc | ||
|
|
abdb9b2673 | ||
|
|
548aaab626 | ||
|
|
20571c87ae | ||
|
|
19e49200de | ||
|
|
d32f5c67a9 | ||
|
|
b83b626435 | ||
|
|
ab2c1b25ec | ||
|
|
f2d46313a4 | ||
|
|
87e4796a6c | ||
|
|
0014a5c2f4 | ||
|
|
7af56e0dad | ||
|
|
a5ae6e3c01 | ||
|
|
fe1a0d1faf | ||
|
|
302f05cdda | ||
|
|
c0044be7b0 | ||
|
|
0110078807 | ||
|
|
42eacb45f8 | ||
|
|
a43e2e1991 | ||
|
|
c8b721d4f6 | ||
|
|
d0bfddc3d9 | ||
|
|
6b925a16c8 | ||
|
|
89ebd9fc22 | ||
|
|
6b4ba66231 | ||
|
|
5b697dc381 | ||
|
|
9ba73c95d1 | ||
|
|
93aa390447 | ||
|
|
3cfb8945dd | ||
|
|
cd8d38f2a1 | ||
|
|
8ec8cd6294 | ||
|
|
c028113561 | ||
|
|
5f22e1c803 | ||
|
|
d3351787e5 | ||
|
|
e5bea9ae3b | ||
|
|
93c710abe9 | ||
|
|
c29e7cfe5c | ||
|
|
cb5c1ba00d | ||
|
|
9a012b94e1 | ||
|
|
400f5a44ec | ||
|
|
a2ab5bab91 | ||
|
|
fc8ab27361 | ||
|
|
59f19d33a5 | ||
|
|
dfc9618115 | ||
|
|
8e02e5c75a | ||
|
|
970493ab93 | ||
|
|
64d78956eb | ||
|
|
40c05346f4 | ||
|
|
e9b0bc1409 | ||
|
|
798e6bb7d9 | ||
|
|
12a370deed | ||
|
|
553bfbe1a0 | ||
|
|
52494321fc | ||
|
|
c6c9e06496 | ||
|
|
e3a6c05a58 | ||
|
|
391d06f68d | ||
|
|
99804ea06e | ||
|
|
51935deb2a | ||
|
|
b24d0c3a47 | ||
|
|
762a886d6f | ||
|
|
2c3e9ddfe4 | ||
|
|
c7cd2b63c8 | ||
|
|
f0528ea831 | ||
|
|
5bc5596f60 | ||
|
|
2c27ac46be | ||
|
|
caad840153 | ||
|
|
65bd7e2904 | ||
|
|
c3d7d7db12 | ||
|
|
5605310362 | ||
|
|
17b6efe82e | ||
|
|
a032ae56ae | ||
|
|
eab2f4b056 | ||
|
|
aff86ae6c7 | ||
|
|
7ece9ddc1a | ||
|
|
7cd048e839 | ||
|
|
1e22f610a3 | ||
|
|
5249c972ae | ||
|
|
fd45f856a0 | ||
|
|
c8ab40cd33 | ||
|
|
b2c872925b | ||
|
|
f48e229202 | ||
|
|
799fec92c3 | ||
|
|
87a41c2fca | ||
|
|
7f85de0c46 | ||
|
|
13661b1993 | ||
|
|
51d5c3892d | ||
|
|
e4eab4641a | ||
|
|
9b148e0ba3 | ||
|
|
de28932650 | ||
|
|
5f798d603e | ||
|
|
a0757b2dd3 | ||
|
|
498d51b4e8 | ||
|
|
b06b6bae3f | ||
|
|
b5eaff2137 | ||
|
|
c01bcd3734 | ||
|
|
d75c4068ca | ||
|
|
6aa2d5a3d2 | ||
|
|
065276805f | ||
|
|
a63408c8cf | ||
|
|
69576f6bfa | ||
|
|
19845624e2 | ||
|
|
22ff2964e9 | ||
|
|
d96b3a65a9 | ||
|
|
4989445ef4 | ||
|
|
6770892acd | ||
|
|
d4eba8740f | ||
|
|
9f60760560 | ||
|
|
0a8f8ac934 | ||
|
|
6ae24c8244 | ||
|
|
d3679082a8 | ||
|
|
fb08b42dca | ||
|
|
4aeaa9f42a | ||
|
|
5f5693da04 | ||
|
|
5eb0f61727 | ||
|
|
958e998991 | ||
|
|
b78c1509f6 | ||
|
|
ce184d4d57 | ||
|
|
b4c3714ced | ||
|
|
5b7dfa0438 | ||
|
|
391a388476 | ||
|
|
d9c4e2ed4c | ||
|
|
0c42db38b1 | ||
|
|
2f9be8bf33 | ||
|
|
e8c00155e8 | ||
|
|
cc88fdd9ee | ||
|
|
d9de11ef1d | ||
|
|
0ceda97d09 | ||
|
|
d0dec92ba8 | ||
|
|
d420c008d8 | ||
|
|
f0b32db433 | ||
|
|
bc838eda59 | ||
|
|
afe55b6af0 | ||
|
|
dd3a3ac302 | ||
|
|
f9982a7947 | ||
|
|
07c1be9e9a | ||
|
|
f832b88755 | ||
|
|
0fac757efc | ||
|
|
fc15742065 | ||
|
|
6f2466a131 | ||
|
|
4b90e22f0a | ||
|
|
c493568785 | ||
|
|
1cdf004b77 | ||
|
|
a4ea504261 | ||
|
|
4c2c234c3b | ||
|
|
3d4c0f3e89 | ||
|
|
52fad02903 | ||
|
|
9dcabc057c | ||
|
|
db8c1079dd | ||
|
|
8f954673ab | ||
|
|
79522d1c7d | ||
|
|
a18bf03079 | ||
|
|
c02b6b5d82 | ||
|
|
f99b423284 | ||
|
|
d7d9d45d4f | ||
|
|
90065ec0cd | ||
|
|
51157ebb86 | ||
|
|
96d95c79ca | ||
|
|
e5da34c233 | ||
|
|
f09d657f77 | ||
|
|
0f4b0189f5 | ||
|
|
4666042abb | ||
|
|
027d544c2b | ||
|
|
f1967d0138 | ||
|
|
c1d896027d | ||
|
|
5c2d2a6618 | ||
|
|
997b269b0b | ||
|
|
61257e7525 | ||
|
|
53ee2c3631 | ||
|
|
8bfa0bddec | ||
|
|
ad61e6bc81 | ||
|
|
873b5ba8ac | ||
|
|
6ae50054e2 | ||
|
|
22a35f41bf | ||
|
|
961696c963 | ||
|
|
c7b7f1a5dc | ||
|
|
b5a0d650b1 | ||
|
|
573b279464 | ||
|
|
116e07f161 | ||
|
|
964868c8af | ||
|
|
c8dac32df8 | ||
|
|
72a0016bd8 | ||
|
|
2ad3167434 | ||
|
|
ddabfaa05c | ||
|
|
f857523ca7 | ||
|
|
00d53858e8 | ||
|
|
c008167e66 | ||
|
|
102344a041 | ||
|
|
c865298ef3 | ||
|
|
6ac03faf93 | ||
|
|
49c2701743 | ||
|
|
d1a271b08e | ||
|
|
7388ad19b9 | ||
|
|
2e63cb5fad | ||
|
|
e7f14d02b1 | ||
|
|
873771d05a | ||
|
|
d7de122e36 | ||
|
|
4ef0434f53 | ||
|
|
1aa2c99259 | ||
|
|
c2450b27b0 | ||
|
|
14d6d8b84f | ||
|
|
f0e3846c03 | ||
|
|
6ba64f1128 | ||
|
|
13bcdbc6c9 | ||
|
|
cfba62db20 | ||
|
|
18fb69e36e | ||
|
|
474eb0f3fd | ||
|
|
7f47b53370 | ||
|
|
dc2907d3ce | ||
|
|
1af85811e0 | ||
|
|
1c1b19a478 | ||
|
|
66942d64ba | ||
|
|
2fb6ae08d7 | ||
|
|
bf8811e03e | ||
|
|
c8b502c571 | ||
|
|
81c11a975c | ||
|
|
0d370eb403 | ||
|
|
7492c3f1e3 | ||
|
|
515a8a84b7 | ||
|
|
dd6680efb2 | ||
|
|
a7158373cd | ||
|
|
6d50ec7199 | ||
|
|
95dbf98e8e | ||
|
|
d49323e4eb | ||
|
|
08c1e2aec9 | ||
|
|
a2c137df2e | ||
|
|
fe27dcdb8f | ||
|
|
028e136161 | ||
|
|
9a85a0a4d5 | ||
|
|
3a1cbc4d50 |
31
.github/workflows/pythonapp.yml
vendored
Normal file
31
.github/workflows/pythonapp.yml
vendored
Normal 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
2
.gitignore
vendored
@@ -3,4 +3,4 @@ __pycache__
|
||||
dist/
|
||||
build/
|
||||
*.egg-info/
|
||||
jc/parsers.old/
|
||||
.github/
|
||||
|
||||
2
LICENSE.md
Executable file → Normal file
2
LICENSE.md
Executable file → Normal 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
|
||||
|
||||
115
changelog.txt
115
changelog.txt
@@ -1,5 +1,120 @@
|
||||
jc changelog
|
||||
|
||||
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 osx_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
|
||||
|
||||
14
docgen.sh
14
docgen.sh
@@ -4,15 +4,23 @@
|
||||
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.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 +28,30 @@ 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.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.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.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.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
|
||||
|
||||
109
docs/parsers/airport.md
Normal file
109
docs/parsers/airport.md
Normal file
@@ -0,0 +1,109 @@
|
||||
# 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(self, /, *args, **kwargs)
|
||||
```
|
||||
|
||||
## 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.
|
||||
|
||||
136
docs/parsers/airport_s.md
Normal file
136
docs/parsers/airport_s.md
Normal file
@@ -0,0 +1,136 @@
|
||||
# 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(self, /, *args, **kwargs)
|
||||
```
|
||||
|
||||
## 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.
|
||||
|
||||
@@ -59,6 +59,8 @@ Examples:
|
||||
"hwtype": "ether",
|
||||
"hwaddress": "00:50:56:f0:98:26",
|
||||
"iface": "ens33"
|
||||
"permanent": false,
|
||||
"expires": 1182
|
||||
},
|
||||
{
|
||||
"name": "gateway",
|
||||
@@ -66,6 +68,8 @@ Examples:
|
||||
"hwtype": "ether",
|
||||
"hwaddress": "00:50:56:f7:4a:fc",
|
||||
"iface": "ens33"
|
||||
"permanent": false,
|
||||
"expires": 110
|
||||
}
|
||||
]
|
||||
|
||||
@@ -77,6 +81,8 @@ Examples:
|
||||
"hwtype": "ether",
|
||||
"hwaddress": "00:50:56:fe:7a:b4",
|
||||
"iface": "ens33"
|
||||
"permanent": false,
|
||||
"expires": "1182"
|
||||
},
|
||||
{
|
||||
"name": "_gateway",
|
||||
@@ -84,6 +90,8 @@ Examples:
|
||||
"hwtype": "ether",
|
||||
"hwaddress": "00:50:56:f7:4a:fc",
|
||||
"iface": "ens33"
|
||||
"permanent": false,
|
||||
"expires": "110"
|
||||
}
|
||||
]
|
||||
|
||||
@@ -114,7 +122,9 @@ Returns:
|
||||
"hwtype": string,
|
||||
"hwaddress": string,
|
||||
"flags_mask": string,
|
||||
"iface": string
|
||||
"iface": string,
|
||||
"permanent": boolean,
|
||||
"expires": integer
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
148
docs/parsers/blkid.md
Normal file
148
docs/parsers/blkid.md
Normal file
@@ -0,0 +1,148 @@
|
||||
# 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(self, /, *args, **kwargs)
|
||||
```
|
||||
|
||||
## 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.
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
# jc.parsers.crontab
|
||||
jc - JSON CLI output utility crontab file Parser
|
||||
jc - JSON CLI output utility crontab command and file Parser
|
||||
|
||||
Usage:
|
||||
|
||||
specify --crontab as the first argument if the piped input is coming from a crontab file
|
||||
specify --crontab as the first argument if the piped input is coming from crontab -l or a crontab file
|
||||
|
||||
Compatibility:
|
||||
|
||||
@@ -11,7 +11,7 @@ Compatibility:
|
||||
|
||||
Examples:
|
||||
|
||||
$ cat /etc/crontab | jc --crontab -p
|
||||
$ crontab -l | jc --crontab -p
|
||||
{
|
||||
"variables": [
|
||||
{
|
||||
|
||||
@@ -11,7 +11,7 @@ Compatibility:
|
||||
|
||||
Examples:
|
||||
|
||||
$ cat /etc/crontab | jc --crontab -p
|
||||
$ cat /etc/crontab | jc --crontab-u -p
|
||||
{
|
||||
"variables": [
|
||||
{
|
||||
|
||||
105
docs/parsers/csv.md
Normal file
105
docs/parsers/csv.md
Normal file
@@ -0,0 +1,105 @@
|
||||
# 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(self, /, *args, **kwargs)
|
||||
```
|
||||
|
||||
## 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.
|
||||
|
||||
@@ -7,7 +7,7 @@ Usage:
|
||||
|
||||
Compatibility:
|
||||
|
||||
'linux', 'darwin'
|
||||
'linux', 'darwin', 'freebsd'
|
||||
|
||||
Examples:
|
||||
|
||||
|
||||
@@ -353,6 +353,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,6 +389,7 @@ Returns:
|
||||
"server": string,
|
||||
"when": string,
|
||||
"rcvd": integer
|
||||
"size": string
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
153
docs/parsers/dmidecode.md
Normal file
153
docs/parsers/dmidecode.md
Normal file
@@ -0,0 +1,153 @@
|
||||
# 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(self, /, *args, **kwargs)
|
||||
```
|
||||
|
||||
## 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.
|
||||
|
||||
90
docs/parsers/file.md
Normal file
90
docs/parsers/file.md
Normal file
@@ -0,0 +1,90 @@
|
||||
# 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(self, /, *args, **kwargs)
|
||||
```
|
||||
|
||||
## 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.
|
||||
|
||||
@@ -7,7 +7,7 @@ Usage:
|
||||
|
||||
Compatibility:
|
||||
|
||||
'linux'
|
||||
'linux', 'freebsd'
|
||||
|
||||
Examples:
|
||||
|
||||
|
||||
141
docs/parsers/group.md
Normal file
141
docs/parsers/group.md
Normal file
@@ -0,0 +1,141 @@
|
||||
# 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(self, /, *args, **kwargs)
|
||||
```
|
||||
|
||||
## 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.
|
||||
|
||||
109
docs/parsers/gshadow.md
Normal file
109
docs/parsers/gshadow.md
Normal file
@@ -0,0 +1,109 @@
|
||||
# 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(self, /, *args, **kwargs)
|
||||
```
|
||||
|
||||
## 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.
|
||||
|
||||
@@ -147,6 +147,17 @@ Examples:
|
||||
info(self, /, *args, **kwargs)
|
||||
```
|
||||
|
||||
## IfconfigParser
|
||||
```python
|
||||
IfconfigParser(self, console_output)
|
||||
```
|
||||
|
||||
## InterfaceNotFound
|
||||
```python
|
||||
InterfaceNotFound(self, /, *args, **kwargs)
|
||||
```
|
||||
|
||||
|
||||
## process
|
||||
```python
|
||||
process(proc_data)
|
||||
|
||||
118
docs/parsers/last.md
Normal file
118
docs/parsers/last.md
Normal file
@@ -0,0 +1,118 @@
|
||||
# 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(self, /, *args, **kwargs)
|
||||
```
|
||||
|
||||
## 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.
|
||||
|
||||
@@ -1,16 +1,21 @@
|
||||
# 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
|
||||
- la
|
||||
- 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.
|
||||
|
||||
-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.
|
||||
|
||||
Compatibility:
|
||||
|
||||
@@ -165,6 +170,7 @@ Returns:
|
||||
"filename": string,
|
||||
"flags": string,
|
||||
"links": integer,
|
||||
"parent": string,
|
||||
"owner": string,
|
||||
"group": string,
|
||||
"size": integer,
|
||||
|
||||
@@ -7,7 +7,7 @@ Usage:
|
||||
|
||||
Compatibility:
|
||||
|
||||
'linux', 'darwin'
|
||||
'linux', 'darwin', 'freebsd'
|
||||
|
||||
Example:
|
||||
|
||||
|
||||
@@ -5,13 +5,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,152 +166,73 @@ 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"
|
||||
},
|
||||
{
|
||||
"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"
|
||||
},
|
||||
{
|
||||
"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"
|
||||
"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"
|
||||
}
|
||||
]
|
||||
|
||||
$ 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"
|
||||
},
|
||||
{
|
||||
"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"
|
||||
},
|
||||
...
|
||||
"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
|
||||
@@ -331,28 +257,100 @@ 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,
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
235
docs/parsers/ntpq.md
Normal file
235
docs/parsers/ntpq.md
Normal file
@@ -0,0 +1,235 @@
|
||||
# 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(self, /, *args, **kwargs)
|
||||
```
|
||||
|
||||
## 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.
|
||||
|
||||
126
docs/parsers/passwd.md
Normal file
126
docs/parsers/passwd.md
Normal file
@@ -0,0 +1,126 @@
|
||||
# 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(self, /, *args, **kwargs)
|
||||
```
|
||||
|
||||
## 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.
|
||||
|
||||
@@ -119,17 +119,20 @@ 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
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
133
docs/parsers/shadow.md
Normal file
133
docs/parsers/shadow.md
Normal file
@@ -0,0 +1,133 @@
|
||||
# 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(self, /, *args, **kwargs)
|
||||
```
|
||||
|
||||
## 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.
|
||||
|
||||
@@ -7,7 +7,7 @@ Usage:
|
||||
|
||||
Compatibility:
|
||||
|
||||
'linux'
|
||||
'linux', 'darwin', 'freebsd'
|
||||
|
||||
Examples:
|
||||
|
||||
@@ -141,7 +141,11 @@ Returns:
|
||||
"access_time": string, # - = null
|
||||
"modify_time": string, # - = null
|
||||
"change_time": string, # - = null
|
||||
"birth_time": string # - = null
|
||||
"birth_time": string, # - = null
|
||||
"osx_device": integer,
|
||||
"rdev": integer,
|
||||
"block_size": integer,
|
||||
"osx_flags": string
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
87
docs/parsers/timedatectl.md
Normal file
87
docs/parsers/timedatectl.md
Normal file
@@ -0,0 +1,87 @@
|
||||
# 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(self, /, *args, **kwargs)
|
||||
```
|
||||
|
||||
## 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.
|
||||
|
||||
@@ -11,7 +11,7 @@ Limitations:
|
||||
|
||||
Compatibility:
|
||||
|
||||
'linux', 'darwin'
|
||||
'linux', 'darwin', 'freebsd'
|
||||
|
||||
Example:
|
||||
|
||||
|
||||
152
docs/parsers/who.md
Normal file
152
docs/parsers/who.md
Normal file
@@ -0,0 +1,152 @@
|
||||
# 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(self, /, *args, **kwargs)
|
||||
```
|
||||
|
||||
## 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.
|
||||
|
||||
257
jc/cli.py
257
jc/cli.py
@@ -1,24 +1,48 @@
|
||||
#!/usr/bin/env python3
|
||||
"""jc - JSON CLI output utility
|
||||
JC cli module
|
||||
"""
|
||||
import sys
|
||||
import os
|
||||
import shlex
|
||||
import importlib
|
||||
import textwrap
|
||||
import signal
|
||||
import json
|
||||
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.utils
|
||||
|
||||
|
||||
class info():
|
||||
version = '1.11.2'
|
||||
description = 'jc cli output JSON conversion tool'
|
||||
author = 'Kelly Brazil'
|
||||
author_email = 'kellyjonbrazil@gmail.com'
|
||||
|
||||
|
||||
__version__ = info.version
|
||||
|
||||
parsers = [
|
||||
'airport',
|
||||
'airport-s',
|
||||
'arp',
|
||||
'blkid',
|
||||
'crontab',
|
||||
'crontab-u',
|
||||
'csv',
|
||||
'df',
|
||||
'dig',
|
||||
'dmidecode',
|
||||
'du',
|
||||
'env',
|
||||
'file',
|
||||
'free',
|
||||
'fstab',
|
||||
'group',
|
||||
'gshadow',
|
||||
'history',
|
||||
'hosts',
|
||||
'id',
|
||||
@@ -26,30 +50,98 @@ parsers = [
|
||||
'ini',
|
||||
'iptables',
|
||||
'jobs',
|
||||
'last',
|
||||
'ls',
|
||||
'lsblk',
|
||||
'lsmod',
|
||||
'lsof',
|
||||
'mount',
|
||||
'netstat',
|
||||
'ntpq',
|
||||
'passwd',
|
||||
'pip-list',
|
||||
'pip-show',
|
||||
'ps',
|
||||
'route',
|
||||
'shadow',
|
||||
'ss',
|
||||
'stat',
|
||||
'systemctl',
|
||||
'systemctl-lj',
|
||||
'systemctl-ls',
|
||||
'systemctl-luf',
|
||||
'timedatectl',
|
||||
'uname',
|
||||
'uptime',
|
||||
'w',
|
||||
'who',
|
||||
'xml',
|
||||
'yaml'
|
||||
]
|
||||
|
||||
|
||||
def set_env_colors():
|
||||
"""
|
||||
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
|
||||
|
||||
"""
|
||||
env_colors = os.getenv('JC_COLORS')
|
||||
input_error = False
|
||||
|
||||
if env_colors:
|
||||
color_list = env_colors.split(',')
|
||||
else:
|
||||
input_error = True
|
||||
|
||||
if env_colors and len(color_list) != 4:
|
||||
print('jc: Warning: could not parse JC_COLORS environment variable\n', file=sys.stderr)
|
||||
input_error = True
|
||||
|
||||
if env_colors:
|
||||
for color in color_list:
|
||||
if color not in ['black', 'red', 'green', 'yellow', 'blue', 'magenta', 'cyan', 'gray', 'brightblack', 'brightred',
|
||||
'brightgreen', 'brightyellow', 'brightblue', 'brightmagenta', 'brightcyan', 'white', 'default']:
|
||||
print('jc: Warning: could not parse JC_COLORS environment variable\n', file=sys.stderr)
|
||||
input_error = True
|
||||
|
||||
# if there is an issue with the env variable, just set all colors to default and move on
|
||||
if input_error:
|
||||
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 ansi{color_list[0]}' if not color_list[0] == 'default' else f'bold ansiblue', # key names
|
||||
Keyword: f'ansi{color_list[1]}' if not color_list[1] == 'default' else f'ansibrightblack', # true, false, null
|
||||
Number: f'ansi{color_list[2]}' if not color_list[2] == 'default' else f'ansimagenta', # numbers
|
||||
String: f'ansi{color_list[3]}' if not color_list[3] == 'default' else f'ansigreen' # strings
|
||||
}
|
||||
|
||||
|
||||
def piped_output():
|
||||
"""returns 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"""
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def parser_shortname(parser_argument):
|
||||
"""short name of the parser with dashes and no -- prefix"""
|
||||
return parser_argument[2:]
|
||||
@@ -66,26 +158,13 @@ def parser_mod_shortname(parser):
|
||||
|
||||
|
||||
def parser_module(parser):
|
||||
"""import the module just in time and present the module object"""
|
||||
"""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))
|
||||
|
||||
|
||||
class info():
|
||||
version = '1.7.1'
|
||||
description = 'jc cli output JSON conversion tool'
|
||||
author = 'Kelly Brazil'
|
||||
author_email = 'kellyjonbrazil@gmail.com'
|
||||
|
||||
|
||||
__version__ = info.version
|
||||
|
||||
|
||||
def ctrlc(signum, frame):
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def parsers_text(indent=0, pad=0):
|
||||
"""return the argument and description information from each parser"""
|
||||
ptext = ''
|
||||
for parser in parsers:
|
||||
parser_arg = parser_argument(parser)
|
||||
@@ -103,6 +182,7 @@ def parsers_text(indent=0, pad=0):
|
||||
|
||||
|
||||
def about_jc():
|
||||
"""return jc info and the contents of each parser.info as a dictionary"""
|
||||
parser_list = []
|
||||
|
||||
for parser in parsers:
|
||||
@@ -132,59 +212,153 @@ def about_jc():
|
||||
|
||||
|
||||
def helptext(message):
|
||||
"""return the help text with the list of parsers"""
|
||||
parsers_string = parsers_text(indent=12, pad=17)
|
||||
|
||||
helptext_string = f'''
|
||||
jc: {message}
|
||||
|
||||
Usage: jc PARSER [OPTIONS]
|
||||
Usage: COMMAND | jc PARSER [OPTIONS]
|
||||
|
||||
or magic syntax:
|
||||
|
||||
jc [OPTIONS] COMMAND
|
||||
|
||||
Parsers:
|
||||
{parsers_string}
|
||||
Options:
|
||||
-a about jc
|
||||
-d debug - show trace messages
|
||||
-m monochrome output
|
||||
-p pretty print output
|
||||
-q quiet - suppress warnings
|
||||
-r raw JSON output
|
||||
|
||||
Example:
|
||||
ls -al | jc --ls -p
|
||||
|
||||
or using the magic syntax:
|
||||
|
||||
jc -p ls -al
|
||||
'''
|
||||
print(textwrap.dedent(helptext_string), file=sys.stderr)
|
||||
|
||||
|
||||
def json_out(data, pretty=False):
|
||||
if pretty:
|
||||
print(json.dumps(data, indent=2))
|
||||
def json_out(data, pretty=False, mono=False, piped_out=False):
|
||||
# set colors
|
||||
class JcStyle(Style):
|
||||
styles = set_env_colors()
|
||||
|
||||
|
||||
if not mono and not piped_out:
|
||||
if pretty:
|
||||
print(highlight(json.dumps(data, indent=2), JsonLexer(), Terminal256Formatter(style=JcStyle))[0:-1])
|
||||
else:
|
||||
print(highlight(json.dumps(data), JsonLexer(), Terminal256Formatter(style=JcStyle))[0:-1])
|
||||
else:
|
||||
print(json.dumps(data))
|
||||
if pretty:
|
||||
print(json.dumps(data, indent=2))
|
||||
else:
|
||||
print(json.dumps(data))
|
||||
|
||||
|
||||
def generate_magic_command(args):
|
||||
"""
|
||||
Returns 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():
|
||||
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:
|
||||
helptext(f'parser not found for "{run_command}"')
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def main():
|
||||
# break on ctrl-c keyboard interrupt
|
||||
signal.signal(signal.SIGINT, ctrlc)
|
||||
|
||||
debug = False
|
||||
pretty = False
|
||||
quiet = False
|
||||
raw = False
|
||||
# break on pipe error. need try/except for windows compatibility
|
||||
try:
|
||||
signal.signal(signal.SIGPIPE, signal.SIG_DFL)
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
# try magic syntax first: e.g. jc -p ls -al
|
||||
magic()
|
||||
|
||||
options = []
|
||||
|
||||
# options
|
||||
if '-d' in sys.argv:
|
||||
debug = True
|
||||
for opt in sys.argv:
|
||||
if opt.startswith('-') and not opt.startswith('--'):
|
||||
options.extend(opt[1:])
|
||||
|
||||
if '-p' in sys.argv:
|
||||
pretty = True
|
||||
debug = 'd' in options
|
||||
mono = 'm' in options
|
||||
pretty = 'p' in options
|
||||
quiet = 'q' in options
|
||||
raw = 'r' in options
|
||||
|
||||
if '-q' in sys.argv:
|
||||
quiet = True
|
||||
|
||||
if '-r' in sys.argv:
|
||||
raw = True
|
||||
|
||||
if '-a' in sys.argv:
|
||||
json_out(about_jc(), pretty=pretty)
|
||||
exit()
|
||||
if 'a' in options:
|
||||
json_out(about_jc(), pretty=pretty, mono=mono, piped_out=piped_output())
|
||||
sys.exit(0)
|
||||
|
||||
if sys.stdin.isatty():
|
||||
helptext('missing piped data')
|
||||
@@ -215,15 +389,16 @@ def main():
|
||||
result = parser.parse(data, raw=raw, quiet=quiet)
|
||||
found = True
|
||||
break
|
||||
except:
|
||||
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:
|
||||
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.')
|
||||
sys.exit(1)
|
||||
|
||||
if not found:
|
||||
helptext('missing or incorrect arguments')
|
||||
sys.exit(1)
|
||||
|
||||
json_out(result, pretty=pretty)
|
||||
json_out(result, pretty=pretty, mono=mono, piped_out=piped_output())
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
141
jc/parsers/airport.py
Normal file
141
jc/parsers/airport.py
Normal file
@@ -0,0 +1,141 @@
|
||||
"""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.0'
|
||||
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 = {}
|
||||
|
||||
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)
|
||||
186
jc/parsers/airport_s.py
Normal file
186
jc/parsers/airport_s.py
Normal file
@@ -0,0 +1,186 @@
|
||||
"""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.0'
|
||||
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)
|
||||
|
||||
cleandata = data.splitlines()
|
||||
|
||||
# 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)
|
||||
@@ -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,13 +99,14 @@ import jc.parsers.universal
|
||||
|
||||
|
||||
class info():
|
||||
version = '1.1'
|
||||
description = 'arp parser'
|
||||
version = '1.4'
|
||||
description = 'arp 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 = ['arp']
|
||||
|
||||
|
||||
__version__ = info.version
|
||||
@@ -122,7 +131,9 @@ def process(proc_data):
|
||||
"hwtype": string,
|
||||
"hwaddress": string,
|
||||
"flags_mask": string,
|
||||
"iface": string
|
||||
"iface": string,
|
||||
"permanent": boolean,
|
||||
"expires": integer
|
||||
}
|
||||
]
|
||||
"""
|
||||
@@ -132,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
|
||||
|
||||
|
||||
@@ -155,20 +174,30 @@ def parse(data, raw=False, quiet=False):
|
||||
cleandata = data.splitlines()
|
||||
|
||||
# remove final Entries row if -v was used
|
||||
if cleandata[-1].find('Entries:') == 0:
|
||||
if cleandata[-1].startswith('Entries:'):
|
||||
cleandata.pop(-1)
|
||||
|
||||
# detect if osx style was used
|
||||
if cleandata[0].find(' ifscope ') != -1:
|
||||
# detect if freebsd/osx style was used
|
||||
if cleandata[0][-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]
|
||||
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:
|
||||
@@ -177,7 +206,7 @@ def parse(data, raw=False, quiet=False):
|
||||
return process(raw_output)
|
||||
|
||||
# detect if linux style was used
|
||||
elif cleandata[0].find('Address') == 0:
|
||||
elif cleandata[0].startswith('Address'):
|
||||
|
||||
# fix header row to change Flags Mask to flags_mask
|
||||
cleandata[0] = cleandata[0].replace('Flags Mask', 'flags_mask')
|
||||
@@ -195,12 +224,13 @@ def parse(data, raw=False, quiet=False):
|
||||
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]
|
||||
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)
|
||||
|
||||
if raw:
|
||||
|
||||
218
jc/parsers/blkid.py
Normal file
218
jc/parsers/blkid.py
Normal file
@@ -0,0 +1,218 @@
|
||||
"""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.0'
|
||||
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 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)
|
||||
@@ -1,8 +1,8 @@
|
||||
"""jc - JSON CLI output utility crontab file Parser
|
||||
"""jc - JSON CLI output utility crontab command and file Parser
|
||||
|
||||
Usage:
|
||||
|
||||
specify --crontab as the first argument if the piped input is coming from a crontab file
|
||||
specify --crontab as the first argument if the piped input is coming from crontab -l or a crontab file
|
||||
|
||||
Compatibility:
|
||||
|
||||
@@ -10,7 +10,7 @@ Compatibility:
|
||||
|
||||
Examples:
|
||||
|
||||
$ cat /etc/crontab | jc --crontab -p
|
||||
$ crontab -l | jc --crontab -p
|
||||
{
|
||||
"variables": [
|
||||
{
|
||||
@@ -132,14 +132,15 @@ import jc.parsers.universal
|
||||
|
||||
|
||||
class info():
|
||||
version = '1.1'
|
||||
description = 'crontab file parser'
|
||||
version = '1.2'
|
||||
description = 'crontab command and 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']
|
||||
magic_commands = ['crontab']
|
||||
|
||||
|
||||
__version__ = info.version
|
||||
@@ -226,13 +227,13 @@ def parse(data, raw=False, quiet=False):
|
||||
|
||||
# Clear any commented lines
|
||||
for i, line in reversed(list(enumerate(cleandata))):
|
||||
if line.strip().find('#') == 0:
|
||||
if line.strip().startswith('#'):
|
||||
cleandata.pop(i)
|
||||
|
||||
# Pop any variable assignment lines
|
||||
cron_var = []
|
||||
for i, line in reversed(list(enumerate(cleandata))):
|
||||
if line.find('=') != -1:
|
||||
if '=' in line:
|
||||
var_line = cleandata.pop(i)
|
||||
var_name = var_line.split('=', maxsplit=1)[0].strip()
|
||||
var_value = var_line.split('=', maxsplit=1)[1].strip()
|
||||
|
||||
@@ -10,7 +10,7 @@ Compatibility:
|
||||
|
||||
Examples:
|
||||
|
||||
$ cat /etc/crontab | jc --crontab -p
|
||||
$ cat /etc/crontab | jc --crontab-u -p
|
||||
{
|
||||
"variables": [
|
||||
{
|
||||
@@ -133,7 +133,7 @@ import jc.parsers.universal
|
||||
|
||||
|
||||
class info():
|
||||
version = '1.0'
|
||||
version = '1.1'
|
||||
description = 'crontab file parser with user support'
|
||||
author = 'Kelly Brazil'
|
||||
author_email = 'kellyjonbrazil@gmail.com'
|
||||
@@ -228,13 +228,13 @@ def parse(data, raw=False, quiet=False):
|
||||
|
||||
# Clear any commented lines
|
||||
for i, line in reversed(list(enumerate(cleandata))):
|
||||
if line.strip().find('#') == 0:
|
||||
if line.strip().startswith('#'):
|
||||
cleandata.pop(i)
|
||||
|
||||
# Pop any variable assignment lines
|
||||
cron_var = []
|
||||
for i, line in reversed(list(enumerate(cleandata))):
|
||||
if line.find('=') != -1:
|
||||
if '=' in line:
|
||||
var_line = cleandata.pop(i)
|
||||
var_name = var_line.split('=', maxsplit=1)[0].strip()
|
||||
var_value = var_line.split('=', maxsplit=1)[1].strip()
|
||||
|
||||
141
jc/parsers/csv.py
Normal file
141
jc/parsers/csv.py
Normal file
@@ -0,0 +1,141 @@
|
||||
"""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.0'
|
||||
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 cleandata:
|
||||
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)
|
||||
@@ -6,7 +6,7 @@ Usage:
|
||||
|
||||
Compatibility:
|
||||
|
||||
'linux', 'darwin'
|
||||
'linux', 'darwin', 'freebsd'
|
||||
|
||||
Examples:
|
||||
|
||||
@@ -73,13 +73,14 @@ import jc.parsers.universal
|
||||
|
||||
|
||||
class info():
|
||||
version = '1.1'
|
||||
description = 'df parser'
|
||||
version = '1.3'
|
||||
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']
|
||||
|
||||
|
||||
__version__ = info.version
|
||||
@@ -134,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
|
||||
|
||||
@@ -324,13 +324,14 @@ import jc.utils
|
||||
|
||||
|
||||
class info():
|
||||
version = '1.0'
|
||||
description = 'dig parser'
|
||||
version = '1.2'
|
||||
description = 'dig 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 = ['dig']
|
||||
|
||||
|
||||
__version__ = info.version
|
||||
@@ -360,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,
|
||||
@@ -387,6 +397,7 @@ def process(proc_data):
|
||||
"server": string,
|
||||
"when": string,
|
||||
"rcvd": integer
|
||||
"size": string
|
||||
}
|
||||
]
|
||||
"""
|
||||
@@ -401,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:
|
||||
@@ -507,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
|
||||
@@ -533,23 +571,38 @@ 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:
|
||||
if line.startswith('; <<>> ') and ' axfr ' in line.lower():
|
||||
question = False
|
||||
authority = False
|
||||
answer = False
|
||||
axfr = True
|
||||
axfr_list = []
|
||||
continue
|
||||
|
||||
if ';' not in line and axfr:
|
||||
axfr_list.append(parse_axfr(line))
|
||||
output_entry.update({'axfr': axfr_list})
|
||||
continue
|
||||
|
||||
if line.startswith(';; ->>HEADER<<-'):
|
||||
output_entry = {}
|
||||
output_entry.update(parse_header(line))
|
||||
continue
|
||||
|
||||
if line.find(';; flags:') == 0:
|
||||
if line.startswith(';; flags:'):
|
||||
output_entry.update(parse_flags_line(line))
|
||||
continue
|
||||
|
||||
if line.find(';; QUESTION SECTION:') == 0:
|
||||
if line.startswith(';; QUESTION SECTION:'):
|
||||
question = True
|
||||
authority = False
|
||||
answer = False
|
||||
axfr = False
|
||||
continue
|
||||
|
||||
if question:
|
||||
@@ -557,52 +610,60 @@ def parse(data, raw=False, quiet=False):
|
||||
question = False
|
||||
authority = False
|
||||
answer = False
|
||||
axfr = False
|
||||
continue
|
||||
|
||||
if line.find(';; AUTHORITY SECTION:') == 0:
|
||||
if line.startswith(';; AUTHORITY SECTION:'):
|
||||
question = False
|
||||
authority = True
|
||||
answer = False
|
||||
axfr = False
|
||||
authority_list = []
|
||||
continue
|
||||
|
||||
if line.find(';') == -1 and authority:
|
||||
if ';' not in line and authority:
|
||||
authority_list.append(parse_authority(line))
|
||||
output_entry.update({'authority': authority_list})
|
||||
continue
|
||||
|
||||
if line.find(';; ANSWER SECTION:') == 0:
|
||||
if line.startswith(';; ANSWER SECTION:'):
|
||||
question = False
|
||||
authority = False
|
||||
answer = True
|
||||
axfr = False
|
||||
answer_list = []
|
||||
continue
|
||||
|
||||
if line.find(';') == -1 and answer:
|
||||
if ';' not in line and answer:
|
||||
answer_list.append(parse_answer(line))
|
||||
output_entry.update({'answer': answer_list})
|
||||
continue
|
||||
|
||||
# footer consists of 4 lines
|
||||
# footer line 1
|
||||
if line.find(';; Query time:') == 0:
|
||||
if line.startswith(';; Query time:'):
|
||||
output_entry.update({'query_time': line.split(':')[1].lstrip()})
|
||||
continue
|
||||
|
||||
# footer line 2
|
||||
if line.find(';; SERVER:') == 0:
|
||||
if line.startswith(';; SERVER:'):
|
||||
output_entry.update({'server': line.split(':')[1].lstrip()})
|
||||
continue
|
||||
|
||||
# footer line 3
|
||||
if line.find(';; WHEN:') == 0:
|
||||
if line.startswith(';; WHEN:'):
|
||||
output_entry.update({'when': line.split(':', maxsplit=1)[1].lstrip()})
|
||||
continue
|
||||
|
||||
# footer line 4 (last line)
|
||||
if line.find(';; MSG SIZE rcvd:') == 0:
|
||||
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)
|
||||
|
||||
|
||||
339
jc/parsers/dmidecode.py
Normal file
339
jc/parsers/dmidecode.py
Normal file
@@ -0,0 +1,339 @@
|
||||
"""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.0'
|
||||
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 = []
|
||||
|
||||
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)
|
||||
@@ -74,13 +74,14 @@ import jc.parsers.universal
|
||||
|
||||
class info():
|
||||
version = '1.1'
|
||||
description = 'du parser'
|
||||
description = 'du 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 = ['du']
|
||||
|
||||
|
||||
__version__ = info.version
|
||||
|
||||
@@ -53,12 +53,13 @@ import jc.utils
|
||||
|
||||
class info():
|
||||
version = '1.1'
|
||||
description = 'env parser'
|
||||
description = 'env command parser'
|
||||
author = 'Kelly Brazil'
|
||||
author_email = 'kellyjonbrazil@gmail.com'
|
||||
|
||||
# compatible options: linux, darwin, cygwin, win32, aix, freebsd
|
||||
compatible = ['linux', 'darwin', 'cygwin', 'win32', 'aix', 'freebsd']
|
||||
magic_commands = ['env']
|
||||
|
||||
|
||||
__version__ = info.version
|
||||
|
||||
128
jc/parsers/file.py
Normal file
128
jc/parsers/file.py
Normal file
@@ -0,0 +1,128 @@
|
||||
"""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.1'
|
||||
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
|
||||
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)
|
||||
@@ -21,13 +21,14 @@ import jc.utils
|
||||
|
||||
class info():
|
||||
version = '1.0'
|
||||
description = 'foo parser'
|
||||
description = 'foo command parser'
|
||||
author = 'John Doe'
|
||||
author_email = 'johndoe@gmail.com'
|
||||
# details = 'enter any other details here'
|
||||
|
||||
# compatible options: linux, darwin, cygwin, win32, aix, freebsd
|
||||
compatible = ['linux', 'darwin', 'cygwin', 'win32', 'aix', 'freebsd']
|
||||
magic_commands = ['foo']
|
||||
|
||||
|
||||
__version__ = info.version
|
||||
@@ -76,12 +77,8 @@ 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 cleandata:
|
||||
for line in filter(None, data.splitlines()):
|
||||
# parse the content
|
||||
pass
|
||||
|
||||
|
||||
@@ -54,12 +54,13 @@ import jc.parsers.universal
|
||||
|
||||
class info():
|
||||
version = '1.0'
|
||||
description = 'free parser'
|
||||
description = 'free command parser'
|
||||
author = 'Kelly Brazil'
|
||||
author_email = 'kellyjonbrazil@gmail.com'
|
||||
|
||||
# compatible options: linux, darwin, cygwin, win32, aix, freebsd
|
||||
compatible = ['linux']
|
||||
magic_commands = ['free']
|
||||
|
||||
|
||||
__version__ = info.version
|
||||
|
||||
@@ -6,7 +6,7 @@ Usage:
|
||||
|
||||
Compatibility:
|
||||
|
||||
'linux'
|
||||
'linux', 'freebsd'
|
||||
|
||||
Examples:
|
||||
|
||||
@@ -70,13 +70,13 @@ import jc.utils
|
||||
|
||||
|
||||
class info():
|
||||
version = '1.0'
|
||||
description = '/etc/fstab file parser'
|
||||
version = '1.2'
|
||||
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
|
||||
@@ -145,7 +145,7 @@ def parse(data, raw=False, quiet=False):
|
||||
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)
|
||||
|
||||
190
jc/parsers/group.py
Normal file
190
jc/parsers/group.py
Normal file
@@ -0,0 +1,190 @@
|
||||
"""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.0'
|
||||
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 cleandata:
|
||||
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)
|
||||
152
jc/parsers/gshadow.py
Normal file
152
jc/parsers/gshadow.py
Normal file
@@ -0,0 +1,152 @@
|
||||
"""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.0'
|
||||
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 cleandata:
|
||||
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)
|
||||
@@ -40,14 +40,15 @@ Examples:
|
||||
...
|
||||
}
|
||||
"""
|
||||
import jc
|
||||
import jc.utils
|
||||
|
||||
|
||||
class info():
|
||||
version = '1.1'
|
||||
description = 'history parser'
|
||||
version = '1.2'
|
||||
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
|
||||
|
||||
|
||||
@@ -120,17 +111,14 @@ def parse(data, raw=False, quiet=False):
|
||||
# split lines and clear out any non-ascii chars
|
||||
linedata = data.encode('ascii', errors='ignore').decode().splitlines()
|
||||
|
||||
# Clear any blank lines
|
||||
cleandata = list(filter(None, linedata))
|
||||
|
||||
if cleandata:
|
||||
for entry in cleandata:
|
||||
try:
|
||||
parsed_line = entry.split(maxsplit=1)
|
||||
raw_output[parsed_line[0]] = parsed_line[1]
|
||||
except IndexError:
|
||||
# need to catch indexerror in case there is weird input from prior commands
|
||||
pass
|
||||
# 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]
|
||||
except IndexError:
|
||||
# need to catch indexerror in case there is weird input from prior commands
|
||||
pass
|
||||
|
||||
if raw:
|
||||
return raw_output
|
||||
|
||||
@@ -61,7 +61,7 @@ import jc.utils
|
||||
|
||||
|
||||
class info():
|
||||
version = '1.0'
|
||||
version = '1.1'
|
||||
description = '/etc/hosts file parser'
|
||||
author = 'Kelly Brazil'
|
||||
author_email = 'kellyjonbrazil@gmail.com'
|
||||
@@ -126,7 +126,7 @@ def parse(data, raw=False, quiet=False):
|
||||
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
|
||||
|
||||
@@ -71,13 +71,14 @@ import jc.utils
|
||||
|
||||
class info():
|
||||
version = '1.0'
|
||||
description = 'id parser'
|
||||
description = 'id 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 = ['id']
|
||||
|
||||
|
||||
__version__ = info.version
|
||||
|
||||
@@ -141,24 +141,242 @@ Examples:
|
||||
}
|
||||
]
|
||||
"""
|
||||
import re
|
||||
from collections import namedtuple
|
||||
import jc.utils
|
||||
from ifconfigparser import IfconfigParser
|
||||
|
||||
|
||||
class info():
|
||||
version = '1.5'
|
||||
description = 'ifconfig parser'
|
||||
version = '1.7'
|
||||
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']
|
||||
magic_commands = ['ifconfig']
|
||||
|
||||
|
||||
__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.
|
||||
@@ -219,7 +437,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)])
|
||||
|
||||
@@ -134,13 +134,14 @@ import jc.utils
|
||||
|
||||
|
||||
class info():
|
||||
version = '1.1'
|
||||
description = 'iptables parser'
|
||||
version = '1.2'
|
||||
description = 'iptables command parser'
|
||||
author = 'Kelly Brazil'
|
||||
author_email = 'kellyjonbrazil@gmail.com'
|
||||
|
||||
# compatible options: linux, darwin, cygwin, win32, aix, freebsd
|
||||
compatible = ['linux']
|
||||
magic_commands = ['iptables']
|
||||
|
||||
|
||||
__version__ = info.version
|
||||
@@ -246,7 +247,7 @@ def parse(data, raw=False, quiet=False):
|
||||
|
||||
for line in cleandata:
|
||||
|
||||
if line.find('Chain') == 0:
|
||||
if line.startswith('Chain'):
|
||||
raw_output.append(chain)
|
||||
chain = {}
|
||||
headers = []
|
||||
@@ -258,7 +259,7 @@ def parse(data, raw=False, quiet=False):
|
||||
|
||||
continue
|
||||
|
||||
elif line.find('target') == 0 or line.find('pkts') == 1 or line.find('num') == 0:
|
||||
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")
|
||||
|
||||
@@ -77,13 +77,14 @@ import jc.utils
|
||||
|
||||
|
||||
class info():
|
||||
version = '1.0'
|
||||
description = 'jobs parser'
|
||||
version = '1.1'
|
||||
description = 'jobs command parser'
|
||||
author = 'Kelly Brazil'
|
||||
author_email = 'kellyjonbrazil@gmail.com'
|
||||
|
||||
# compatible options: linux, darwin, cygwin, win32, aix, freebsd
|
||||
compatible = ['linux', 'darwin', 'cygwin', 'aix', 'freebsd']
|
||||
magic_commands = ['jobs']
|
||||
|
||||
|
||||
__version__ = info.version
|
||||
@@ -175,11 +176,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('-')
|
||||
|
||||
|
||||
194
jc/parsers/last.py
Normal file
194
jc/parsers/last.py
Normal 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.2'
|
||||
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 = []
|
||||
cleandata = data.splitlines()
|
||||
|
||||
# Clear any blank lines
|
||||
cleandata = list(filter(None, cleandata))
|
||||
|
||||
if cleandata:
|
||||
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)
|
||||
@@ -1,15 +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
|
||||
- la
|
||||
- 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.
|
||||
|
||||
-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.
|
||||
|
||||
Compatibility:
|
||||
|
||||
@@ -144,13 +149,14 @@ import jc.utils
|
||||
|
||||
|
||||
class info():
|
||||
version = '1.0'
|
||||
description = 'ls parser'
|
||||
version = '1.4'
|
||||
description = 'ls command parser'
|
||||
author = 'Kelly Brazil'
|
||||
author_email = 'kellyjonbrazil@gmail.com'
|
||||
|
||||
# compatible options: linux, darwin, cygwin, win32, aix, freebsd
|
||||
compatible = ['linux', 'darwin', 'cygwin', 'aix', 'freebsd']
|
||||
magic_commands = ['ls']
|
||||
|
||||
|
||||
__version__ = info.version
|
||||
@@ -173,6 +179,7 @@ def process(proc_data):
|
||||
"filename": string,
|
||||
"flags": string,
|
||||
"links": integer,
|
||||
"parent": string,
|
||||
"owner": string,
|
||||
"group": string,
|
||||
"size": integer,
|
||||
@@ -212,27 +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'
|
||||
# Delete first line if it starts with 'total 1234'
|
||||
if linedata:
|
||||
if linedata[0].find('total') == 0:
|
||||
if re.match(r'total [0-9]+', linedata[0]):
|
||||
linedata.pop(0)
|
||||
|
||||
# Clear any blank lines
|
||||
cleandata = list(filter(None, linedata))
|
||||
# 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)
|
||||
|
||||
if cleandata:
|
||||
if linedata:
|
||||
# Check if -l was used to parse extra data
|
||||
if re.match('^[-dclpsbDCMnP?]([-r][-w][-xsS]){2}([-r][-w][-xtT])[+]?', cleandata[0]):
|
||||
for entry in cleandata:
|
||||
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(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(r'total [0-9]+', entry):
|
||||
new_section = False
|
||||
continue
|
||||
|
||||
# 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]
|
||||
@@ -240,6 +284,9 @@ def parse(data, raw=False, quiet=False):
|
||||
if len(filename_field) > 1:
|
||||
output_line['link_to'] = filename_field[1]
|
||||
|
||||
if parent:
|
||||
output_line['parent'] = parent
|
||||
|
||||
output_line['flags'] = parsed_line[0]
|
||||
output_line['links'] = parsed_line[1]
|
||||
output_line['owner'] = parsed_line[2]
|
||||
@@ -248,9 +295,27 @@ def parse(data, raw=False, quiet=False):
|
||||
output_line['date'] = ' '.join(parsed_line[5:8])
|
||||
raw_output.append(output_line)
|
||||
else:
|
||||
for entry in cleandata:
|
||||
for entry in linedata:
|
||||
output_line = {}
|
||||
|
||||
if entry == '':
|
||||
next_is_parent = True
|
||||
continue
|
||||
|
||||
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:
|
||||
output_line['parent'] = parent
|
||||
|
||||
raw_output.append(output_line)
|
||||
|
||||
if raw:
|
||||
|
||||
@@ -217,12 +217,13 @@ import jc.parsers.universal
|
||||
|
||||
class info():
|
||||
version = '1.3'
|
||||
description = 'lsblk parser'
|
||||
description = 'lsblk command parser'
|
||||
author = 'Kelly Brazil'
|
||||
author_email = 'kellyjonbrazil@gmail.com'
|
||||
|
||||
# compatible options: linux, darwin, cygwin, win32, aix, freebsd
|
||||
compatible = ['linux']
|
||||
magic_commands = ['lsblk']
|
||||
|
||||
|
||||
__version__ = info.version
|
||||
|
||||
@@ -108,12 +108,13 @@ import jc.parsers.universal
|
||||
|
||||
class info():
|
||||
version = '1.1'
|
||||
description = 'lsmod parser'
|
||||
description = 'lsmod command parser'
|
||||
author = 'Kelly Brazil'
|
||||
author_email = 'kellyjonbrazil@gmail.com'
|
||||
|
||||
# compatible options: linux, darwin, cygwin, win32, aix, freebsd
|
||||
compatible = ['linux']
|
||||
magic_commands = ['lsmod']
|
||||
|
||||
|
||||
__version__ = info.version
|
||||
|
||||
@@ -97,13 +97,14 @@ import jc.parsers.universal
|
||||
|
||||
|
||||
class info():
|
||||
version = '1.0'
|
||||
description = 'lsof parser'
|
||||
version = '1.1'
|
||||
description = 'lsof command parser'
|
||||
author = 'Kelly Brazil'
|
||||
author_email = 'kellyjonbrazil@gmail.com'
|
||||
|
||||
# compatible options: linux, darwin, cygwin, win32, aix, freebsd
|
||||
compatible = ['linux']
|
||||
magic_commands = ['lsof']
|
||||
|
||||
|
||||
__version__ = info.version
|
||||
@@ -179,47 +180,6 @@ def parse(data, raw=False, quiet=False):
|
||||
|
||||
raw_output = jc.parsers.universal.sparse_table_parse(cleandata)
|
||||
|
||||
'''
|
||||
# find column value of last character of each header
|
||||
header_text = cleandata.pop(0).lower()
|
||||
|
||||
# clean up 'size/off' header
|
||||
# even though forward slash in a key is valid json, it can make things difficult
|
||||
header_row = header_text.replace('/', '_')
|
||||
|
||||
headers = header_row.split()
|
||||
|
||||
header_spec = []
|
||||
for i, h in enumerate(headers):
|
||||
# header tuple is (index, header_name, col)
|
||||
header_spec.append((i, h, header_row.find(h) + len(h)))
|
||||
|
||||
# parse lines
|
||||
for entry in cleandata:
|
||||
output_line = {}
|
||||
|
||||
# normalize data by inserting Null for missing data
|
||||
temp_line = entry.split(maxsplit=len(headers) - 1)
|
||||
|
||||
for spec in header_spec:
|
||||
|
||||
index = spec[0]
|
||||
header_name = spec[1]
|
||||
col = spec[2] - 1 # subtract one since column starts at 0 instead of 1
|
||||
|
||||
if header_name == 'command' or header_name == 'name':
|
||||
continue
|
||||
if entry[col] in string.whitespace:
|
||||
temp_line.insert(index, None)
|
||||
|
||||
name = ' '.join(temp_line[9:])
|
||||
fixed_line = temp_line[0:9]
|
||||
fixed_line.append(name)
|
||||
|
||||
output_line = dict(zip(headers, fixed_line))
|
||||
raw_output.append(output_line)
|
||||
'''
|
||||
|
||||
if raw:
|
||||
return raw_output
|
||||
else:
|
||||
|
||||
@@ -6,7 +6,7 @@ Usage:
|
||||
|
||||
Compatibility:
|
||||
|
||||
'linux', 'darwin'
|
||||
'linux', 'darwin', 'freebsd'
|
||||
|
||||
Example:
|
||||
|
||||
@@ -56,13 +56,14 @@ import jc.utils
|
||||
|
||||
|
||||
class info():
|
||||
version = '1.1'
|
||||
description = 'mount parser'
|
||||
version = '1.3'
|
||||
description = 'mount 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 = ['mount']
|
||||
|
||||
|
||||
__version__ = info.version
|
||||
@@ -164,7 +165,7 @@ def parse(data, raw=False, quiet=False):
|
||||
|
||||
if cleandata:
|
||||
# check for OSX output
|
||||
if cleandata[0].find(' type ') == -1:
|
||||
if ' type ' not in cleandata[0]:
|
||||
raw_output = osx_parse(cleandata)
|
||||
|
||||
else:
|
||||
|
||||
@@ -4,13 +4,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",
|
||||
@@ -160,166 +165,86 @@ 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"
|
||||
},
|
||||
{
|
||||
"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"
|
||||
},
|
||||
{
|
||||
"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"
|
||||
"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"
|
||||
}
|
||||
]
|
||||
|
||||
$ 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"
|
||||
},
|
||||
{
|
||||
"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"
|
||||
},
|
||||
...
|
||||
"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"
|
||||
}
|
||||
]
|
||||
"""
|
||||
import string
|
||||
import jc.utils
|
||||
|
||||
|
||||
class info():
|
||||
version = '1.2'
|
||||
description = 'netstat parser'
|
||||
version = '1.6'
|
||||
description = 'netstat command parser'
|
||||
author = 'Kelly Brazil'
|
||||
author_email = 'kellyjonbrazil@gmail.com'
|
||||
|
||||
# compatible options: linux, darwin, cygwin, win32, aix, freebsd
|
||||
compatible = ['linux']
|
||||
compatible = ['linux', 'darwin', 'freebsd']
|
||||
magic_commands = ['netstat']
|
||||
|
||||
|
||||
__version__ = info.version
|
||||
@@ -339,34 +264,112 @@ def process(proc_data):
|
||||
|
||||
[
|
||||
{
|
||||
"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,
|
||||
}
|
||||
]
|
||||
"""
|
||||
for entry in proc_data:
|
||||
# integer changes
|
||||
int_list = ['recv_q', 'send_q', 'pid', 'refcnt', 'inode']
|
||||
int_list = ['recv_q', 'send_q', 'pid', 'refcnt', 'inode', 'unit', 'vendor', 'class',
|
||||
'osx_flags', 'subcla', 'pcbcount', 'rcvbuf', 'sndbuf', 'rxbytes', 'txbytes',
|
||||
'route_refs', 'use', 'mtu', 'mss', 'window', 'irtt', 'metric', 'ipkts',
|
||||
'ierrs', 'opkts', 'oerrs', 'coll', 'rx_ok', 'rx_err', 'rx_drp', 'rx_ovr',
|
||||
'tx_ok', 'tx_err', 'tx_drp', 'tx_ovr', 'idrop', 'ibytes', 'obytes', 'r_mbuf',
|
||||
's_mbuf', 'r_clus', 's_clus', 'r_hiwa', 's_hiwa', 'r_lowa', 's_lowa', 'r_bcnt',
|
||||
's_bcnt', 'r_bmax', 's_bmax', 'rexmit', 'ooorcv', '0_win']
|
||||
for key in int_list:
|
||||
if key in entry:
|
||||
try:
|
||||
@@ -375,6 +378,16 @@ def process(proc_data):
|
||||
except (ValueError):
|
||||
entry[key] = None
|
||||
|
||||
# float changes
|
||||
float_list = ['rexmt', 'persist', 'keep', '2msl', 'delack', 'rcvtime']
|
||||
for key in float_list:
|
||||
if key in entry:
|
||||
try:
|
||||
key_float = float(entry[key])
|
||||
entry[key] = key_float
|
||||
except (ValueError):
|
||||
entry[key] = None
|
||||
|
||||
if 'local_port' in entry:
|
||||
try:
|
||||
entry['local_port_num'] = int(entry['local_port'])
|
||||
@@ -390,128 +403,6 @@ def process(proc_data):
|
||||
return proc_data
|
||||
|
||||
|
||||
def normalize_headers(header):
|
||||
header = header.lower()
|
||||
header = header.replace('local address', 'local_address')
|
||||
header = header.replace('foreign address', 'foreign_address')
|
||||
header = header.replace('pid/program name', 'program_name')
|
||||
header = header.replace('security context', 'security_context')
|
||||
header = header.replace('i-node', 'inode')
|
||||
header = header.replace('-', '_')
|
||||
|
||||
return header
|
||||
|
||||
|
||||
def parse_network(headers, entry):
|
||||
# Count words in header
|
||||
# if len of line is one less than len of header, then insert None in field 5
|
||||
entry = entry.split(maxsplit=len(headers) - 1)
|
||||
|
||||
if len(entry) == len(headers) - 1:
|
||||
entry.insert(5, None)
|
||||
|
||||
output_line = dict(zip(headers, entry))
|
||||
output_line['kind'] = 'network'
|
||||
|
||||
return output_line
|
||||
|
||||
|
||||
def parse_socket(header_text, headers, entry):
|
||||
output_line = {}
|
||||
# get the column # of first letter of "state"
|
||||
state_col = header_text.find('state')
|
||||
# get the program name column area
|
||||
pn_start = header_text.find('program_name')
|
||||
pn_end = header_text.find('path') - 1
|
||||
|
||||
# remove [ and ] from each line
|
||||
entry = entry.replace('[ ]', '---')
|
||||
entry = entry.replace('[', ' ').replace(']', ' ')
|
||||
|
||||
# find program_name column area and substitute spaces with \u2063 there
|
||||
old_pn = entry[pn_start:pn_end]
|
||||
new_pn = old_pn.replace(' ', '\u2063')
|
||||
entry = entry.replace(old_pn, new_pn)
|
||||
|
||||
entry_list = entry.split(maxsplit=len(headers) - 1)
|
||||
# check column # to see if state column is populated
|
||||
if entry[state_col] in string.whitespace:
|
||||
entry_list.insert(4, None)
|
||||
|
||||
output_line = dict(zip(headers, entry_list))
|
||||
output_line['kind'] = 'socket'
|
||||
|
||||
# fix program_name field to turn \u2063 back to spaces
|
||||
if 'program_name' in output_line:
|
||||
if output_line['program_name']:
|
||||
old_d_pn = output_line['program_name']
|
||||
new_d_pn = old_d_pn.replace('\u2063', ' ')
|
||||
output_line['program_name'] = new_d_pn
|
||||
|
||||
return output_line
|
||||
|
||||
|
||||
def parse_post(raw_data):
|
||||
# clean up trailing whitespace on each item in each entry
|
||||
# flags --- = null
|
||||
# program_name - = null
|
||||
# split pid and program name and ip addresses and ports
|
||||
# create network and transport protocol fields
|
||||
|
||||
for entry in raw_data:
|
||||
for item in entry:
|
||||
try:
|
||||
entry[item] = entry[item].rstrip()
|
||||
except (AttributeError):
|
||||
# skips trying to rstrip Null entries
|
||||
pass
|
||||
|
||||
if 'flags' in entry:
|
||||
if entry['flags'] == '---':
|
||||
entry['flags'] = None
|
||||
|
||||
if 'program_name' in entry:
|
||||
entry['program_name'] = entry['program_name'].strip()
|
||||
if entry['program_name'] == '-':
|
||||
entry['program_name'] = None
|
||||
|
||||
if entry['program_name']:
|
||||
pid = entry['program_name'].split('/', maxsplit=1)[0]
|
||||
name = entry['program_name'].split('/', maxsplit=1)[1]
|
||||
entry['pid'] = pid
|
||||
entry['program_name'] = name
|
||||
|
||||
if 'local_address' in entry:
|
||||
if entry['local_address']:
|
||||
ladd = entry['local_address'].rsplit(':', maxsplit=1)[0]
|
||||
lport = entry['local_address'].rsplit(':', maxsplit=1)[1]
|
||||
entry['local_address'] = ladd
|
||||
entry['local_port'] = lport
|
||||
|
||||
if 'foreign_address' in entry:
|
||||
if entry['foreign_address']:
|
||||
fadd = entry['foreign_address'].rsplit(':', maxsplit=1)[0]
|
||||
fport = entry['foreign_address'].rsplit(':', maxsplit=1)[1]
|
||||
entry['foreign_address'] = fadd
|
||||
entry['foreign_port'] = fport
|
||||
|
||||
if 'proto' in entry and 'kind' in entry:
|
||||
if entry['kind'] == 'network':
|
||||
if entry['proto'].find('tcp') != -1:
|
||||
entry['transport_protocol'] = 'tcp'
|
||||
elif entry['proto'].find('udp') != -1:
|
||||
entry['transport_protocol'] = 'udp'
|
||||
else:
|
||||
entry['transport_protocol'] = None
|
||||
|
||||
if entry['proto'].find('6') != -1:
|
||||
entry['network_protocol'] = 'ipv6'
|
||||
else:
|
||||
entry['network_protocol'] = 'ipv4'
|
||||
|
||||
return raw_data
|
||||
|
||||
|
||||
def parse(data, raw=False, quiet=False):
|
||||
"""
|
||||
Main text parsing function
|
||||
@@ -526,51 +417,33 @@ def parse(data, raw=False, quiet=False):
|
||||
|
||||
List of dictionaries. Raw or processed structured data.
|
||||
"""
|
||||
import jc.utils
|
||||
if not quiet:
|
||||
jc.utils.compatibility(__name__, info.compatible)
|
||||
|
||||
cleandata = data.splitlines()
|
||||
cleandata = list(filter(None, cleandata))
|
||||
|
||||
raw_output = []
|
||||
network = False
|
||||
socket = False
|
||||
headers = ''
|
||||
network_list = []
|
||||
socket_list = []
|
||||
|
||||
for line in cleandata:
|
||||
# check for FreeBSD/OSX vs Linux
|
||||
# is this from FreeBSD/OSX?
|
||||
if cleandata[0] == 'Active Internet connections' \
|
||||
or cleandata[0] == 'Active Internet connections (including servers)' \
|
||||
or cleandata[0] == 'Active Multipath Internet connections' \
|
||||
or cleandata[0] == 'Active LOCAL (UNIX) domain sockets' \
|
||||
or cleandata[0] == 'Registered kernel control modules' \
|
||||
or cleandata[0] == 'Active kernel event sockets' \
|
||||
or cleandata[0] == 'Active kernel control sockets' \
|
||||
or cleandata[0] == 'Routing tables' \
|
||||
or cleandata[0].startswith('Name '):
|
||||
|
||||
if line.find('Active Internet') == 0:
|
||||
network_list = []
|
||||
network = True
|
||||
socket = False
|
||||
continue
|
||||
import jc.parsers.netstat_freebsd_osx
|
||||
raw_output = jc.parsers.netstat_freebsd_osx.parse(cleandata)
|
||||
|
||||
if line.find('Active UNIX') == 0:
|
||||
socket_list = []
|
||||
network = False
|
||||
socket = True
|
||||
continue
|
||||
|
||||
if line.find('Proto') == 0:
|
||||
header_text = normalize_headers(line)
|
||||
headers = header_text.split()
|
||||
continue
|
||||
|
||||
if network:
|
||||
network_list.append(parse_network(headers, line))
|
||||
continue
|
||||
|
||||
if socket:
|
||||
socket_list.append(parse_socket(header_text, headers, line))
|
||||
continue
|
||||
|
||||
for item in [network_list, socket_list]:
|
||||
for entry in item:
|
||||
raw_output.append(entry)
|
||||
|
||||
raw_output = parse_post(raw_output)
|
||||
# use linux parser
|
||||
else:
|
||||
import jc.parsers.netstat_linux
|
||||
raw_output = jc.parsers.netstat_linux.parse(cleandata)
|
||||
|
||||
if raw:
|
||||
return raw_output
|
||||
|
||||
320
jc/parsers/netstat_freebsd_osx.py
Normal file
320
jc/parsers/netstat_freebsd_osx.py
Normal file
@@ -0,0 +1,320 @@
|
||||
"""jc - JSON CLI output utility FreeBSD and OSX netstat Parser"""
|
||||
|
||||
|
||||
def normalize_headers(header):
|
||||
header = header.lower()
|
||||
header = header.replace('local address', 'local_address')
|
||||
header = header.replace('foreign address', 'foreign_address')
|
||||
header = header.replace('(state)', 'state')
|
||||
header = header.replace('inode', 'unix_inode')
|
||||
header = header.replace('flags', 'unix_flags')
|
||||
header = header.replace('-', '_')
|
||||
|
||||
return header
|
||||
|
||||
|
||||
def normalize_route_headers(header):
|
||||
header = header.lower()
|
||||
header = header.replace('flags', 'route_flags')
|
||||
header = header.replace('refs', 'route_refs')
|
||||
header = header.replace('netif', 'iface')
|
||||
header = header.replace('-', '_')
|
||||
|
||||
return header
|
||||
|
||||
|
||||
def normalize_interface_headers(header):
|
||||
header = header.lower()
|
||||
header = header.replace('name', 'iface')
|
||||
header = header.replace('-', '_')
|
||||
|
||||
return header
|
||||
|
||||
|
||||
def parse_item(headers, entry, kind):
|
||||
entry = entry.split(maxsplit=len(headers) - 1)
|
||||
|
||||
# fixup udp records with no state field entry
|
||||
if kind == 'network' and entry[0].startswith('udp'):
|
||||
entry.insert(5, None)
|
||||
|
||||
if kind == 'network' and 'socket' in headers and 'udp' in str(entry):
|
||||
entry.insert(7, None)
|
||||
|
||||
# fixup -T output on FreeBSD
|
||||
if kind == 'network' and '0_win' in headers and entry[0].startswith('udp'):
|
||||
entry.insert(1, '')
|
||||
entry.insert(1, '')
|
||||
entry.insert(1, '')
|
||||
|
||||
# fixup interface records with no address field entry
|
||||
if kind == 'interface' and len(entry) == 8:
|
||||
entry.insert(3, None)
|
||||
|
||||
output_line = dict(zip(headers, entry))
|
||||
output_line['kind'] = kind
|
||||
|
||||
return output_line
|
||||
|
||||
|
||||
def parse_post(raw_data):
|
||||
for entry in raw_data:
|
||||
# fixup name field in Registered kernel control module
|
||||
if 'name' in entry:
|
||||
if entry['name']:
|
||||
entry['name'] = entry['name'].strip()
|
||||
|
||||
# create network and transport protocol fields
|
||||
if 'local_address' in entry:
|
||||
if entry['local_address']:
|
||||
ladd = entry['local_address'].rsplit('.', maxsplit=1)[0]
|
||||
lport = entry['local_address'].rsplit('.', maxsplit=1)[1]
|
||||
entry['local_address'] = ladd
|
||||
entry['local_port'] = lport
|
||||
|
||||
if 'foreign_address' in entry:
|
||||
if entry['foreign_address']:
|
||||
fadd = entry['foreign_address'].rsplit('.', maxsplit=1)[0]
|
||||
fport = entry['foreign_address'].rsplit('.', maxsplit=1)[1]
|
||||
entry['foreign_address'] = fadd
|
||||
entry['foreign_port'] = fport
|
||||
|
||||
if 'proto' in entry and 'kind' in entry:
|
||||
if entry['kind'] == 'network':
|
||||
if entry['proto'] == 'udp46':
|
||||
entry['transport_protocol'] = entry['proto'][:-2]
|
||||
elif entry['proto'].startswith('icm'):
|
||||
entry['transport_protocol'] = 'icmp'
|
||||
else:
|
||||
entry['transport_protocol'] = entry['proto'][:-1]
|
||||
|
||||
if '6' in entry['proto']:
|
||||
entry['network_protocol'] = 'ipv6'
|
||||
else:
|
||||
entry['network_protocol'] = 'ipv4'
|
||||
|
||||
# add route_flags_pretty field
|
||||
if 'route_flags' in entry:
|
||||
flag_map = {
|
||||
'1': 'PROTO1',
|
||||
'2': 'PROTO2',
|
||||
'3': 'PROTO3',
|
||||
'B': 'BLACKHOLE',
|
||||
'b': 'BROADCAST',
|
||||
'C': 'CLONING',
|
||||
'c': 'PRCLONING',
|
||||
'D': 'DYNAMIC',
|
||||
'G': 'GATEWAY',
|
||||
'H': 'HOST',
|
||||
'I': 'IFSCOPE',
|
||||
'i': 'IFREF',
|
||||
'L': 'LLINFO',
|
||||
'M': 'MODIFIED',
|
||||
'm': 'MULTICAST',
|
||||
'R': 'REJECT',
|
||||
'r': 'ROUTER',
|
||||
'S': 'STATIC',
|
||||
'U': 'UP',
|
||||
'W': 'WASCLONED',
|
||||
'X': 'XRESOLVE',
|
||||
'Y': 'PROXY',
|
||||
}
|
||||
|
||||
pretty_flags = []
|
||||
|
||||
for flag in entry['route_flags']:
|
||||
if flag in flag_map:
|
||||
pretty_flags.append(flag_map[flag])
|
||||
|
||||
entry['route_flags_pretty'] = pretty_flags
|
||||
|
||||
# strip whitespace from beginning and end of all string values
|
||||
for item in entry:
|
||||
if isinstance(entry[item], str):
|
||||
entry[item] = entry[item].strip()
|
||||
|
||||
return raw_data
|
||||
|
||||
|
||||
def parse(cleandata):
|
||||
"""
|
||||
Main text parsing function for OSX netstat
|
||||
|
||||
Parameters:
|
||||
|
||||
cleandata: (string) text data to parse
|
||||
|
||||
Returns:
|
||||
|
||||
List of dictionaries. Raw structured data.
|
||||
"""
|
||||
raw_output = []
|
||||
network = False
|
||||
multipath = False
|
||||
socket = False
|
||||
reg_kernel_control = False
|
||||
active_kernel_event = False
|
||||
active_kernel_control = False
|
||||
routing_table = False
|
||||
interface_table = False
|
||||
|
||||
for line in cleandata:
|
||||
|
||||
if line.startswith('Active Internet'):
|
||||
network = True
|
||||
multipath = False
|
||||
socket = False
|
||||
reg_kernel_control = False
|
||||
active_kernel_event = False
|
||||
active_kernel_control = False
|
||||
routing_table = False
|
||||
interface_table = False
|
||||
continue
|
||||
|
||||
if line.startswith('Active Multipath Internet connections'):
|
||||
network = False
|
||||
multipath = True
|
||||
socket = False
|
||||
reg_kernel_control = False
|
||||
active_kernel_event = False
|
||||
active_kernel_control = False
|
||||
routing_table = False
|
||||
interface_table = False
|
||||
continue
|
||||
|
||||
if line.startswith('Active LOCAL (UNIX) domain sockets') or line.startswith('Active UNIX domain sockets'):
|
||||
network = False
|
||||
multipath = False
|
||||
socket = True
|
||||
reg_kernel_control = False
|
||||
active_kernel_event = False
|
||||
active_kernel_control = False
|
||||
routing_table = False
|
||||
interface_table = False
|
||||
continue
|
||||
|
||||
if line.startswith('Registered kernel control modules'):
|
||||
network = False
|
||||
multipath = False
|
||||
socket = False
|
||||
reg_kernel_control = True
|
||||
active_kernel_event = False
|
||||
active_kernel_control = False
|
||||
routing_table = False
|
||||
interface_table = False
|
||||
continue
|
||||
|
||||
if line.startswith('Active kernel event sockets'):
|
||||
network = False
|
||||
multipath = False
|
||||
socket = False
|
||||
reg_kernel_control = False
|
||||
active_kernel_event = True
|
||||
active_kernel_control = False
|
||||
routing_table = False
|
||||
interface_table = False
|
||||
continue
|
||||
|
||||
if line.startswith('Active kernel control sockets'):
|
||||
network = False
|
||||
multipath = False
|
||||
socket = False
|
||||
reg_kernel_control = False
|
||||
active_kernel_event = False
|
||||
active_kernel_control = True
|
||||
routing_table = False
|
||||
interface_table = False
|
||||
continue
|
||||
|
||||
if line.startswith('Routing tables'):
|
||||
network = False
|
||||
multipath = False
|
||||
socket = False
|
||||
reg_kernel_control = False
|
||||
active_kernel_event = False
|
||||
active_kernel_control = False
|
||||
routing_table = True
|
||||
interface_table = False
|
||||
continue
|
||||
|
||||
if line.startswith('Name '):
|
||||
network = False
|
||||
multipath = False
|
||||
socket = False
|
||||
reg_kernel_control = False
|
||||
active_kernel_event = False
|
||||
active_kernel_control = False
|
||||
routing_table = False
|
||||
interface_table = True
|
||||
# don't continue since there is no real header row for this table
|
||||
|
||||
# get headers
|
||||
if network and (line.startswith('Socket ') or line.startswith('Proto ') or line.startswith('Tcpcb ')):
|
||||
header_text = normalize_headers(line)
|
||||
headers = header_text.split()
|
||||
continue
|
||||
|
||||
if socket and line.startswith('Address '):
|
||||
header_text = normalize_headers(line)
|
||||
headers = header_text.split()
|
||||
continue
|
||||
|
||||
if reg_kernel_control and (line.startswith('id ') or line.startswith('kctlref ')):
|
||||
header_text = normalize_headers(line)
|
||||
headers = header_text.split()
|
||||
continue
|
||||
|
||||
if active_kernel_event and (line.startswith('Proto ') or line.startswith(' pcb ')):
|
||||
header_text = normalize_headers(line)
|
||||
headers = header_text.split()
|
||||
continue
|
||||
|
||||
if active_kernel_control and (line.startswith('Proto ') or line.startswith(' pcb ')):
|
||||
header_text = normalize_headers(line)
|
||||
headers = header_text.split()
|
||||
continue
|
||||
|
||||
if routing_table and line.startswith('Destination '):
|
||||
header_text = normalize_route_headers(line)
|
||||
headers = header_text.split()
|
||||
continue
|
||||
|
||||
if interface_table and line.startswith('Name '):
|
||||
header_text = normalize_interface_headers(line)
|
||||
headers = header_text.split()
|
||||
continue
|
||||
|
||||
# get items
|
||||
if network:
|
||||
raw_output.append(parse_item(headers, line, 'network'))
|
||||
continue
|
||||
|
||||
if multipath:
|
||||
# not implemented
|
||||
continue
|
||||
|
||||
if socket:
|
||||
raw_output.append(parse_item(headers, line, 'socket'))
|
||||
continue
|
||||
|
||||
if reg_kernel_control:
|
||||
raw_output.append(parse_item(headers, line, 'Registered kernel control module'))
|
||||
continue
|
||||
|
||||
if active_kernel_event:
|
||||
raw_output.append(parse_item(headers, line, 'Active kernel event socket'))
|
||||
continue
|
||||
|
||||
if active_kernel_control:
|
||||
raw_output.append(parse_item(headers, line, 'Active kernel control socket'))
|
||||
continue
|
||||
|
||||
if routing_table and not (line.startswith('Internet:') or line.startswith('Internet6:')):
|
||||
raw_output.append(parse_item(headers, line, 'route'))
|
||||
continue
|
||||
|
||||
if interface_table:
|
||||
raw_output.append(parse_item(headers, line, 'interface'))
|
||||
continue
|
||||
|
||||
return parse_post(raw_output)
|
||||
280
jc/parsers/netstat_linux.py
Normal file
280
jc/parsers/netstat_linux.py
Normal file
@@ -0,0 +1,280 @@
|
||||
"""jc - JSON CLI output utility Linux netstat Parser"""
|
||||
import string
|
||||
|
||||
|
||||
def normalize_headers(header):
|
||||
header = header.lower()
|
||||
header = header.replace('local address', 'local_address')
|
||||
header = header.replace('foreign address', 'foreign_address')
|
||||
header = header.replace('pid/program name', 'program_name')
|
||||
header = header.replace('security context', 'security_context')
|
||||
header = header.replace('i-node', 'inode')
|
||||
header = header.replace('-', '_')
|
||||
|
||||
return header
|
||||
|
||||
|
||||
def normalize_route_headers(header):
|
||||
header = header.lower()
|
||||
header = header.replace('flags', 'route_flags')
|
||||
header = header.replace('ref', 'route_refs')
|
||||
header = header.replace('-', '_')
|
||||
|
||||
return header
|
||||
|
||||
|
||||
def normalize_interface_headers(header):
|
||||
header = header.lower()
|
||||
header = header.replace('-', '_')
|
||||
|
||||
return header
|
||||
|
||||
|
||||
def parse_network(headers, entry):
|
||||
# Count words in header
|
||||
# if len of line is one less than len of header, then insert None in field 5
|
||||
entry = entry.split(maxsplit=len(headers) - 1)
|
||||
|
||||
if len(entry) == len(headers) - 1:
|
||||
entry.insert(5, None)
|
||||
|
||||
output_line = dict(zip(headers, entry))
|
||||
output_line['kind'] = 'network'
|
||||
|
||||
return output_line
|
||||
|
||||
|
||||
def parse_socket(header_text, headers, entry):
|
||||
# get the column # of first letter of "state"
|
||||
state_col = header_text.find('state')
|
||||
# get the program name column area
|
||||
pn_start = header_text.find('program_name')
|
||||
pn_end = header_text.find('path') - 1
|
||||
|
||||
# remove [ and ] from each line
|
||||
entry = entry.replace('[ ]', '---')
|
||||
entry = entry.replace('[', ' ').replace(']', ' ')
|
||||
|
||||
# find program_name column area and substitute spaces with \u2063 there
|
||||
old_pn = entry[pn_start:pn_end]
|
||||
new_pn = old_pn.replace(' ', '\u2063')
|
||||
entry = entry.replace(old_pn, new_pn)
|
||||
|
||||
entry_list = entry.split(maxsplit=len(headers) - 1)
|
||||
# check column # to see if state column is populated
|
||||
if entry[state_col] in string.whitespace:
|
||||
entry_list.insert(4, None)
|
||||
|
||||
output_line = dict(zip(headers, entry_list))
|
||||
output_line['kind'] = 'socket'
|
||||
|
||||
# fix program_name field to turn \u2063 back to spaces
|
||||
if 'program_name' in output_line:
|
||||
if output_line['program_name']:
|
||||
old_d_pn = output_line['program_name']
|
||||
new_d_pn = old_d_pn.replace('\u2063', ' ')
|
||||
output_line['program_name'] = new_d_pn
|
||||
|
||||
return output_line
|
||||
|
||||
|
||||
def parse_route(headers, entry):
|
||||
entry = entry.split(maxsplit=len(headers) - 1)
|
||||
output_line = dict(zip(headers, entry))
|
||||
output_line['kind'] = 'route'
|
||||
|
||||
return output_line
|
||||
|
||||
|
||||
def parse_interface(headers, entry):
|
||||
entry = entry.split(maxsplit=len(headers) - 1)
|
||||
output_line = dict(zip(headers, entry))
|
||||
output_line['kind'] = 'interface'
|
||||
|
||||
return output_line
|
||||
|
||||
|
||||
def parse_post(raw_data):
|
||||
# clean up trailing whitespace on each item in each entry
|
||||
# flags --- = null
|
||||
# program_name - = null
|
||||
# split pid and program name and ip addresses and ports
|
||||
# create network and transport protocol fields
|
||||
|
||||
for entry in raw_data:
|
||||
for item in entry:
|
||||
try:
|
||||
entry[item] = entry[item].rstrip()
|
||||
except (AttributeError):
|
||||
# skips trying to rstrip Null entries
|
||||
pass
|
||||
|
||||
if 'flags' in entry:
|
||||
if entry['flags'] == '---':
|
||||
entry['flags'] = None
|
||||
|
||||
if 'program_name' in entry:
|
||||
entry['program_name'] = entry['program_name'].strip()
|
||||
if entry['program_name'] == '-':
|
||||
entry['program_name'] = None
|
||||
|
||||
if entry['program_name']:
|
||||
pid = entry['program_name'].split('/', maxsplit=1)[0]
|
||||
name = entry['program_name'].split('/', maxsplit=1)[1]
|
||||
entry['pid'] = pid
|
||||
entry['program_name'] = name
|
||||
|
||||
if 'local_address' in entry:
|
||||
if entry['local_address']:
|
||||
ladd = entry['local_address'].rsplit(':', maxsplit=1)[0]
|
||||
lport = entry['local_address'].rsplit(':', maxsplit=1)[1]
|
||||
entry['local_address'] = ladd
|
||||
entry['local_port'] = lport
|
||||
|
||||
if 'foreign_address' in entry:
|
||||
if entry['foreign_address']:
|
||||
fadd = entry['foreign_address'].rsplit(':', maxsplit=1)[0]
|
||||
fport = entry['foreign_address'].rsplit(':', maxsplit=1)[1]
|
||||
entry['foreign_address'] = fadd
|
||||
entry['foreign_port'] = fport
|
||||
|
||||
if 'proto' in entry and 'kind' in entry:
|
||||
if entry['kind'] == 'network':
|
||||
if 'tcp' in entry['proto']:
|
||||
entry['transport_protocol'] = 'tcp'
|
||||
elif 'udp' in entry['proto']:
|
||||
entry['transport_protocol'] = 'udp'
|
||||
else:
|
||||
entry['transport_protocol'] = None
|
||||
|
||||
if '6' in entry['proto']:
|
||||
entry['network_protocol'] = 'ipv6'
|
||||
else:
|
||||
entry['network_protocol'] = 'ipv4'
|
||||
|
||||
# add route_flags_pretty
|
||||
# Flag mapping from https://www.man7.org/linux/man-pages/man8/route.8.html
|
||||
if 'route_flags' in entry:
|
||||
flag_map = {
|
||||
'U': 'UP',
|
||||
'H': 'HOST',
|
||||
'G': 'GATEWAY',
|
||||
'R': 'REINSTATE',
|
||||
'D': 'DYNAMIC',
|
||||
'M': 'MODIFIED',
|
||||
'A': 'ADDRCONF',
|
||||
'C': 'CACHE',
|
||||
'!': 'REJECT'
|
||||
}
|
||||
|
||||
pretty_flags = []
|
||||
|
||||
for flag in entry['route_flags']:
|
||||
if flag in flag_map:
|
||||
pretty_flags.append(flag_map[flag])
|
||||
|
||||
entry['route_flags_pretty'] = pretty_flags
|
||||
|
||||
return raw_data
|
||||
|
||||
|
||||
def parse(cleandata):
|
||||
"""
|
||||
Main text parsing function for OSX netstat
|
||||
|
||||
Parameters:
|
||||
|
||||
cleandata: (string) text data to parse
|
||||
|
||||
Returns:
|
||||
|
||||
List of dictionaries. Raw structured data.
|
||||
"""
|
||||
raw_output = []
|
||||
network = False
|
||||
socket = False
|
||||
bluetooth = False
|
||||
routing_table = False
|
||||
interface_table = False
|
||||
headers = None
|
||||
|
||||
for line in cleandata:
|
||||
|
||||
if line.startswith('Active Internet'):
|
||||
network = True
|
||||
socket = False
|
||||
bluetooth = False
|
||||
routing_table = False
|
||||
interface_table = False
|
||||
continue
|
||||
|
||||
if line.startswith('Active UNIX'):
|
||||
network = False
|
||||
socket = True
|
||||
bluetooth = False
|
||||
routing_table = False
|
||||
interface_table = False
|
||||
continue
|
||||
|
||||
if line.startswith('Active Bluetooth'):
|
||||
network = False
|
||||
socket = False
|
||||
bluetooth = True
|
||||
routing_table = False
|
||||
interface_table = False
|
||||
continue
|
||||
|
||||
if line.startswith('Kernel IP routing table'):
|
||||
network = False
|
||||
socket = False
|
||||
bluetooth = False
|
||||
routing_table = True
|
||||
interface_table = False
|
||||
continue
|
||||
|
||||
if line.startswith('Kernel Interface table'):
|
||||
network = False
|
||||
socket = False
|
||||
bluetooth = False
|
||||
routing_table = False
|
||||
interface_table = True
|
||||
continue
|
||||
|
||||
# get headers
|
||||
if line.startswith('Proto'):
|
||||
header_text = normalize_headers(line)
|
||||
headers = header_text.split()
|
||||
continue
|
||||
|
||||
if line.startswith('Destination '):
|
||||
header_text = normalize_route_headers(line)
|
||||
headers = header_text.split()
|
||||
continue
|
||||
|
||||
if line.startswith('Iface '):
|
||||
header_text = normalize_interface_headers(line)
|
||||
headers = header_text.split()
|
||||
continue
|
||||
|
||||
# parse items
|
||||
if network:
|
||||
raw_output.append(parse_network(headers, line))
|
||||
continue
|
||||
|
||||
if socket:
|
||||
raw_output.append(parse_socket(header_text, headers, line))
|
||||
continue
|
||||
|
||||
if bluetooth:
|
||||
# not implemented
|
||||
continue
|
||||
|
||||
if routing_table:
|
||||
raw_output.append(parse_route(headers, line))
|
||||
continue
|
||||
|
||||
if interface_table:
|
||||
raw_output.append(parse_interface(headers, line))
|
||||
continue
|
||||
|
||||
return parse_post(raw_output)
|
||||
297
jc/parsers/ntpq.py
Normal file
297
jc/parsers/ntpq.py
Normal file
@@ -0,0 +1,297 @@
|
||||
"""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"
|
||||
}
|
||||
]
|
||||
"""
|
||||
import jc.utils
|
||||
import jc.parsers.universal
|
||||
|
||||
|
||||
class info():
|
||||
version = '1.1'
|
||||
description = 'ntpq -p command parser'
|
||||
author = 'Kelly Brazil'
|
||||
author_email = 'kellyjonbrazil@gmail.com'
|
||||
|
||||
# compatible options: linux, darwin, cygwin, win32, aix, freebsd
|
||||
compatible = ['linux', 'freebsd']
|
||||
magic_commands = ['ntpq']
|
||||
|
||||
|
||||
__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:
|
||||
|
||||
[
|
||||
{
|
||||
"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
|
||||
},
|
||||
]
|
||||
|
||||
"""
|
||||
for entry in proc_data:
|
||||
|
||||
if entry['s'] == '~':
|
||||
entry['s'] = None
|
||||
|
||||
entry['state'] = entry.pop('s')
|
||||
|
||||
int_list = ['st', 'when', 'poll', 'reach']
|
||||
for key in int_list:
|
||||
if key in entry:
|
||||
try:
|
||||
entry[key] = int(entry[key])
|
||||
except (ValueError):
|
||||
entry[key] = None
|
||||
|
||||
float_list = ['delay', 'offset', 'jitter']
|
||||
for key in float_list:
|
||||
if key in entry:
|
||||
try:
|
||||
entry[key] = float(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 = []
|
||||
|
||||
cleandata = data.splitlines()
|
||||
cleandata[0] = 's ' + cleandata[0]
|
||||
cleandata[0] = cleandata[0].lower()
|
||||
|
||||
# delete header delimiter
|
||||
del cleandata[1]
|
||||
|
||||
# separate first character with a space for easier parsing
|
||||
for i, line in list(enumerate(cleandata[1:])):
|
||||
if line[0] == ' ':
|
||||
# fixup for no-state
|
||||
cleandata[i + 1] = '~ ' + line[1:]
|
||||
else:
|
||||
# fixup - realign columns since we added the 's' column
|
||||
cleandata[i + 1] = line[:1] + ' ' + line[1:]
|
||||
|
||||
# fixup for occaisional ip/hostname fields with a space
|
||||
cleandata[i + 1] = cleandata[i + 1].replace(' (', '_(')
|
||||
|
||||
raw_output = jc.parsers.universal.simple_table_parse(cleandata)
|
||||
|
||||
if raw:
|
||||
return raw_output
|
||||
else:
|
||||
return process(raw_output)
|
||||
175
jc/parsers/passwd.py
Normal file
175
jc/parsers/passwd.py
Normal file
@@ -0,0 +1,175 @@
|
||||
"""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"
|
||||
},
|
||||
...
|
||||
]
|
||||
"""
|
||||
import jc.utils
|
||||
|
||||
|
||||
class info():
|
||||
version = '1.0'
|
||||
description = '/etc/passwd 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:
|
||||
|
||||
[
|
||||
{
|
||||
"username": string,
|
||||
"password": string,
|
||||
"uid": integer,
|
||||
"gid": integer,
|
||||
"comment": string,
|
||||
"home": string,
|
||||
"shell": string
|
||||
}
|
||||
]
|
||||
"""
|
||||
for entry in proc_data:
|
||||
int_list = ['uid', '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
|
||||
|
||||
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 cleandata:
|
||||
for entry in cleandata:
|
||||
if entry.startswith('#'):
|
||||
continue
|
||||
|
||||
output_line = {}
|
||||
fields = entry.split(':')
|
||||
|
||||
output_line['username'] = fields[0]
|
||||
output_line['password'] = fields[1]
|
||||
output_line['uid'] = fields[2]
|
||||
output_line['gid'] = fields[3]
|
||||
output_line['comment'] = fields[4]
|
||||
output_line['home'] = fields[5]
|
||||
output_line['shell'] = fields[6]
|
||||
|
||||
raw_output.append(output_line)
|
||||
|
||||
if raw:
|
||||
return raw_output
|
||||
else:
|
||||
return process(raw_output)
|
||||
@@ -32,13 +32,14 @@ import jc.parsers.universal
|
||||
|
||||
|
||||
class info():
|
||||
version = '1.0'
|
||||
description = 'pip list parser'
|
||||
version = '1.1'
|
||||
description = 'pip list command parser'
|
||||
author = 'Kelly Brazil'
|
||||
author_email = 'kellyjonbrazil@gmail.com'
|
||||
|
||||
# compatible options: linux, darwin, cygwin, win32, aix, freebsd
|
||||
compatible = ['linux', 'darwin', 'cygwin', 'win32', 'aix', 'freebsd']
|
||||
magic_commands = ['pip list', 'pip3 list']
|
||||
|
||||
|
||||
__version__ = info.version
|
||||
@@ -93,7 +94,7 @@ def parse(data, raw=False, quiet=False):
|
||||
cleandata = list(filter(None, linedata))
|
||||
|
||||
# detect legacy output type
|
||||
if cleandata[0].find(' (') != -1:
|
||||
if ' (' in cleandata[0]:
|
||||
for row in cleandata:
|
||||
raw_output.append({'package': row.split(' (')[0],
|
||||
'version': row.split(' (')[1].rstrip(')')})
|
||||
@@ -102,7 +103,7 @@ def parse(data, raw=False, quiet=False):
|
||||
else:
|
||||
# clear separator line
|
||||
for i, line in reversed(list(enumerate(cleandata))):
|
||||
if line.find('---') != -1:
|
||||
if '---' in line:
|
||||
cleandata.pop(i)
|
||||
|
||||
cleandata[0] = cleandata[0].lower()
|
||||
|
||||
@@ -43,12 +43,13 @@ import jc.utils
|
||||
|
||||
class info():
|
||||
version = '1.0'
|
||||
description = 'pip show parser'
|
||||
description = 'pip show command parser'
|
||||
author = 'Kelly Brazil'
|
||||
author_email = 'kellyjonbrazil@gmail.com'
|
||||
|
||||
# compatible options: linux, darwin, cygwin, win32, aix, freebsd
|
||||
compatible = ['linux', 'darwin', 'cygwin', 'win32', 'aix', 'freebsd']
|
||||
magic_commands = ['pip show', 'pip3 show']
|
||||
|
||||
|
||||
__version__ = info.version
|
||||
|
||||
@@ -178,12 +178,13 @@ import jc.parsers.universal
|
||||
|
||||
class info():
|
||||
version = '1.1'
|
||||
description = 'ps parser'
|
||||
description = 'ps command parser'
|
||||
author = 'Kelly Brazil'
|
||||
author_email = 'kellyjonbrazil@gmail.com'
|
||||
|
||||
# compatible options: linux, darwin, cygwin, win32, aix, freebsd
|
||||
compatible = ['linux', 'darwin', 'cygwin', 'aix', 'freebsd']
|
||||
magic_commands = ['ps']
|
||||
|
||||
|
||||
__version__ = info.version
|
||||
|
||||
@@ -101,13 +101,14 @@ import jc.parsers.universal
|
||||
|
||||
|
||||
class info():
|
||||
version = '1.0'
|
||||
description = 'route parser'
|
||||
version = '1.1'
|
||||
description = 'route command parser'
|
||||
author = 'Kelly Brazil'
|
||||
author_email = 'kellyjonbrazil@gmail.com'
|
||||
|
||||
# compatible options: linux, darwin, cygwin, win32, aix, freebsd
|
||||
compatible = ['linux']
|
||||
magic_commands = ['route']
|
||||
|
||||
|
||||
__version__ = info.version
|
||||
@@ -127,17 +128,20 @@ def process(proc_data):
|
||||
|
||||
[
|
||||
{
|
||||
"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
|
||||
}
|
||||
]
|
||||
"""
|
||||
@@ -151,6 +155,29 @@ def process(proc_data):
|
||||
except (ValueError):
|
||||
entry[key] = None
|
||||
|
||||
# add flags_pretty
|
||||
# Flag mapping from https://www.man7.org/linux/man-pages/man8/route.8.html
|
||||
if 'flags' in entry:
|
||||
flag_map = {
|
||||
'U': 'UP',
|
||||
'H': 'HOST',
|
||||
'G': 'GATEWAY',
|
||||
'R': 'REINSTATE',
|
||||
'D': 'DYNAMIC',
|
||||
'M': 'MODIFIED',
|
||||
'A': 'ADDRCONF',
|
||||
'C': 'CACHE',
|
||||
'!': 'REJECT'
|
||||
}
|
||||
|
||||
pretty_flags = []
|
||||
|
||||
for flag in entry['flags']:
|
||||
if flag in flag_map:
|
||||
pretty_flags.append(flag_map[flag])
|
||||
|
||||
entry['flags_pretty'] = pretty_flags
|
||||
|
||||
return proc_data
|
||||
|
||||
|
||||
|
||||
183
jc/parsers/shadow.py
Normal file
183
jc/parsers/shadow.py
Normal file
@@ -0,0 +1,183 @@
|
||||
"""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": ""
|
||||
},
|
||||
...
|
||||
]
|
||||
"""
|
||||
import jc.utils
|
||||
|
||||
|
||||
class info():
|
||||
version = '1.0'
|
||||
description = '/etc/shadow 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:
|
||||
|
||||
[
|
||||
{
|
||||
"username": string,
|
||||
"password": string,
|
||||
"last_changed": integer,
|
||||
"minimum": integer,
|
||||
"maximum": integer,
|
||||
"warn": integer,
|
||||
"inactive": integer,
|
||||
"expire": integer
|
||||
}
|
||||
]
|
||||
"""
|
||||
for entry in proc_data:
|
||||
int_list = ['last_changed', 'minimum', 'maximum', 'warn', 'inactive', 'expire']
|
||||
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 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 cleandata:
|
||||
for entry in cleandata:
|
||||
if entry.startswith('#'):
|
||||
continue
|
||||
|
||||
output_line = {}
|
||||
fields = entry.split(':')
|
||||
|
||||
output_line['username'] = fields[0]
|
||||
output_line['password'] = fields[1]
|
||||
output_line['last_changed'] = fields[2]
|
||||
output_line['minimum'] = fields[3]
|
||||
output_line['maximum'] = fields[4]
|
||||
output_line['warn'] = fields[5]
|
||||
output_line['inactive'] = fields[6]
|
||||
output_line['expire'] = fields[7]
|
||||
|
||||
raw_output.append(output_line)
|
||||
|
||||
if raw:
|
||||
return raw_output
|
||||
else:
|
||||
return process(raw_output)
|
||||
@@ -252,12 +252,13 @@ import jc.utils
|
||||
|
||||
class info():
|
||||
version = '1.0'
|
||||
description = 'ss parser'
|
||||
description = 'ss command parser'
|
||||
author = 'Kelly Brazil'
|
||||
author_email = 'kellyjonbrazil@gmail.com'
|
||||
|
||||
# compatible options: linux, darwin, cygwin, win32, aix, freebsd
|
||||
compatible = ['linux']
|
||||
magic_commands = ['ss']
|
||||
|
||||
|
||||
__version__ = info.version
|
||||
|
||||
@@ -6,7 +6,7 @@ Usage:
|
||||
|
||||
Compatibility:
|
||||
|
||||
'linux'
|
||||
'linux', 'darwin', 'freebsd'
|
||||
|
||||
Examples:
|
||||
|
||||
@@ -100,17 +100,19 @@ Examples:
|
||||
..
|
||||
]
|
||||
"""
|
||||
import shlex
|
||||
import jc.utils
|
||||
|
||||
|
||||
class info():
|
||||
version = '1.0'
|
||||
description = 'stat parser'
|
||||
version = '1.4'
|
||||
description = 'stat command parser'
|
||||
author = 'Kelly Brazil'
|
||||
author_email = 'kellyjonbrazil@gmail.com'
|
||||
|
||||
# compatible options: linux, darwin, cygwin, win32, aix, freebsd
|
||||
compatible = ['linux']
|
||||
compatible = ['linux', 'darwin', 'freebsd']
|
||||
magic_commands = ['stat']
|
||||
|
||||
|
||||
__version__ = info.version
|
||||
@@ -148,12 +150,16 @@ def process(proc_data):
|
||||
"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
|
||||
}
|
||||
]
|
||||
"""
|
||||
for entry in proc_data:
|
||||
int_list = ['size', 'blocks', 'io_blocks', 'inode', 'links', 'uid', 'gid']
|
||||
int_list = ['size', 'blocks', 'io_blocks', 'inode', 'links', 'uid', 'gid', 'unix_device', 'rdev', 'block_size']
|
||||
for key in int_list:
|
||||
if key in entry:
|
||||
try:
|
||||
@@ -197,81 +203,109 @@ def parse(data, raw=False, quiet=False):
|
||||
cleandata = list(filter(None, cleandata))
|
||||
|
||||
if cleandata:
|
||||
# stats output contains 8 lines
|
||||
for line in cleandata:
|
||||
|
||||
# line #1
|
||||
if line.find('File:') == 2:
|
||||
output_line = {}
|
||||
line_list = line.split(maxsplit=1)
|
||||
output_line['file'] = line_list[1]
|
||||
# linux output
|
||||
if cleandata[0].startswith(' File: '):
|
||||
# stats output contains 8 lines
|
||||
for line in cleandata:
|
||||
|
||||
# populate link_to field if -> found
|
||||
if output_line['file'].find(' -> ') != -1:
|
||||
filename = output_line['file'].split(' -> ')[0].strip('\u2018').rstrip('\u2019')
|
||||
link = output_line['file'].split(' -> ')[1].strip('\u2018').rstrip('\u2019')
|
||||
output_line['file'] = filename
|
||||
output_line['link_to'] = link
|
||||
else:
|
||||
filename = output_line['file'].split(' -> ')[0].strip('\u2018').rstrip('\u2019')
|
||||
output_line['file'] = filename
|
||||
# line #1
|
||||
if line.find('File:') == 2:
|
||||
output_line = {}
|
||||
line_list = line.split(maxsplit=1)
|
||||
output_line['file'] = line_list[1]
|
||||
|
||||
continue
|
||||
# populate link_to field if -> found
|
||||
if ' -> ' in output_line['file']:
|
||||
filename = output_line['file'].split(' -> ')[0].strip('\u2018').rstrip('\u2019')
|
||||
link = output_line['file'].split(' -> ')[1].strip('\u2018').rstrip('\u2019')
|
||||
output_line['file'] = filename
|
||||
output_line['link_to'] = link
|
||||
else:
|
||||
filename = output_line['file'].split(' -> ')[0].strip('\u2018').rstrip('\u2019')
|
||||
output_line['file'] = filename
|
||||
|
||||
# line #2
|
||||
if line.find('Size:') == 2:
|
||||
line_list = line.split(maxsplit=7)
|
||||
output_line['size'] = line_list[1]
|
||||
output_line['blocks'] = line_list[3]
|
||||
output_line['io_blocks'] = line_list[6]
|
||||
output_line['type'] = line_list[7]
|
||||
continue
|
||||
continue
|
||||
|
||||
# line #3
|
||||
if line.find('Device:') == 0:
|
||||
line_list = line.split()
|
||||
output_line['device'] = line_list[1]
|
||||
output_line['inode'] = line_list[3]
|
||||
output_line['links'] = line_list[5]
|
||||
continue
|
||||
# line #2
|
||||
if line.find('Size:') == 2:
|
||||
line_list = line.split(maxsplit=7)
|
||||
output_line['size'] = line_list[1]
|
||||
output_line['blocks'] = line_list[3]
|
||||
output_line['io_blocks'] = line_list[6]
|
||||
output_line['type'] = line_list[7]
|
||||
continue
|
||||
|
||||
# line #4
|
||||
if line.find('Access: (') == 0:
|
||||
line = line.replace('(', ' ').replace(')', ' ').replace('/', ' ')
|
||||
line_list = line.split()
|
||||
output_line['access'] = line_list[1]
|
||||
output_line['flags'] = line_list[2]
|
||||
output_line['uid'] = line_list[4]
|
||||
output_line['user'] = line_list[5]
|
||||
output_line['gid'] = line_list[7]
|
||||
output_line['group'] = line_list[8]
|
||||
continue
|
||||
# line #3
|
||||
if line.startswith('Device:'):
|
||||
line_list = line.split()
|
||||
output_line['device'] = line_list[1]
|
||||
output_line['inode'] = line_list[3]
|
||||
output_line['links'] = line_list[5]
|
||||
continue
|
||||
|
||||
# line #5
|
||||
if line.find('Access: 2') == 0:
|
||||
line_list = line.split(maxsplit=1)
|
||||
output_line['access_time'] = line_list[1]
|
||||
continue
|
||||
# line #4
|
||||
if line.startswith('Access: ('):
|
||||
line = line.replace('(', ' ').replace(')', ' ').replace('/', ' ')
|
||||
line_list = line.split()
|
||||
output_line['access'] = line_list[1]
|
||||
output_line['flags'] = line_list[2]
|
||||
output_line['uid'] = line_list[4]
|
||||
output_line['user'] = line_list[5]
|
||||
output_line['gid'] = line_list[7]
|
||||
output_line['group'] = line_list[8]
|
||||
continue
|
||||
|
||||
# line #6
|
||||
if line.find('Modify:') == 0:
|
||||
line_list = line.split(maxsplit=1)
|
||||
output_line['modify_time'] = line_list[1]
|
||||
continue
|
||||
# line #5
|
||||
if line.startswith('Access: 2'):
|
||||
line_list = line.split(maxsplit=1)
|
||||
output_line['access_time'] = line_list[1]
|
||||
continue
|
||||
|
||||
# line #7
|
||||
if line.find('Change:') == 0:
|
||||
line_list = line.split(maxsplit=1)
|
||||
output_line['change_time'] = line_list[1]
|
||||
continue
|
||||
# line #6
|
||||
if line.startswith('Modify:'):
|
||||
line_list = line.split(maxsplit=1)
|
||||
output_line['modify_time'] = line_list[1]
|
||||
continue
|
||||
|
||||
# line #8
|
||||
if line.find('Birth:') == 1:
|
||||
line_list = line.split(maxsplit=1)
|
||||
output_line['birth_time'] = line_list[1]
|
||||
# line #7
|
||||
if line.startswith('Change:'):
|
||||
line_list = line.split(maxsplit=1)
|
||||
output_line['change_time'] = line_list[1]
|
||||
continue
|
||||
|
||||
# line #8
|
||||
if line.find('Birth:') == 1:
|
||||
line_list = line.split(maxsplit=1)
|
||||
output_line['birth_time'] = line_list[1]
|
||||
|
||||
raw_output.append(output_line)
|
||||
continue
|
||||
|
||||
# FreeBSD/OSX output
|
||||
else:
|
||||
for line in cleandata:
|
||||
value = shlex.split(line)
|
||||
output_line = {
|
||||
'file': value[15],
|
||||
'unix_device': value[0],
|
||||
'inode': value[1],
|
||||
'flags': value[2],
|
||||
'links': value[3],
|
||||
'user': value[4],
|
||||
'group': value[5],
|
||||
'rdev': value[6],
|
||||
'size': value[7],
|
||||
'access_time': value[8],
|
||||
'modify_time': value[9],
|
||||
'change_time': value[10],
|
||||
'birth_time': value[11],
|
||||
'block_size': value[12],
|
||||
'blocks': value[13],
|
||||
'unix_flags': value[14]
|
||||
}
|
||||
|
||||
raw_output.append(output_line)
|
||||
continue
|
||||
|
||||
if raw:
|
||||
return raw_output
|
||||
|
||||
@@ -40,13 +40,14 @@ import jc.utils
|
||||
|
||||
|
||||
class info():
|
||||
version = '1.0'
|
||||
description = 'systemctl parser'
|
||||
version = '1.1'
|
||||
description = 'systemctl command parser'
|
||||
author = 'Kelly Brazil'
|
||||
author_email = 'kellyjonbrazil@gmail.com'
|
||||
|
||||
# compatible options: linux, darwin, cygwin, win32, aix, freebsd
|
||||
compatible = ['linux']
|
||||
magic_commands = ['systemctl']
|
||||
|
||||
|
||||
__version__ = info.version
|
||||
@@ -109,7 +110,7 @@ def parse(data, raw=False, quiet=False):
|
||||
raw_output = []
|
||||
|
||||
for entry in cleandata[1:]:
|
||||
if entry.find('LOAD = ') != -1:
|
||||
if 'LOAD = ' in entry:
|
||||
break
|
||||
|
||||
else:
|
||||
|
||||
@@ -59,13 +59,14 @@ import jc.utils
|
||||
|
||||
|
||||
class info():
|
||||
version = '1.0'
|
||||
description = 'systemctl list-jobs parser'
|
||||
version = '1.1'
|
||||
description = 'systemctl list-jobs command parser'
|
||||
author = 'Kelly Brazil'
|
||||
author_email = 'kellyjonbrazil@gmail.com'
|
||||
|
||||
# compatible options: linux, darwin, cygwin, win32, aix, freebsd
|
||||
compatible = ['linux']
|
||||
magic_commands = ['systemctl list-jobs']
|
||||
|
||||
|
||||
__version__ = info.version
|
||||
@@ -136,7 +137,7 @@ def parse(data, raw=False, quiet=False):
|
||||
raw_output = []
|
||||
|
||||
for entry in cleandata[1:]:
|
||||
if entry.find('No jobs running.') != -1 or entry.find('jobs listed.') != -1:
|
||||
if 'No jobs running.' in entry or 'jobs listed.' in entry:
|
||||
break
|
||||
|
||||
else:
|
||||
|
||||
@@ -34,13 +34,14 @@ import jc.utils
|
||||
|
||||
|
||||
class info():
|
||||
version = '1.0'
|
||||
description = 'systemctl list-sockets parser'
|
||||
version = '1.1'
|
||||
description = 'systemctl list-sockets command parser'
|
||||
author = 'Kelly Brazil'
|
||||
author_email = 'kellyjonbrazil@gmail.com'
|
||||
|
||||
# compatible options: linux, darwin, cygwin, win32, aix, freebsd
|
||||
compatible = ['linux']
|
||||
magic_commands = ['systemctl list-sockets']
|
||||
|
||||
|
||||
__version__ = info.version
|
||||
@@ -101,7 +102,7 @@ def parse(data, raw=False, quiet=False):
|
||||
raw_output = []
|
||||
|
||||
for entry in cleandata[1:]:
|
||||
if entry.find('sockets listed.') != -1:
|
||||
if 'sockets listed.' in entry:
|
||||
break
|
||||
|
||||
else:
|
||||
|
||||
@@ -31,13 +31,14 @@ import jc.utils
|
||||
|
||||
|
||||
class info():
|
||||
version = '1.0'
|
||||
description = 'systemctl list-unit-files parser'
|
||||
version = '1.1'
|
||||
description = 'systemctl list-unit-files command parser'
|
||||
author = 'Kelly Brazil'
|
||||
author_email = 'kellyjonbrazil@gmail.com'
|
||||
|
||||
# compatible options: linux, darwin, cygwin, win32, aix, freebsd
|
||||
compatible = ['linux']
|
||||
magic_commands = ['systemctl list-unit-files']
|
||||
|
||||
|
||||
__version__ = info.version
|
||||
@@ -98,7 +99,7 @@ def parse(data, raw=False, quiet=False):
|
||||
raw_output = []
|
||||
|
||||
for entry in cleandata[1:]:
|
||||
if entry.find('unit files listed.') != -1:
|
||||
if 'unit files listed.' in entry:
|
||||
break
|
||||
|
||||
else:
|
||||
|
||||
122
jc/parsers/timedatectl.py
Normal file
122
jc/parsers/timedatectl.py
Normal file
@@ -0,0 +1,122 @@
|
||||
"""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"
|
||||
}
|
||||
"""
|
||||
import jc.utils
|
||||
|
||||
|
||||
class info():
|
||||
version = '1.0'
|
||||
description = 'timedatectl status 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 = ['timedatectl', 'timedatectl status']
|
||||
|
||||
|
||||
__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:
|
||||
|
||||
{
|
||||
"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
|
||||
}
|
||||
"""
|
||||
# boolean changes
|
||||
bool_list = ['ntp_enabled', 'ntp_synchronized', 'rtc_in_local_tz', 'dst_active',
|
||||
'system_clock_synchronized', 'systemd-timesyncd.service_active']
|
||||
for key in proc_data:
|
||||
if key in bool_list:
|
||||
try:
|
||||
proc_data[key] = True if proc_data[key] == 'yes' else False
|
||||
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 = {}
|
||||
|
||||
for line in filter(None, data.splitlines()):
|
||||
linedata = line.split(':', maxsplit=1)
|
||||
raw_output[linedata[0].strip().lower().replace(' ', '_')] = linedata[1].strip()
|
||||
|
||||
if linedata[0].strip() == 'DST active':
|
||||
break
|
||||
|
||||
if raw:
|
||||
return raw_output
|
||||
else:
|
||||
return process(raw_output)
|
||||
@@ -10,7 +10,7 @@ Limitations:
|
||||
|
||||
Compatibility:
|
||||
|
||||
'linux', 'darwin'
|
||||
'linux', 'darwin', 'freebsd'
|
||||
|
||||
Example:
|
||||
|
||||
@@ -30,13 +30,14 @@ import jc.utils
|
||||
|
||||
|
||||
class info():
|
||||
version = '1.1'
|
||||
description = 'uname -a parser'
|
||||
version = '1.2'
|
||||
description = 'uname -a 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 = ['uname']
|
||||
|
||||
|
||||
__version__ = info.version
|
||||
|
||||
@@ -35,12 +35,13 @@ import jc.utils
|
||||
|
||||
class info():
|
||||
version = '1.0'
|
||||
description = 'uptime parser'
|
||||
description = 'uptime command parser'
|
||||
author = 'Kelly Brazil'
|
||||
author_email = 'kellyjonbrazil@gmail.com'
|
||||
|
||||
# compatible options: linux, darwin, cygwin, win32, aix, freebsd
|
||||
compatible = ['linux', 'darwin', 'cygwin', 'aix', 'freebsd']
|
||||
magic_commands = ['uptime']
|
||||
|
||||
|
||||
__version__ = info.version
|
||||
|
||||
@@ -83,13 +83,14 @@ import jc.utils
|
||||
|
||||
|
||||
class info():
|
||||
version = '1.0'
|
||||
description = 'w parser'
|
||||
version = '1.1'
|
||||
description = 'w command parser'
|
||||
author = 'Kelly Brazil'
|
||||
author_email = 'kellyjonbrazil@gmail.com'
|
||||
|
||||
# compatible options: linux, darwin, cygwin, win32, aix, freebsd
|
||||
compatible = ['linux', 'darwin', 'cygwin', 'aix', 'freebsd']
|
||||
magic_commands = ['w']
|
||||
|
||||
|
||||
__version__ = info.version
|
||||
@@ -173,6 +174,12 @@ def parse(data, raw=False, quiet=False):
|
||||
output_line = dict(zip(headers, temp_line))
|
||||
raw_output.append(output_line)
|
||||
|
||||
# strip whitespace from beginning and end of all string values
|
||||
for row in raw_output:
|
||||
for item in row:
|
||||
if isinstance(row[item], str):
|
||||
row[item] = row[item].strip()
|
||||
|
||||
if raw:
|
||||
return raw_output
|
||||
else:
|
||||
|
||||
284
jc/parsers/who.py
Normal file
284
jc/parsers/who.py
Normal file
@@ -0,0 +1,284 @@
|
||||
"""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"
|
||||
}
|
||||
]
|
||||
"""
|
||||
import re
|
||||
import jc.utils
|
||||
|
||||
|
||||
class info():
|
||||
version = '1.0'
|
||||
description = 'who 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', 'cygwin', 'aix', 'freebsd']
|
||||
magic_commands = ['who']
|
||||
|
||||
|
||||
__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,
|
||||
"event": string,
|
||||
"writeable_tty": string,
|
||||
"tty": string,
|
||||
"time": string,
|
||||
"idle": string,
|
||||
"pid": integer,
|
||||
"from": string,
|
||||
"comment": string
|
||||
}
|
||||
]
|
||||
"""
|
||||
for entry in proc_data:
|
||||
int_list = ['pid']
|
||||
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 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 cleandata:
|
||||
for line in cleandata:
|
||||
output_line = {}
|
||||
linedata = line.split()
|
||||
|
||||
# clear headers, if they exist
|
||||
if ''.join(linedata[0:3]) == 'NAMELINETIME' \
|
||||
or ''.join(linedata[0:3]) == 'USERLINEWHEN':
|
||||
linedata.pop(0)
|
||||
continue
|
||||
|
||||
# mac reboot line
|
||||
if linedata[0] == 'reboot':
|
||||
output_line['event'] = 'reboot'
|
||||
output_line['time'] = ' '.join(linedata[2:5])
|
||||
output_line['pid'] = linedata[6]
|
||||
raw_output.append(output_line)
|
||||
continue
|
||||
|
||||
# linux reboot line
|
||||
if ''.join(linedata[0:2]) == 'systemboot':
|
||||
output_line['event'] = 'reboot'
|
||||
output_line['time'] = ' '.join(linedata[2:4])
|
||||
raw_output.append(output_line)
|
||||
continue
|
||||
|
||||
# linux login line
|
||||
if linedata[0] == 'LOGIN':
|
||||
output_line['event'] = 'login'
|
||||
output_line['tty'] = linedata[1]
|
||||
output_line['time'] = ' '.join(linedata[2:4])
|
||||
output_line['pid'] = linedata[4]
|
||||
if len(linedata) > 5:
|
||||
output_line['comment'] = ' '.join(linedata[5:])
|
||||
raw_output.append(output_line)
|
||||
continue
|
||||
|
||||
# linux run-level
|
||||
if linedata[0] == 'run-level':
|
||||
output_line['event'] = ' '.join(linedata[0:2])
|
||||
output_line['time'] = ' '.join(linedata[2:4])
|
||||
raw_output.append(output_line)
|
||||
continue
|
||||
|
||||
# mac run-level (ignore because not enough useful info)
|
||||
if linedata[1] == 'run-level':
|
||||
continue
|
||||
|
||||
# pts lines with no user information
|
||||
if linedata[0].startswith('pts/'):
|
||||
output_line['tty'] = linedata[0]
|
||||
output_line['time'] = ' '.join(linedata[1:3])
|
||||
output_line['pid'] = linedata[3]
|
||||
output_line['comment'] = ' '.join(linedata[4:])
|
||||
raw_output.append(output_line)
|
||||
continue
|
||||
|
||||
# user logins
|
||||
output_line['user'] = linedata.pop(0)
|
||||
|
||||
if linedata[0] in '+-?':
|
||||
output_line['writeable_tty'] = linedata.pop(0)
|
||||
|
||||
output_line['tty'] = linedata.pop(0)
|
||||
|
||||
# mac
|
||||
if re.match(r'[JFMASOND][aepuco][nbrynlgptvc]', linedata[0]):
|
||||
output_line['time'] = ' '.join([linedata.pop(0),
|
||||
linedata.pop(0),
|
||||
linedata.pop(0)])
|
||||
# linux
|
||||
else:
|
||||
output_line['time'] = ' '.join([linedata.pop(0),
|
||||
linedata.pop(0)])
|
||||
|
||||
# if just one more field, then it's the remote IP
|
||||
if len(linedata) == 1:
|
||||
output_line['from'] = linedata[0].replace('(', '').replace(')', '')
|
||||
raw_output.append(output_line)
|
||||
continue
|
||||
|
||||
# extended info: idle
|
||||
if len(linedata) > 0:
|
||||
output_line['idle'] = linedata.pop(0)
|
||||
|
||||
# extended info: pid
|
||||
if len(linedata) > 0:
|
||||
output_line['pid'] = linedata.pop(0)
|
||||
|
||||
# extended info is from
|
||||
if len(linedata) > 0 and linedata[0].startswith('('):
|
||||
output_line['from'] = linedata[0].replace('(', '').replace(')', '')
|
||||
|
||||
# else, extended info is comment
|
||||
elif len(linedata) > 0:
|
||||
output_line['comment'] = ' '.join(linedata)
|
||||
|
||||
raw_output.append(output_line)
|
||||
|
||||
if raw:
|
||||
return raw_output
|
||||
else:
|
||||
return process(raw_output)
|
||||
@@ -56,7 +56,14 @@ def compatibility(mod_name, compatible):
|
||||
|
||||
no return, just prints output to STDERR
|
||||
"""
|
||||
if sys.platform not in compatible:
|
||||
platform_found = False
|
||||
|
||||
for platform in compatible:
|
||||
if sys.platform.startswith(platform):
|
||||
platform_found = True
|
||||
break
|
||||
|
||||
if not platform_found:
|
||||
mod = mod_name.split('.')[-1]
|
||||
compat_list = ', '.join(compatible)
|
||||
warning_message(f'{mod} parser not compatible with your OS ({sys.platform}).\n Compatible platforms: {compat_list}')
|
||||
|
||||
3
requirements.txt
Normal file
3
requirements.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
ruamel.yaml>=0.15.0
|
||||
xmltodict>=0.12.0
|
||||
Pygments>=2.4.2
|
||||
10
setup.py
10
setup.py
@@ -5,21 +5,21 @@ with open('README.md', 'r') as f:
|
||||
|
||||
setuptools.setup(
|
||||
name='jc',
|
||||
version='1.7.1',
|
||||
version='1.11.2',
|
||||
author='Kelly Brazil',
|
||||
author_email='kellyjonbrazil@gmail.com',
|
||||
description='This tool serializes the output of popular command line tools and filetypes to structured JSON output.',
|
||||
description='Converts the output of popular command-line tools and file-types to JSON.',
|
||||
install_requires=[
|
||||
'ifconfig-parser>=0.0.5',
|
||||
'ruamel.yaml>=0.15.0',
|
||||
'xmltodict>=0.12.0'
|
||||
'xmltodict>=0.12.0',
|
||||
'Pygments>=2.4.2'
|
||||
],
|
||||
license='MIT',
|
||||
long_description=long_description,
|
||||
long_description_content_type='text/markdown',
|
||||
python_requires='>=3.6',
|
||||
url='https://github.com/kellyjonbrazil/jc',
|
||||
packages=setuptools.find_packages(),
|
||||
packages=setuptools.find_packages(exclude=['*.tests', '*.tests.*', 'tests.*', 'tests']),
|
||||
entry_points={
|
||||
'console_scripts': [
|
||||
'jc=jc.cli:main'
|
||||
|
||||
1
tests/fixtures/centos-7.7/blkid-ip-multi.json
vendored
Normal file
1
tests/fixtures/centos-7.7/blkid-ip-multi.json
vendored
Normal file
@@ -0,0 +1 @@
|
||||
[{"uuid": "3klkIj-w1qk-DkJi-0XBJ-y3o7-i2Ac-vHqWBM", "version": "LVM2 001", "type": "LVM2_member", "usage": "raid", "minimum_io_size": 512, "physical_sector_size": 512, "logical_sector_size": 512, "part_entry_scheme": "dos", "part_entry_type": "0x8e", "part_entry_number": 2, "part_entry_offset": 2099200, "part_entry_size": 39843840, "part_entry_disk": "8:0", "device": "/dev/sda2"}, {"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", "device": "/dev/sda1"}]
|
||||
29
tests/fixtures/centos-7.7/blkid-ip-multi.out
vendored
Normal file
29
tests/fixtures/centos-7.7/blkid-ip-multi.out
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
DEVNAME=/dev/sda2
|
||||
UUID=3klkIj-w1qk-DkJi-0XBJ-y3o7-i2Ac-vHqWBM
|
||||
VERSION=LVM2 001
|
||||
TYPE=LVM2_member
|
||||
USAGE=raid
|
||||
MINIMUM_IO_SIZE=512
|
||||
PHYSICAL_SECTOR_SIZE=512
|
||||
LOGICAL_SECTOR_SIZE=512
|
||||
PART_ENTRY_SCHEME=dos
|
||||
PART_ENTRY_TYPE=0x8e
|
||||
PART_ENTRY_NUMBER=2
|
||||
PART_ENTRY_OFFSET=2099200
|
||||
PART_ENTRY_SIZE=39843840
|
||||
PART_ENTRY_DISK=8:0
|
||||
|
||||
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
|
||||
1
tests/fixtures/centos-7.7/blkid-ip-udev-multi.json
vendored
Normal file
1
tests/fixtures/centos-7.7/blkid-ip-udev-multi.json
vendored
Normal file
@@ -0,0 +1 @@
|
||||
[{"id_fs_uuid": "3klkIj-w1qk-DkJi-0XBJ-y3o7-i2Ac-vHqWBM", "id_fs_uuid_enc": "3klkIj-w1qk-DkJi-0XBJ-y3o7-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"}, {"id_fs_uuid": "05d927bb-5875-49e3-ada1-7f46cb31c932", "id_fs_uuid_enc": "05d927bb-5875-49e3-ada1-7f46cb31c932", "id_fs_type": "xfs", "id_fs_usage": "filesystem", "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": "0x83", "id_part_entry_flags": "0x80", "id_part_entry_number": 1, "id_part_entry_offset": 2048, "id_part_entry_size": 2097152, "id_part_entry_disk": "8:0"}]
|
||||
29
tests/fixtures/centos-7.7/blkid-ip-udev-multi.out
vendored
Normal file
29
tests/fixtures/centos-7.7/blkid-ip-udev-multi.out
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
ID_FS_UUID=3klkIj-w1qk-DkJi-0XBJ-y3o7-i2Ac-vHqWBM
|
||||
ID_FS_UUID_ENC=3klkIj-w1qk-DkJi-0XBJ-y3o7-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
|
||||
|
||||
ID_FS_UUID=05d927bb-5875-49e3-ada1-7f46cb31c932
|
||||
ID_FS_UUID_ENC=05d927bb-5875-49e3-ada1-7f46cb31c932
|
||||
ID_FS_TYPE=xfs
|
||||
ID_FS_USAGE=filesystem
|
||||
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=0x83
|
||||
ID_PART_ENTRY_FLAGS=0x80
|
||||
ID_PART_ENTRY_NUMBER=1
|
||||
ID_PART_ENTRY_OFFSET=2048
|
||||
ID_PART_ENTRY_SIZE=2097152
|
||||
ID_PART_ENTRY_DISK=8:0
|
||||
1
tests/fixtures/centos-7.7/blkid-ip-udev.json
vendored
Normal file
1
tests/fixtures/centos-7.7/blkid-ip-udev.json
vendored
Normal file
@@ -0,0 +1 @@
|
||||
[{"id_fs_uuid": "05d927bb-5875-49e3-ada1-7f46cb31c932", "id_fs_uuid_enc": "05d927bb-5875-49e3-ada1-7f46cb31c932", "id_fs_type": "xfs", "id_fs_usage": "filesystem", "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": "0x83", "id_part_entry_flags": "0x80", "id_part_entry_number": 1, "id_part_entry_offset": 2048, "id_part_entry_size": 2097152, "id_part_entry_disk": "8:0"}]
|
||||
14
tests/fixtures/centos-7.7/blkid-ip-udev.out
vendored
Normal file
14
tests/fixtures/centos-7.7/blkid-ip-udev.out
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
ID_FS_UUID=05d927bb-5875-49e3-ada1-7f46cb31c932
|
||||
ID_FS_UUID_ENC=05d927bb-5875-49e3-ada1-7f46cb31c932
|
||||
ID_FS_TYPE=xfs
|
||||
ID_FS_USAGE=filesystem
|
||||
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=0x83
|
||||
ID_PART_ENTRY_FLAGS=0x80
|
||||
ID_PART_ENTRY_NUMBER=1
|
||||
ID_PART_ENTRY_OFFSET=2048
|
||||
ID_PART_ENTRY_SIZE=2097152
|
||||
ID_PART_ENTRY_DISK=8:0
|
||||
1
tests/fixtures/centos-7.7/blkid-sda2.json
vendored
Normal file
1
tests/fixtures/centos-7.7/blkid-sda2.json
vendored
Normal file
@@ -0,0 +1 @@
|
||||
[{"device": "/dev/sda2", "uuid": "3klkIj-w1qk-DkJi-0XBJ-y3o7-i2Ac-vHqWBM", "type": "LVM2_member"}]
|
||||
1
tests/fixtures/centos-7.7/blkid-sda2.out
vendored
Normal file
1
tests/fixtures/centos-7.7/blkid-sda2.out
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/dev/sda2: UUID="3klkIj-w1qk-DkJi-0XBJ-y3o7-i2Ac-vHqWBM" TYPE="LVM2_member"
|
||||
1
tests/fixtures/centos-7.7/blkid.json
vendored
Normal file
1
tests/fixtures/centos-7.7/blkid.json
vendored
Normal file
@@ -0,0 +1 @@
|
||||
[{"device": "/dev/sda1", "uuid": "05d927bb-5875-49e3-ada1-7f46cb31c932", "type": "xfs"}, {"device": "/dev/sda2", "uuid": "3klkIj-w1qk-DkJi-0XBJ-y3o7-i2Ac-vHqWBM", "type": "LVM2_member"}, {"device": "/dev/mapper/centos-root", "uuid": "07d718ef-950c-4e5b-98e0-42a1147b77d9", "type": "xfs"}, {"device": "/dev/mapper/centos-swap", "uuid": "615eb89d-bcbf-46ad-80e3-c483ef5c931f", "type": "swap"}]
|
||||
4
tests/fixtures/centos-7.7/blkid.out
vendored
Normal file
4
tests/fixtures/centos-7.7/blkid.out
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
/dev/sda1: UUID="05d927bb-5875-49e3-ada1-7f46cb31c932" TYPE="xfs"
|
||||
/dev/sda2: UUID="3klkIj-w1qk-DkJi-0XBJ-y3o7-i2Ac-vHqWBM" TYPE="LVM2_member"
|
||||
/dev/mapper/centos-root: UUID="07d718ef-950c-4e5b-98e0-42a1147b77d9" TYPE="xfs"
|
||||
/dev/mapper/centos-swap: UUID="615eb89d-bcbf-46ad-80e3-c483ef5c931f" TYPE="swap"
|
||||
1
tests/fixtures/centos-7.7/dig-axfr.json
vendored
Normal file
1
tests/fixtures/centos-7.7/dig-axfr.json
vendored
Normal file
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user