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

Compare commits

...

72 Commits

Author SHA1 Message Date
Kelly Brazil
2c1935115d Merge pull request #61 from kellyjonbrazil/dev
Dev v1.10.10
2020-05-09 11:47:05 -07:00
Kelly Brazil
d98e43dc78 add netstat item 2020-05-09 11:42:56 -07:00
Kelly Brazil
9348988d64 add netstat test for Fedora32 2020-05-09 11:41:30 -07:00
Kelly Brazil
1285c66467 netstat with bluetooth section 2020-05-09 11:36:23 -07:00
Kelly Brazil
b7191bbc13 handle bluetooth section (ignore for now) 2020-05-09 11:36:03 -07:00
Kelly Brazil
98b97509f7 version bump 2020-05-09 11:25:13 -07:00
Kelly Brazil
2b2b570490 add tests for 'gone - no logout' 2020-05-09 11:22:26 -07:00
Kelly Brazil
cce2d1ff29 add condition for 'gone - no logout' 2020-05-09 11:22:01 -07:00
Kelly Brazil
b79600c572 version bump 2020-05-09 11:01:48 -07:00
Kelly Brazil
140f1a8543 test fixes for issue #60 2020-05-09 11:00:04 -07:00
Kelly Brazil
e34657cfde fix issue #60 that was skipping the first file in some instances using -R without -l 2020-05-08 15:26:11 -07:00
Kelly Brazil
99070fa607 version bump 2020-05-08 10:51:13 -07:00
Kelly Brazil
2b46785b1f add MIT license to vendorized IfconfigParser class 2020-05-08 10:49:30 -07:00
Kelly Brazil
c72562524b fully remove tests from packaging 2020-05-08 09:30:31 -07:00
Kelly Brazil
b7dd6441c7 version bump 2020-05-08 08:20:33 -07:00
Kelly Brazil
31fcc2f755 remove manifest.in - no longer needed due to removing tests 2020-05-08 08:20:21 -07:00
Kelly Brazil
b391aa14bc add license_file to metadata 2020-05-08 08:19:51 -07:00
Kelly Brazil
d3c45debbb remove tests and add license file 2020-05-08 08:19:31 -07:00
Kelly Brazil
5b08469b87 Merge pull request #57 from kellyjonbrazil/dev
Dev v1.10.7
2020-05-01 15:55:50 -07:00
Kelly Brazil
4a77ec63a4 add IfconfigParser class 2020-05-01 14:57:50 -07:00
Kelly Brazil
d13606b6dc modify dependencies for easier packaging into Fedora 2020-05-01 14:37:23 -07:00
Kelly Brazil
05291c93bb vendorize ifconfig-parser module for easier packaging in Fedora 2020-05-01 14:36:54 -07:00
Kelly Brazil
8cf00a208e change text to strings 2020-04-29 15:57:55 -07:00
Kelly Brazil
06d73c8876 formatting 2020-04-23 07:06:44 -07:00
Kelly Brazil
649c646ea2 add brew install option 2020-04-22 16:08:34 -07:00
Kelly Brazil
b7756d9250 version bump 2020-04-20 16:33:26 -07:00
Kelly Brazil
1cd2cd954c remove references to homebrew/shim to allow tests to pass in homebrew packaging ci/cd 2020-04-20 16:31:22 -07:00
Kelly Brazil
72020b8da9 move packages info to jc-packages github page 2020-04-17 10:20:25 -07:00
Kelly Brazil
cf9720b749 update install info 2020-04-16 14:03:31 -07:00
Kelly Brazil
967b9db7f9 spelling 2020-04-15 21:27:22 -07:00
Kelly Brazil
bb3acb1182 formatting 2020-04-15 21:25:06 -07:00
Kelly Brazil
560c7f7e6d formatting 2020-04-15 21:23:55 -07:00
Kelly Brazil
79b2841764 add new binary package install info 2020-04-15 21:22:43 -07:00
Kelly Brazil
a06a89cbd1 version bump 2020-04-14 11:15:24 -07:00
Kelly Brazil
431bd969eb use sys.exit(0) instead of exit() 2020-04-14 11:10:31 -07:00
Kelly Brazil
c87b722aec spelling 2020-04-12 13:23:58 -07:00
Kelly Brazil
3688b8b014 Merge pull request #56 from kellyjonbrazil/dev
Dev v1.10.4
2020-04-12 13:21:38 -07:00
Kelly Brazil
07b8d9e0c0 version bump 2020-04-12 13:18:28 -07:00
Kelly Brazil
7454b53e39 formatting 2020-04-12 13:13:28 -07:00
Kelly Brazil
3d6a76024d update with JC_COLORS info 2020-04-12 13:10:57 -07:00
Kelly Brazil
421b980957 JC_COLORS working 2020-04-12 13:03:09 -07:00
Kelly Brazil
4a22e27d6a add set_env_colors function 2020-04-12 12:43:51 -07:00
Kelly Brazil
99f7842dee fix brek on pipe error 2020-04-09 13:38:33 -07:00
Kelly Brazil
7f869b4b18 change colors to ansi and match jello style 2020-04-09 07:31:21 -07:00
Kelly Brazil
9665f4ee84 add pypi badge 2020-04-07 08:44:15 -07:00
Kelly Brazil
606904d48b Merge pull request #53 from kellyjonbrazil/dev
Dev v1.10.1
2020-04-04 17:28:21 -07:00
Kelly Brazil
3f5279b97c version bump to 1.10.1 2020-04-04 17:25:55 -07:00
Kelly Brazil
f5ec21e6ac use in instead of find() 2020-04-04 17:19:32 -07:00
Kelly Brazil
578a284465 use in instead of find() 2020-04-04 17:18:39 -07:00
Kelly Brazil
422e392d9d use in instead of find() 2020-04-04 17:17:43 -07:00
Kelly Brazil
54dfffd34a use in instead of find() 2020-04-04 17:16:25 -07:00
Kelly Brazil
cffba64d2b use in and startswith() instead of find() 2020-04-04 17:15:03 -07:00
Kelly Brazil
56a0c12a59 use in instead of find() 2020-04-04 17:12:22 -07:00
Kelly Brazil
c174d3de18 use in and startswith() instead of find() 2020-04-04 17:10:46 -07:00
Kelly Brazil
a9c59ef9fc fix logic to not for ' type ' in cleandata[0] 2020-04-04 17:07:38 -07:00
Kelly Brazil
abdb9b2673 use in instead of find() 2020-04-04 17:05:17 -07:00
Kelly Brazil
548aaab626 remove old commented code 2020-04-04 17:04:20 -07:00
Kelly Brazil
20571c87ae us in instead of find() 2020-04-04 17:01:56 -07:00
Kelly Brazil
19e49200de version bump 2020-04-04 16:59:48 -07:00
Kelly Brazil
d32f5c67a9 use startswith() instead of find() 2020-04-04 16:59:03 -07:00
Kelly Brazil
b83b626435 use startswith() instead of find() 2020-04-04 16:57:23 -07:00
Kelly Brazil
ab2c1b25ec use startswith() and in instead of find() 2020-04-04 16:56:11 -07:00
Kelly Brazil
f2d46313a4 use startswith() instead of find() 2020-04-04 16:53:55 -07:00
Kelly Brazil
87e4796a6c use in instead of .find() 2020-04-04 16:52:45 -07:00
Kelly Brazil
0014a5c2f4 us startswith() and in instead of .find() 2020-04-04 16:51:36 -07:00
Kelly Brazil
7af56e0dad use startswith() and in instead of find() 2020-04-04 16:50:05 -07:00
Kelly Brazil
a5ae6e3c01 use startswith() instead of find() 2020-04-04 16:48:16 -07:00
Kelly Brazil
fe1a0d1faf use in instead of .find() 2020-04-04 16:46:09 -07:00
Kelly Brazil
302f05cdda prettify style block 2020-04-03 14:50:20 -07:00
Kelly Brazil
c0044be7b0 rename color grey to gray 2020-04-03 14:48:19 -07:00
Kelly Brazil
0110078807 update badge 2020-04-03 14:47:08 -07:00
Kelly Brazil
42eacb45f8 rename to Tests 2020-04-03 14:45:04 -07:00
47 changed files with 625 additions and 155 deletions

View File

@@ -1,4 +1,4 @@
name: Test code
name: Tests
on:
push:

View File

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

View File

@@ -1,4 +1,5 @@
![Test code](https://github.com/kellyjonbrazil/jc/workflows/Test%20code/badge.svg?branch=master)
![Tests](https://github.com/kellyjonbrazil/jc/workflows/Tests/badge.svg?branch=master)
![Pypi](https://img.shields.io/pypi/v/jc.svg)
# JC
JSON CLI output utility
@@ -68,10 +69,21 @@ Release notes can be found [here](https://blog.kellybrazil.com/category/jc-news/
For more information on the motivations for this project, please see my [blog post](https://blog.kellybrazil.com/2019/11/26/bringing-the-unix-philosophy-to-the-21st-century/).
## Installation
There are several ways to get `jc`. You can install via `pip`, `brew`, DEB or RPM packages, or by downloading the correct binary for your architecture and running it anywhere on your filesystem.
### Pip (macOS, linux, unix, Windows)
```
$ pip3 install --upgrade jc
```
### Brew (macOS)
```
$ brew install jc
```
### Packages and Binaries
Please see https://kellyjonbrazil.github.io/jc-packaging/ for details.
## Usage
`jc` accepts piped input from `STDIN` and outputs a JSON representation of the previous command's output to `STDOUT`.
```
@@ -141,7 +153,23 @@ The JSON output can be compact (default) or pretty formatted with the `-p` optio
- `-m` monochrome JSON output
- `-p` pretty format the JSON output
- `-q` quiet mode. Suppresses warning messages
- `-r` raw output. Provides a more literal JSON output with all values as text and no additional sematic processing
- `-r` raw output. Provides a more literal JSON output with all values as strings and no additional sematic processing
### Setting Custom Colors via Environment Variable
You can specify custom colors via the `JC_COLORS` environment variable. The `JC_COLORS` environment variable takes four comma separated string values in the following format:
```
JC_COLORS=<keyname_color>,<keyword_color>,<number_color>,<string_color>
```
Where colors are: `black`, `red`, `green`, `yellow`, `blue`, `magenta`, `cyan`, `gray`, `brightblack`, `brightred`, `brightgreen`, `brightyellow`, `brightblue`, `brightmagenta`, `brightcyan`, `white`, or `default`
For example, to set to the default colors:
```
JC_COLORS=blue,brightblack,magenta,green
```
or
```
JC_COLORS=default,default,default,default
```
## Contributions
Feel free to add/improve code or parsers! You can use the [`jc/parsers/foo.py`](https://github.com/kellyjonbrazil/jc/blob/master/jc/parsers/foo.py) parser as a template and submit your parser with a pull request.

View File

@@ -1,5 +1,38 @@
jc changelog
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

View File

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

View File

@@ -18,7 +18,7 @@ import jc.utils
class info():
version = '1.10.0'
version = '1.10.10'
description = 'jc cli output JSON conversion tool'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
@@ -80,17 +80,52 @@ parsers = [
]
class JcStyle(Style):
BLUE = '#2c5dcd'
GREY = '#4d4d4d'
PURPLE = '#5918bb'
GREEN = '#00cc00'
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:
styles = {
Name.Tag: 'bold {}'.format(BLUE), # key names
Keyword: GREY, # true, false, null
Number: PURPLE, # int, float
String: GREEN # string
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
}
@@ -210,6 +245,11 @@ def helptext(message):
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])
@@ -282,7 +322,7 @@ def magic():
valid_command, run_command = generate_magic_command(sys.argv)
if valid_command:
os.system(run_command)
exit()
sys.exit(0)
elif run_command is None:
return
else:
@@ -294,6 +334,12 @@ def main():
# break on ctrl-c keyboard interrupt
signal.signal(signal.SIGINT, ctrlc)
# break on pipe error. need try/except for windows compatibility
try:
signal.signal(signal.SIGPIPE, signal.SIG_DFL)
except AttributeError:
pass
# try magic syntax first: e.g. jc -p ls -al
magic()
@@ -312,7 +358,7 @@ def main():
if 'a' in options:
json_out(about_jc(), pretty=pretty, mono=mono, piped_out=piped_output())
exit()
sys.exit(0)
if sys.stdin.isatty():
helptext('missing piped data')

View File

@@ -91,7 +91,7 @@ import jc.parsers.universal
class info():
version = '1.2'
version = '1.3'
description = 'arp command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
@@ -156,7 +156,7 @@ 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
@@ -179,7 +179,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')

View File

@@ -132,7 +132,7 @@ import jc.parsers.universal
class info():
version = '1.1'
version = '1.2'
description = 'crontab command and file parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
@@ -227,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()

View File

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

View File

@@ -73,7 +73,7 @@ import jc.parsers.universal
class info():
version = '1.1'
version = '1.2'
description = 'df command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
@@ -135,7 +135,7 @@ def process(proc_data):
# change any entry for key with '_blocks' in the name to int
for k in entry:
if str(k).find('_blocks') != -1:
if '_blocks' in str(k):
try:
blocks_int = int(entry[k])
entry[k] = blocks_int

View File

@@ -324,7 +324,7 @@ import jc.utils
class info():
version = '1.1'
version = '1.2'
description = 'dig command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
@@ -525,11 +525,12 @@ def parse_answer(answer):
'ttl': answer_ttl,
'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
# ; <<>> 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]
@@ -575,7 +576,7 @@ def parse(data, raw=False, quiet=False):
output_entry = {}
for line in cleandata:
if line.startswith('; <<>> ') and line.lower().find(' axfr ') != -1:
if line.startswith('; <<>> ') and ' axfr ' in line.lower():
question = False
authority = False
answer = False
@@ -583,7 +584,7 @@ def parse(data, raw=False, quiet=False):
axfr_list = []
continue
if line.find(';') == -1 and axfr:
if ';' not in line and axfr:
axfr_list.append(parse_axfr(line))
output_entry.update({'axfr': axfr_list})
continue
@@ -620,7 +621,7 @@ def parse(data, raw=False, quiet=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
@@ -633,7 +634,7 @@ def parse(data, raw=False, quiet=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

View File

@@ -70,7 +70,7 @@ import jc.utils
class info():
version = '1.0'
version = '1.1'
description = 'fstab file parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
@@ -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)

View File

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

View File

@@ -141,16 +141,17 @@ Examples:
}
]
"""
import re
from collections import namedtuple
import jc.utils
from ifconfigparser import IfconfigParser
class info():
version = '1.5'
version = '1.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']
@@ -160,6 +161,222 @@ class info():
__version__ = info.version
class IfconfigParser(object):
# Author: threeheadedknight@protonmail.com
# Date created: 30.06.2018 17:03
# Python Version: 3.7
# MIT License
# Copyright (c) 2018 threeheadedknight@protonmail.com
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
attributes = ['name', 'type', 'mac_addr', 'ipv4_addr', 'ipv4_bcast', 'ipv4_mask', 'ipv6_addr', 'ipv6_mask',
'ipv6_scope', 'state', 'mtu', 'metric', 'rx_packets', 'rx_errors', 'rx_dropped', 'rx_overruns',
'rx_frame', 'tx_packets', 'tx_errors', 'tx_dropped', 'tx_overruns', 'tx_carrier', 'tx_collisions',
'rx_bytes', 'tx_bytes']
def __init__(self, console_output):
"""
:param console_output:
"""
if isinstance(console_output, list):
source_data = " ".join(console_output)
else:
source_data = console_output.replace("\n", " ")
self.interfaces = self.parser(source_data=source_data)
def list_interfaces(self):
"""
:return:
"""
return sorted(self.interfaces.keys())
def count_interfaces(self):
"""
:return:
"""
return len(self.interfaces.keys())
def filter_interfaces(self, **kwargs):
"""
:param kwargs:
:return:
"""
for attr in kwargs.keys():
if attr not in IfconfigParser.attributes:
raise ValueError("Attribute [{}] not supported.".format(attr))
filtered_interfaces = []
for name, details in self.interfaces.items():
if all(getattr(details, attr) == kwargs[attr] for attr in kwargs.keys()):
filtered_interfaces.append(name)
return sorted(filtered_interfaces)
def get_interface(self, name):
"""
:param name:
:return:
"""
if name in self.list_interfaces():
return self.interfaces[name]
else:
raise InterfaceNotFound("Interface [{}] not found.".format(name))
def get_interfaces(self):
"""
:return:
"""
return self.interfaces
def is_available(self, name):
"""
:param name:
:return:
"""
return name in self.interfaces
def parser(self, source_data):
"""
:param source_data:
:return:
"""
# Linux syntax
re_linux_interface = re.compile(
r"(?P<name>[a-zA-Z0-9:._-]+)\s+Link encap:(?P<type>\S+\s?\S+)(\s+HWaddr\s+\b"
r"(?P<mac_addr>[0-9A-Fa-f:?]+))?",
re.I)
re_linux_ipv4 = re.compile(
r"inet addr:(?P<ipv4_addr>(?:[0-9]{1,3}\.){3}[0-9]{1,3})(\s+Bcast:"
r"(?P<ipv4_bcast>(?:[0-9]{1,3}\.){3}[0-9]{1,3}))?\s+Mask:(?P<ipv4_mask>(?:[0-9]{1,3}\.){3}[0-9]{1,3})",
re.I)
re_linux_ipv6 = re.compile(
r"inet6 addr:\s+(?P<ipv6_addr>\S+)/(?P<ipv6_mask>[0-9]+)\s+Scope:(?P<ipv6_scope>Link|Host)",
re.I)
re_linux_state = re.compile(
r"\W+(?P<state>(?:\w+\s)+)(?:\s+)?MTU:(?P<mtu>[0-9]+)\s+Metric:(?P<metric>[0-9]+)", re.I)
re_linux_rx = re.compile(
r"RX packets:(?P<rx_packets>[0-9]+)\s+errors:(?P<rx_errors>[0-9]+)\s+dropped:"
r"(?P<rx_dropped>[0-9]+)\s+overruns:(?P<rx_overruns>[0-9]+)\s+frame:(?P<rx_frame>[0-9]+)",
re.I)
re_linux_tx = re.compile(
r"TX packets:(?P<tx_packets>[0-9]+)\s+errors:(?P<tx_errors>[0-9]+)\s+dropped:"
r"(?P<tx_dropped>[0-9]+)\s+overruns:(?P<tx_overruns>[0-9]+)\s+carrier:(?P<tx_carrier>[0-9]+)",
re.I)
re_linux_bytes = re.compile(r"\W+RX bytes:(?P<rx_bytes>\d+)\s+\(.*\)\s+TX bytes:(?P<tx_bytes>\d+)\s+\(.*\)", re.I)
re_linux_tx_stats = re.compile(r"collisions:(?P<tx_collisions>[0-9]+)\s+txqueuelen:[0-9]+", re.I)
re_linux = [re_linux_interface, re_linux_ipv4, re_linux_ipv6, re_linux_state, re_linux_rx, re_linux_tx,
re_linux_bytes, re_linux_tx_stats]
# OpenBSD syntax
re_openbsd_interface = re.compile(
r"(?P<name>[a-zA-Z0-9:._-]+):\s+flags=(?P<flags>[0-9]+)<(?P<state>\S+)?>\s+mtu\s+(?P<mtu>[0-9]+)",
re.I)
re_openbsd_ipv4 = re.compile(
r"inet (?P<ipv4_addr>(?:[0-9]{1,3}\.){3}[0-9]{1,3})\s+netmask\s+"
r"(?P<ipv4_mask>(?:[0-9]{1,3}\.){3}[0-9]{1,3})(\s+broadcast\s+"
r"(?P<ipv4_bcast>(?:[0-9]{1,3}\.){3}[0-9]{1,3}))?",
re.I)
re_openbsd_ipv6 = re.compile(
r"inet6\s+(?P<ipv6_addr>\S+)\s+prefixlen\s+(?P<ipv6_mask>[0-9]+)\s+scopeid\s+(?P<ipv6_scope>\w+x\w+)<"
r"(?:link|host)>",
re.I)
re_openbsd_details = re.compile(
r"\S+\s+(?:(?P<mac_addr>[0-9A-Fa-f:?]+)\s+)?txqueuelen\s+[0-9]+\s+\((?P<type>\S+\s?\S+)\)", re.I)
re_openbsd_rx = re.compile(r"RX packets (?P<rx_packets>[0-9]+)\s+bytes\s+(?P<rx_bytes>\d+)\s+.*", re.I)
re_openbsd_rx_stats = re.compile(
r"RX errors (?P<rx_errors>[0-9]+)\s+dropped\s+(?P<rx_dropped>[0-9]+)\s+overruns\s+"
r"(?P<rx_overruns>[0-9]+)\s+frame\s+(?P<rx_frame>[0-9]+)",
re.I)
re_openbsd_tx = re.compile(r"TX packets (?P<tx_packets>[0-9]+)\s+bytes\s+(?P<tx_bytes>\d+)\s+.*", re.I)
re_openbsd_tx_stats = re.compile(
r"TX errors (?P<tx_errors>[0-9]+)\s+dropped\s+(?P<tx_dropped>[0-9]+)\s+overruns\s+"
r"(?P<tx_overruns>[0-9]+)\s+carrier\s+(?P<tx_carrier>[0-9]+)\s+collisions\s+(?P<tx_collisions>[0-9]+)",
re.I)
re_openbsd = [re_openbsd_interface, re_openbsd_ipv4, re_openbsd_ipv6, re_openbsd_details, re_openbsd_rx,
re_openbsd_rx_stats, re_openbsd_tx, re_openbsd_tx_stats]
# FreeBSD syntax
re_freebsd_interface = re.compile(
r"(?P<name>[a-zA-Z0-9:._-]+):\s+flags=(?P<flags>[0-9]+)<(?P<state>\S+)>\s+metric\s+"
r"(?P<metric>[0-9]+)\s+mtu\s+(?P<mtu>[0-9]+)",
re.I)
re_freebsd_ipv4 = re.compile(
r"inet (?P<ipv4_addr>(?:[0-9]{1,3}\.){3}[0-9]{1,3})\s+netmask\s+(?P<ipv4_mask>0x\S+)(\s+broadcast\s+"
r"(?P<ipv4_bcast>(?:[0-9]{1,3}\.){3}[0-9]{1,3}))?",
re.I)
re_freebsd_ipv6 = re.compile(r"\s?inet6\s(?P<ipv6_addr>.*)(?:\%\w+\d+)\sprefixlen\s(?P<ipv6_mask>\d+)(?:\s\w+)?\sscopeid\s(?P<ipv6_scope>\w+x\w+)", re.I)
re_freebsd_details = re.compile(r"ether\s+(?P<mac_addr>[0-9A-Fa-f:?]+)", re.I)
re_freebsd = [re_freebsd_interface, re_freebsd_ipv4, re_freebsd_ipv6, re_freebsd_details]
available_interfaces = dict()
for pattern in [re_linux_interface, re_openbsd_interface, re_freebsd_interface]:
network_interfaces = re.finditer(pattern, source_data)
positions = []
while True:
try:
pos = next(network_interfaces)
positions.append(max(pos.start() - 1, 0))
except StopIteration:
break
if positions:
positions.append(len(source_data))
break
if not positions:
return available_interfaces
for l, r in zip(positions, positions[1:]):
chunk = source_data[l:r]
_interface = dict()
for pattern in re_linux + re_openbsd + re_freebsd:
match = re.search(pattern, chunk.replace('\t', '\n'))
if match:
details = match.groupdict()
for k, v in details.items():
if isinstance(v, str): details[k] = v.strip()
_interface.update(details)
if _interface is not None:
available_interfaces[_interface['name']] = self.update_interface_details(_interface)
return available_interfaces
@staticmethod
def update_interface_details(interface):
for attr in IfconfigParser.attributes:
if attr not in interface:
interface[attr] = None
return namedtuple('Interface', interface.keys())(**interface)
class InterfaceNotFound(Exception):
"""
"""
pass
def process(proc_data):
"""
Final processing to conform to the schema.
@@ -220,7 +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)])

View File

@@ -134,7 +134,7 @@ import jc.utils
class info():
version = '1.1'
version = '1.2'
description = 'iptables command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
@@ -247,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 = []
@@ -259,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")

View File

@@ -77,7 +77,7 @@ import jc.utils
class info():
version = '1.0'
version = '1.1'
description = 'jobs command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
@@ -176,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('-')

View File

@@ -72,7 +72,7 @@ import jc.utils
class info():
version = '1.0'
version = '1.1'
description = 'last and lastb command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
@@ -122,6 +122,9 @@ def process(proc_data):
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
@@ -157,6 +160,7 @@ def parse(data, raw=False, quiet=False):
entry = entry.replace('system boot', 'system_boot')
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])):

View File

@@ -149,7 +149,7 @@ import jc.utils
class info():
version = '1.3'
version = '1.4'
description = 'ls command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
@@ -235,8 +235,9 @@ def parse(data, raw=False, quiet=False):
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
linedata.pop(0)
# Pop following total line if it exists
if re.match(r'total [0-9]+', linedata[0]):
linedata.pop(0)
if linedata:
# Check if -l was used to parse extra data

View File

@@ -97,7 +97,7 @@ import jc.parsers.universal
class info():
version = '1.0'
version = '1.1'
description = 'lsof command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
@@ -180,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:

View File

@@ -56,7 +56,7 @@ import jc.utils
class info():
version = '1.1'
version = '1.2'
description = 'mount command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
@@ -165,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:

View File

@@ -313,7 +313,7 @@ import jc.utils
class info():
version = '1.2'
version = '1.4'
description = 'netstat command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
@@ -498,14 +498,14 @@ def parse_post(raw_data):
if 'proto' in entry and 'kind' in entry:
if entry['kind'] == 'network':
if entry['proto'].find('tcp') != -1:
if 'tcp' in entry['proto']:
entry['transport_protocol'] = 'tcp'
elif entry['proto'].find('udp') != -1:
elif 'udp' in entry['proto']:
entry['transport_protocol'] = 'udp'
else:
entry['transport_protocol'] = None
if entry['proto'].find('6') != -1:
if '6' in entry['proto']:
entry['network_protocol'] = 'ipv6'
else:
entry['network_protocol'] = 'ipv4'
@@ -536,25 +536,34 @@ def parse(data, raw=False, quiet=False):
raw_output = []
network = False
socket = False
bluetooth = False
headers = ''
network_list = []
socket_list = []
for line in cleandata:
if line.find('Active Internet') == 0:
if line.startswith('Active Internet'):
network_list = []
network = True
socket = False
bluetooth = False
continue
if line.find('Active UNIX') == 0:
if line.startswith('Active UNIX'):
socket_list = []
network = False
socket = True
bluetooth = False
continue
if line.find('Proto') == 0:
if line.startswith('Active Bluetooth'):
network = False
socket = False
bluetooth = True
continue
if line.startswith('Proto'):
header_text = normalize_headers(line)
headers = header_text.split()
continue
@@ -567,6 +576,10 @@ def parse(data, raw=False, quiet=False):
socket_list.append(parse_socket(header_text, headers, line))
continue
if bluetooth:
# maybe implement later if requested
continue
for item in [network_list, socket_list]:
for entry in item:
raw_output.append(entry)

View File

@@ -32,7 +32,7 @@ import jc.parsers.universal
class info():
version = '1.0'
version = '1.1'
description = 'pip list command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
@@ -94,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(')')})
@@ -103,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()

View File

@@ -104,7 +104,7 @@ import jc.utils
class info():
version = '1.0'
version = '1.1'
description = 'stat command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
@@ -208,7 +208,7 @@ def parse(data, raw=False, quiet=False):
output_line['file'] = line_list[1]
# populate link_to field if -> found
if output_line['file'].find(' -> ') != -1:
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
@@ -229,7 +229,7 @@ def parse(data, raw=False, quiet=False):
continue
# line #3
if line.find('Device:') == 0:
if line.startswith('Device:'):
line_list = line.split()
output_line['device'] = line_list[1]
output_line['inode'] = line_list[3]
@@ -237,7 +237,7 @@ def parse(data, raw=False, quiet=False):
continue
# line #4
if line.find('Access: (') == 0:
if line.startswith('Access: ('):
line = line.replace('(', ' ').replace(')', ' ').replace('/', ' ')
line_list = line.split()
output_line['access'] = line_list[1]
@@ -249,19 +249,19 @@ def parse(data, raw=False, quiet=False):
continue
# line #5
if line.find('Access: 2') == 0:
if line.startswith('Access: 2'):
line_list = line.split(maxsplit=1)
output_line['access_time'] = line_list[1]
continue
# line #6
if line.find('Modify:') == 0:
if line.startswith('Modify:'):
line_list = line.split(maxsplit=1)
output_line['modify_time'] = line_list[1]
continue
# line #7
if line.find('Change:') == 0:
if line.startswith('Change:'):
line_list = line.split(maxsplit=1)
output_line['change_time'] = line_list[1]
continue

View File

@@ -40,7 +40,7 @@ import jc.utils
class info():
version = '1.0'
version = '1.1'
description = 'systemctl command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
@@ -110,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:

View File

@@ -59,7 +59,7 @@ import jc.utils
class info():
version = '1.0'
version = '1.1'
description = 'systemctl list-jobs command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
@@ -137,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:

View File

@@ -34,7 +34,7 @@ import jc.utils
class info():
version = '1.0'
version = '1.1'
description = 'systemctl list-sockets command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
@@ -102,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:

View File

@@ -31,7 +31,7 @@ import jc.utils
class info():
version = '1.0'
version = '1.1'
description = 'systemctl list-unit-files command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
@@ -99,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:

View File

@@ -1,4 +1,3 @@
ifconfig-parser>=0.0.5
ruamel.yaml>=0.15.0
xmltodict>=0.12.0
Pygments>=2.5.2
Pygments>=2.4.2

2
setup.cfg Normal file
View File

@@ -0,0 +1,2 @@
[metadata]
license_file = LICENSE.md

View File

@@ -5,23 +5,21 @@ with open('README.md', 'r') as f:
setuptools.setup(
name='jc',
version='1.10.0',
version='1.10.10',
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.',
install_requires=[
'ifconfig-parser>=0.0.5',
'ruamel.yaml>=0.15.0',
'xmltodict>=0.12.0',
'Pygments>=2.5.2'
'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(),
include_package_data=True,
packages=setuptools.find_packages(exclude=['*.tests', '*.tests.*', 'tests.*', 'tests']),
entry_points={
'console_scripts': [
'jc=jc.cli:main'

View File

@@ -1 +1 @@
[{"filename": "systemd-private-016de60725a3426792b93fc9f120b8f0-chronyd.service-oZqq4u", "parent": "."}, {"filename": "systemd-private-a30a5a178daa4042b42dfaf5ff9e5f68-chronyd.service-a1tpxv", "parent": "."}, {"filename": "systemd-private-af69d7360f3e40cfa947358c0fb5a6f8-chronyd.service-T3MQ4j", "parent": "."}, {"filename": "tmp.CvALl2jE6u", "parent": "."}, {"filename": "tmp.e7AlxSxY5a", "parent": "."}, {"filename": "tmp.uXm9yegjwj", "parent": "."}, {"filename": "a regular filename", "parent": "./lstest"}, {"filename": "this file has", "parent": "./lstest"}, {"filename": "a combination", "parent": "./lstest"}, {"filename": "of everything", "parent": "./lstest"}, {"filename": "this file has", "parent": "./lstest"}, {"filename": "a newline inside", "parent": "./lstest"}, {"filename": "this file has", "parent": "./lstest"}, {"filename": "four contiguous newlines inside", "parent": "./lstest"}, {"filename": "this file", "parent": "./lstest"}, {"filename": "has", "parent": "./lstest"}, {"filename": "six", "parent": "./lstest"}, {"filename": "newlines", "parent": "./lstest"}, {"filename": "within", "parent": "./lstest"}, {"filename": "this file starts with four newlines", "parent": "./lstest"}, {"filename": "this file starts with one newline", "parent": "./lstest"}]
[{"filename": "lstest", "parent": "."}, {"filename": "systemd-private-016de60725a3426792b93fc9f120b8f0-chronyd.service-oZqq4u", "parent": "."}, {"filename": "systemd-private-a30a5a178daa4042b42dfaf5ff9e5f68-chronyd.service-a1tpxv", "parent": "."}, {"filename": "systemd-private-af69d7360f3e40cfa947358c0fb5a6f8-chronyd.service-T3MQ4j", "parent": "."}, {"filename": "tmp.CvALl2jE6u", "parent": "."}, {"filename": "tmp.e7AlxSxY5a", "parent": "."}, {"filename": "tmp.uXm9yegjwj", "parent": "."}, {"filename": "a regular filename", "parent": "./lstest"}, {"filename": "this file has", "parent": "./lstest"}, {"filename": "a combination", "parent": "./lstest"}, {"filename": "of everything", "parent": "./lstest"}, {"filename": "this file has", "parent": "./lstest"}, {"filename": "a newline inside", "parent": "./lstest"}, {"filename": "this file has", "parent": "./lstest"}, {"filename": "four contiguous newlines inside", "parent": "./lstest"}, {"filename": "this file", "parent": "./lstest"}, {"filename": "has", "parent": "./lstest"}, {"filename": "six", "parent": "./lstest"}, {"filename": "newlines", "parent": "./lstest"}, {"filename": "within", "parent": "./lstest"}, {"filename": "this file starts with four newlines", "parent": "./lstest"}, {"filename": "this file starts with one newline", "parent": "./lstest"}]

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

1
tests/fixtures/fedora32/last.json vendored Normal file
View File

@@ -0,0 +1 @@
[{"user": "kbrazil", "tty": "pts/0", "hostname": "192.168.71.1", "login": "Fri May 1 15:25", "logout": "gone - no logout"}, {"user": "kbrazil", "tty": "tty1", "hostname": null, "login": "Fri May 1 15:24", "logout": "gone - no logout"}, {"user": "reboot", "tty": "system boot", "hostname": "5.6.6-300.fc32.x", "login": "Fri May 1 15:24", "logout": "running"}, {"user": "kbrazil", "tty": "pts/0", "hostname": "192.168.71.1", "login": "Thu Apr 30 15:27", "logout": "15:22", "duration": "23:55"}, {"user": "kbrazil", "tty": "tty1", "hostname": null, "login": "Thu Apr 30 15:25", "logout": "down", "duration": "23:57"}, {"user": "reboot", "tty": "system boot", "hostname": "5.6.6-300.fc32.x", "login": "Thu Apr 30 15:22", "logout": "15:22", "duration": "1+00:00"}]

8
tests/fixtures/fedora32/last.out vendored Normal file
View File

@@ -0,0 +1,8 @@
kbrazil pts/0 192.168.71.1 Fri May 1 15:25 gone - no logout
kbrazil tty1 Fri May 1 15:24 gone - no logout
reboot system boot 5.6.6-300.fc32.x Fri May 1 15:24 still running
kbrazil pts/0 192.168.71.1 Thu Apr 30 15:27 - 15:22 (23:55)
kbrazil tty1 Thu Apr 30 15:25 - down (23:57)
reboot system boot 5.6.6-300.fc32.x Thu Apr 30 15:22 - 15:22 (1+00:00)
wtmp begins Thu Apr 30 15:22:02 2020

1
tests/fixtures/fedora32/netstat.json vendored Normal file

File diff suppressed because one or more lines are too long

139
tests/fixtures/fedora32/netstat.out vendored Normal file
View File

@@ -0,0 +1,139 @@
Active Internet connections (w/o servers)
Proto Recv-Q Send-Q Local Address Foreign Address State
tcp 0 0 localhost.localdoma:ssh 192.168.71.1:52882 ESTABLISHED
udp 0 0 localhost.locald:bootpc 192.168.71.254:bootps ESTABLISHED
Active UNIX domain sockets (w/o servers)
Proto RefCnt Flags Type State I-Node Path
unix 2 [ ] DGRAM 36371 /run/user/1000/systemd/notify
unix 3 [ ] DGRAM 15453 /run/systemd/notify
unix 9 [ ] DGRAM 15471 /run/systemd/journal/dev-log
unix 2 [ ] DGRAM 165956 @userdb-9bf8f59ca8f2bcce47a377edbaf8985d
unix 12 [ ] DGRAM 15479 /run/systemd/journal/socket
unix 2 [ ] DGRAM 29064 /var/run/chrony/chronyd.sock
unix 2 [ ] DGRAM 165866 @userdb-3f208fa7c8c6d98822a696ee7ca5e3ad
unix 2 [ ] DGRAM 165863 @userdb-da253950013f5ea3bc51a4049480d04e
unix 2 [ ] DGRAM 165865
unix 3 [ ] STREAM CONNECTED 36476
unix 3 [ ] STREAM CONNECTED 31389
unix 3 [ ] STREAM CONNECTED 30999 /var/lib/sss/pipes/private/sbus-dp_implicit_files.692
unix 3 [ ] STREAM CONNECTED 33770 /run/dbus/system_bus_socket
unix 3 [ ] STREAM CONNECTED 30731
unix 2 [ ] DGRAM 24653
unix 3 [ ] STREAM CONNECTED 31370 /var/lib/sss/pipes/private/sbus-dp_implicit_files.692
unix 2 [ ] DGRAM 165862
unix 2 [ ] DGRAM 33725
unix 3 [ ] STREAM CONNECTED 33885
unix 3 [ ] STREAM CONNECTED 31173
unix 3 [ ] STREAM CONNECTED 31019
unix 3 [ ] STREAM CONNECTED 33886
unix 3 [ ] STREAM CONNECTED 30735 /var/lib/sss/pipes/private/sbus-monitor
unix 3 [ ] STREAM CONNECTED 166231
unix 2 [ ] DGRAM 30987
unix 2 [ ] DGRAM 24745
unix 2 [ ] DGRAM 165955
unix 2 [ ] DGRAM 30575
unix 3 [ ] STREAM CONNECTED 31062 /run/dbus/system_bus_socket
unix 2 [ ] STREAM CONNECTED 35419
unix 3 [ ] STREAM CONNECTED 31399 /run/dbus/system_bus_socket
unix 2 [ ] DGRAM 32430
unix 2 [ ] DGRAM 29403
unix 3 [ ] STREAM CONNECTED 166232 /var/lib/sss/pipes/nss
unix 2 [ ] DGRAM 31127
unix 2 [ ] DGRAM 29538
unix 2 [ ] STREAM CONNECTED 33876
unix 3 [ ] STREAM CONNECTED 31020 /var/lib/sss/pipes/private/sbus-monitor
unix 3 [ ] STREAM CONNECTED 30998
unix 3 [ ] STREAM CONNECTED 36475
unix 2 [ ] DGRAM 36002
unix 3 [ ] STREAM CONNECTED 33769
unix 3 [ ] STREAM CONNECTED 31390 /var/lib/sss/pipes/private/sbus-monitor
unix 2 [ ] STREAM CONNECTED 33132
unix 3 [ ] STREAM CONNECTED 31398
unix 3 [ ] STREAM CONNECTED 30022
unix 3 [ ] DGRAM 24748
unix 3 [ ] DGRAM 24747
unix 2 [ ] DGRAM 36348
unix 2 [ ] DGRAM 32085
unix 3 [ ] STREAM CONNECTED 31168 /run/systemd/journal/stdout
unix 3 [ ] DGRAM 15455
unix 3 [ ] STREAM CONNECTED 30830
unix 3 [ ] STREAM CONNECTED 26911
unix 3 [ ] STREAM CONNECTED 35960
unix 2 [ ] DGRAM 24662
unix 2 [ ] STREAM CONNECTED 36345
unix 3 [ ] STREAM CONNECTED 32538
unix 3 [ ] STREAM CONNECTED 28747
unix 3 [ ] STREAM CONNECTED 31026
unix 3 [ ] DGRAM 28615
unix 3 [ ] STREAM CONNECTED 31713 /run/dbus/system_bus_socket
unix 3 [ ] STREAM CONNECTED 31167
unix 3 [ ] STREAM CONNECTED 35989 /run/systemd/journal/stdout
unix 3 [ ] STREAM CONNECTED 36303 /run/systemd/journal/stdout
unix 3 [ ] STREAM CONNECTED 31809 /run/systemd/journal/stdout
unix 3 [ ] STREAM CONNECTED 29396 /run/systemd/journal/stdout
unix 2 [ ] STREAM CONNECTED 34911
unix 3 [ ] DGRAM 36374
unix 2 [ ] STREAM CONNECTED 32241
unix 3 [ ] STREAM CONNECTED 32057 /run/dbus/system_bus_socket
unix 3 [ ] STREAM CONNECTED 32000 /run/systemd/journal/stdout
unix 2 [ ] DGRAM 33083
unix 3 [ ] STREAM CONNECTED 31017
unix 3 [ ] DGRAM 36373
unix 3 [ ] STREAM CONNECTED 34101
unix 3 [ ] STREAM CONNECTED 32539 /run/systemd/journal/stdout
unix 3 [ ] STREAM CONNECTED 26914
unix 3 [ ] STREAM CONNECTED 29395
unix 3 [ ] STREAM CONNECTED 31022
unix 3 [ ] DGRAM 15456
unix 3 [ ] STREAM CONNECTED 29251 /run/systemd/journal/stdout
unix 3 [ ] STREAM CONNECTED 26915
unix 2 [ ] STREAM CONNECTED 33052
unix 3 [ ] STREAM CONNECTED 31999
unix 2 [ ] DGRAM 29496
unix 3 [ ] STREAM CONNECTED 28400
unix 3 [ ] STREAM CONNECTED 36378 /run/dbus/system_bus_socket
unix 3 [ ] STREAM CONNECTED 31808
unix 3 [ ] STREAM CONNECTED 29250
unix 3 [ ] STREAM CONNECTED 24582
unix 3 [ ] STREAM CONNECTED 32056
unix 3 [ ] STREAM CONNECTED 31347
unix 3 [ ] STREAM CONNECTED 28678 /run/systemd/journal/stdout
unix 2 [ ] DGRAM 30980
unix 3 [ ] STREAM CONNECTED 31348 /run/systemd/journal/stdout
unix 3 [ ] STREAM CONNECTED 28677
unix 3 [ ] STREAM CONNECTED 34102 /run/dbus/system_bus_socket
unix 3 [ ] STREAM CONNECTED 24805 /run/systemd/journal/stdout
unix 3 [ ] STREAM CONNECTED 29322 /run/systemd/journal/stdout
unix 2 [ ] DGRAM 36357
unix 3 [ ] STREAM CONNECTED 30832 /run/systemd/journal/stdout
unix 2 [ ] DGRAM 26913
unix 2 [ ] STREAM CONNECTED 32010
unix 3 [ ] STREAM CONNECTED 28748 /run/systemd/journal/stdout
unix 2 [ ] DGRAM 28602
unix 3 [ ] STREAM CONNECTED 28401 /run/systemd/journal/stdout
unix 3 [ ] STREAM CONNECTED 31069 /run/dbus/system_bus_socket
unix 3 [ ] DGRAM 28616
unix 3 [ ] STREAM CONNECTED 32334 /run/systemd/journal/stdout
unix 3 [ ] STREAM CONNECTED 31712
unix 3 [ ] STREAM CONNECTED 36302
unix 3 [ ] STREAM CONNECTED 32333
unix 3 [ ] STREAM CONNECTED 32093
unix 3 [ ] STREAM CONNECTED 26912
unix 2 [ ] STREAM CONNECTED 35774
unix 3 [ ] STREAM CONNECTED 29321
unix 3 [ ] STREAM CONNECTED 31025
unix 3 [ ] STREAM CONNECTED 28470
unix 3 [ ] STREAM CONNECTED 32094 /run/dbus/system_bus_socket
unix 3 [ ] STREAM CONNECTED 31259 /run/systemd/journal/stdout
unix 2 [ ] DGRAM 36471
unix 3 [ ] STREAM CONNECTED 31054
unix 3 [ ] STREAM CONNECTED 31064 /run/dbus/system_bus_socket
unix 3 [ ] STREAM CONNECTED 28471 /run/systemd/journal/stdout
unix 3 [ ] STREAM CONNECTED 36377
unix 2 [ ] STREAM CONNECTED 35788
unix 3 [ ] STREAM CONNECTED 31258
unix 3 [ ] STREAM CONNECTED 31065 /run/dbus/system_bus_socket
Active Bluetooth connections (w/o servers)
Proto Destination Source State PSM DCID SCID IMTU OMTU Security
Proto Destination Source State Channel

File diff suppressed because one or more lines are too long

View File

@@ -911,13 +911,6 @@
112 /usr/local/Homebrew/Library/Homebrew/rubocops/cask
40 /usr/local/Homebrew/Library/Homebrew/rubocops/extend
344 /usr/local/Homebrew/Library/Homebrew/rubocops
288 /usr/local/Homebrew/Library/Homebrew/shims/linux/super
288 /usr/local/Homebrew/Library/Homebrew/shims/linux
344 /usr/local/Homebrew/Library/Homebrew/shims/mac/super
344 /usr/local/Homebrew/Library/Homebrew/shims/mac
16 /usr/local/Homebrew/Library/Homebrew/shims/scm
280 /usr/local/Homebrew/Library/Homebrew/shims/super
928 /usr/local/Homebrew/Library/Homebrew/shims
24 /usr/local/Homebrew/Library/Homebrew/test/cask/artifact/shared_examples
144 /usr/local/Homebrew/Library/Homebrew/test/cask/artifact
24 /usr/local/Homebrew/Library/Homebrew/test/cask/cask_loader

File diff suppressed because one or more lines are too long

View File

@@ -317,13 +317,6 @@
32 /usr/local/Homebrew/Library/Homebrew/cli
56 /usr/local/Homebrew/Library/Homebrew/manpages
8 /usr/local/Homebrew/Library/Homebrew/version
64 /usr/local/Homebrew/Library/Homebrew/shims/mac/super
64 /usr/local/Homebrew/Library/Homebrew/shims/mac
24 /usr/local/Homebrew/Library/Homebrew/shims/super
8 /usr/local/Homebrew/Library/Homebrew/shims/linux/super
8 /usr/local/Homebrew/Library/Homebrew/shims/linux
8 /usr/local/Homebrew/Library/Homebrew/shims/scm
104 /usr/local/Homebrew/Library/Homebrew/shims
8 /usr/local/Homebrew/Library/Homebrew/debrew
56 /usr/local/Homebrew/Library/Homebrew/os/mac/pkgconfig/10.8
24 /usr/local/Homebrew/Library/Homebrew/os/mac/pkgconfig/10.6

File diff suppressed because one or more lines are too long

View File

@@ -1 +1 @@
[{"filename": "systemd-private-65c1089f1d4c4cf5bc50ca55478abfde-systemd-resolved.service-La9AqY", "parent": "."}, {"filename": "systemd-private-65c1089f1d4c4cf5bc50ca55478abfde-systemd-timesyncd.service-L7q4cJ", "parent": "."}, {"filename": "vmware-root_670-2722828838", "parent": "."}, {"filename": "a regular filename", "parent": "./lstest"}, {"filename": "this file has", "parent": "./lstest"}, {"filename": "a combination", "parent": "./lstest"}, {"filename": "of everything", "parent": "./lstest"}, {"filename": "this file has", "parent": "./lstest"}, {"filename": "a newline inside", "parent": "./lstest"}, {"filename": "this file has", "parent": "./lstest"}, {"filename": "four contiguous newlines inside", "parent": "./lstest"}, {"filename": "this file", "parent": "./lstest"}, {"filename": "has", "parent": "./lstest"}, {"filename": "six", "parent": "./lstest"}, {"filename": "newlines", "parent": "./lstest"}, {"filename": "within", "parent": "./lstest"}, {"filename": "this file starts with four newlines", "parent": "./lstest"}, {"filename": "this file starts with one newline", "parent": "./lstest"}]
[{"filename": "lstest", "parent": "."}, {"filename": "systemd-private-65c1089f1d4c4cf5bc50ca55478abfde-systemd-resolved.service-La9AqY", "parent": "."}, {"filename": "systemd-private-65c1089f1d4c4cf5bc50ca55478abfde-systemd-timesyncd.service-L7q4cJ", "parent": "."}, {"filename": "vmware-root_670-2722828838", "parent": "."}, {"filename": "a regular filename", "parent": "./lstest"}, {"filename": "this file has", "parent": "./lstest"}, {"filename": "a combination", "parent": "./lstest"}, {"filename": "of everything", "parent": "./lstest"}, {"filename": "this file has", "parent": "./lstest"}, {"filename": "a newline inside", "parent": "./lstest"}, {"filename": "this file has", "parent": "./lstest"}, {"filename": "four contiguous newlines inside", "parent": "./lstest"}, {"filename": "this file", "parent": "./lstest"}, {"filename": "has", "parent": "./lstest"}, {"filename": "six", "parent": "./lstest"}, {"filename": "newlines", "parent": "./lstest"}, {"filename": "within", "parent": "./lstest"}, {"filename": "this file starts with four newlines", "parent": "./lstest"}, {"filename": "this file starts with one newline", "parent": "./lstest"}]

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -31,6 +31,9 @@ class MyTests(unittest.TestCase):
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/ubuntu-18.04/last-w.out'), 'r', encoding='utf-8') as f:
self.ubuntu_18_4_last_w = f.read()
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/fedora32/last.out'), 'r', encoding='utf-8') as f:
self.fedora32_last = f.read()
# output
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/centos-7.7/last.json'), 'r', encoding='utf-8') as f:
self.centos_7_7_last_json = json.loads(f.read())
@@ -53,6 +56,9 @@ class MyTests(unittest.TestCase):
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/ubuntu-18.04/last-w.json'), 'r', encoding='utf-8') as f:
self.ubuntu_18_4_last_w_json = json.loads(f.read())
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/fedora32/last.json'), 'r', encoding='utf-8') as f:
self.fedora32_last_json = json.loads(f.read())
def test_last_centos_7_7(self):
"""
Test plain 'last' on Centos 7.7
@@ -95,6 +101,12 @@ class MyTests(unittest.TestCase):
"""
self.assertEqual(jc.parsers.last.parse(self.ubuntu_18_4_last_w, quiet=True), self.ubuntu_18_4_last_w_json)
def test_last_fedora32(self):
"""
Test plain 'last' on Fedora32
"""
self.assertEqual(jc.parsers.last.parse(self.fedora32_last, quiet=True), self.fedora32_last_json)
if __name__ == '__main__':
unittest.main()

View File

@@ -40,6 +40,9 @@ class MyTests(unittest.TestCase):
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/ubuntu-18.04/netstat-sudo-aeep.out'), 'r', encoding='utf-8') as f:
self.ubuntu_18_4_netstat_sudo_aeep = f.read()
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/fedora32/netstat.out'), 'r', encoding='utf-8') as f:
self.fedora32_netstat = f.read()
# output
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/centos-7.7/netstat.json'), 'r', encoding='utf-8') as f:
self.centos_7_7_netstat_json = json.loads(f.read())
@@ -71,6 +74,9 @@ class MyTests(unittest.TestCase):
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/ubuntu-18.04/netstat-sudo-aeep.json'), 'r', encoding='utf-8') as f:
self.ubuntu_18_4_netstat_sudo_aeep_json = json.loads(f.read())
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/fedora32/netstat.json'), 'r', encoding='utf-8') as f:
self.fedora32_netstat_json = json.loads(f.read())
def test_netstat_centos_7_7(self):
"""
Test 'netstat' on Centos 7.7
@@ -131,6 +137,12 @@ class MyTests(unittest.TestCase):
"""
self.assertEqual(jc.parsers.netstat.parse(self.ubuntu_18_4_netstat_sudo_aeep, quiet=True), self.ubuntu_18_4_netstat_sudo_aeep_json)
def test_netstat_fedora32(self):
"""
Test 'netstat' on Fedora32
"""
self.assertEqual(jc.parsers.netstat.parse(self.fedora32_netstat, quiet=True), self.fedora32_netstat_json)
if __name__ == '__main__':
unittest.main()