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

Compare commits

...

60 Commits

Author SHA1 Message Date
Kelly Brazil
ab482e521d Merge pull request #163 from kellyjonbrazil/dev
Dev v1.16.2
2021-08-31 09:50:38 -07:00
Kelly Brazil
e08b61fa81 add schema note to sfdisk 2021-08-31 08:54:23 -07:00
Kelly Brazil
ce61bd1d2b add tests for -F fixes 2021-08-31 08:50:42 -07:00
Kelly Brazil
7b708f7518 Don't convert 'size' to int, except for legacy -d support. Change partition table detection logic to fix -F output 2021-08-31 08:39:54 -07:00
Kelly Brazil
89ca50c7fc add -l to sfdisk raw example 2021-08-30 21:56:36 -07:00
Kelly Brazil
fb54899dcc doc update 2021-08-30 21:53:40 -07:00
Kelly Brazil
0a625ad7dd remove unneeded lines 2021-08-30 21:52:55 -07:00
Kelly Brazil
d32e45efbe add sfdisk tests 2021-08-30 21:49:07 -07:00
Kelly Brazil
c77696bc78 version bump 2021-08-30 20:55:26 -07:00
Kelly Brazil
736fde9e78 add support for newer versions of sfdisk 2021-08-30 20:51:19 -07:00
Kelly Brazil
9c1ad92fed fix examples 2021-08-26 10:31:51 -07:00
Kelly Brazil
1a9fd2139d add "For new parsers:" 2021-08-26 10:29:13 -07:00
Kelly Brazil
7661e7f27a formatting 2021-08-26 10:27:11 -07:00
Kelly Brazil
f857b7fbf7 add custom parser folder info 2021-08-26 10:25:37 -07:00
Kelly Brazil
d94d12dbc5 add foo parser template info 2021-08-26 10:20:53 -07:00
Kelly Brazil
700916276a Merge pull request #159 from kellyjonbrazil/master
Sync Master to dev
2021-08-26 10:01:08 -07:00
Kelly Brazil
834e52369c update man page 2021-08-16 10:33:26 -07:00
Kelly Brazil
1ce53365de Merge pull request #156 from kellyjonbrazil/dev
Dev v1.16.1
2021-08-16 10:32:03 -07:00
Kelly Brazil
473f70668f Merge branch 'master' into dev
# Conflicts:
#	jc/man/jc.1.gz
#	man/jc.1
#	man/jc.1.gz
2021-08-16 10:28:11 -07:00
Kelly Brazil
0dbd2702f6 reformat doc_text for nicer indentation 2021-08-13 17:17:51 -07:00
Kelly Brazil
01e3764a9b fix join syntax 2021-08-13 16:03:46 -07:00
Kelly Brazil
ff9c81722a try/except for shlex.join since only available in python 3.8 2021-08-13 16:01:45 -07:00
Kelly Brazil
166aef7a02 version bump 2021-08-13 15:22:33 -07:00
Kelly Brazil
78caf7646b add stat fix 2021-08-13 15:19:41 -07:00
Kelly Brazil
1f99d40cec fix filename with spaces for osx/bsd 2021-08-13 15:19:31 -07:00
Kelly Brazil
4c2912d3d5 use shlex.join to quote run_command_str for better error messages 2021-08-13 15:19:17 -07:00
Kelly Brazil
45e6e06be5 add glob and process substitution fixes 2021-08-13 14:19:45 -07:00
Kelly Brazil
fdbe3e05f3 fix issue with globs not including filenames with spaces with magic syntax (introduced during switch to use subprocess to grab process exit code). No longer need to shlex quote arguments. 2021-08-13 14:19:26 -07:00
Kelly Brazil
7cc168f640 add comment 2021-08-12 16:55:07 -07:00
Kelly Brazil
ff2d609c9b add close_fds=False in subprocess.Popen() to allow process substitution in magic syntax. Also check for too many open files exception 2021-08-12 16:36:35 -07:00
Kelly Brazil
2689697b4c formatting 2021-07-22 17:03:00 -07:00
Kelly Brazil
f90a0ea8ab formatting 2021-07-22 12:20:48 -07:00
Kelly Brazil
caabe60f84 fix kv example 2021-07-22 12:20:31 -07:00
Kelly Brazil
2bef4ed603 package updates 2021-07-21 08:47:16 -07:00
Kelly Brazil
ee57be533b fix sfdisk example 2021-07-20 12:35:02 -05:00
Kelly Brazil
c5b7aaca25 Merge pull request #149 from kellyjonbrazil/dev
Dev v1.16.0
2021-07-20 10:19:14 -07:00
Kelly Brazil
7a1be905bb version bump 2021-07-20 12:15:29 -05:00
Kelly Brazil
5798495a11 add 1.15.7 to 1.16.0 changes 2021-07-20 12:13:33 -05:00
Kelly Brazil
46171e2202 add sfdisk example 2021-07-20 12:13:14 -05:00
Kelly Brazil
dd5c924ff5 add man page location change 2021-07-19 12:13:46 -07:00
Kelly Brazil
30c4ab2976 new man page location 2021-07-19 12:08:50 -07:00
Kelly Brazil
26ea4d47b3 version bump and deprecate /man/jc.1.gz and /jc/man/jc.1.gz 2021-07-19 12:04:25 -07:00
Kelly Brazil
2732cd175c include CHANGELOG in source distribution 2021-07-12 08:10:18 -07:00
Kelly Brazil
3e54b597be add timezone change for unit tests 2021-07-07 09:00:19 -07:00
Kelly Brazil
f10ebea209 update tests to set correct timezone on POSIX systems 2021-07-07 08:53:52 -07:00
Kelly Brazil
2c6f3993cb remove extra comma 2021-07-01 15:53:10 -07:00
Kelly Brazil
708a696920 remove extra lines 2021-07-01 15:51:38 -07:00
Kelly Brazil
20bbb5d331 remove extra lines 2021-07-01 15:50:55 -07:00
Kelly Brazil
223e785b54 rename variable 2021-06-30 17:07:43 -07:00
Kelly Brazil
3d78692c59 add sfdisk tests 2021-06-30 16:54:34 -07:00
Kelly Brazil
5321a15dcf update examples 2021-06-30 14:30:35 -07:00
Kelly Brazil
a452f8252a fix integer conversion 2021-06-30 14:27:12 -07:00
Kelly Brazil
49267f09ac change to root prompt in examples 2021-06-30 14:06:15 -07:00
Kelly Brazil
db47f35783 add working sfdisk parser 2021-06-30 12:38:36 -07:00
Kelly Brazil
d48abf312c Merge pull request #147 from kellyjonbrazil/master
sync to dev
2021-06-30 10:10:59 -07:00
Kelly Brazil
3b22ce4110 fix local plugin parser issue where parser has .py in the name but it is not at the end 2021-06-28 08:04:46 -07:00
Kelly Brazil
c521ca5bc9 change possible to available 2021-06-11 11:41:16 -07:00
Kelly Brazil
3ddc1c6659 add jello 2021-06-11 11:39:38 -07:00
Kelly Brazil
a8e19402b7 remove temporary fix 2021-06-09 08:33:48 -07:00
Kelly Brazil
0927902b30 temp fix for wrong url 2021-06-08 16:51:03 -07:00
64 changed files with 1790 additions and 47 deletions

View File

@@ -1,5 +1,35 @@
jc changelog
20210830 v1.16.2
- Note to Package Maintainers: please see note at 20210720 v1.16.0
- Update sfdisk parser to support the -F option and newer versions of sfdisk
20210813 v1.16.1
- Note to Package Maintainers: please see note at 20210720 v1.16.0
- Fix issue with process substitution with the magic syntax
- Fix issue with globs not including filenames with spaces with magic syntax
- Fix stat parser to properly handle filenames with spaces on macOS/BSD
20210720 v1.16.0
- Note to Package Maintainers:
TL;DR: `/man/jc.1.gz` and `/jc/man/jc.1.gz` are deprecated and only `/man/jc.1` should be used.
The Man page in the PyPi source packages will be moving from `/jc/man/jc.1.gz` to `/man/jc.1`
in version 1.17.0. For now the Man pages will be available in both locations, but be aware that
the Man page at `/jc/man/jc.1.gz` is now considered deprecated.
Also, starting in v1.17.0, the Man page will no longer be compressed in the source package,
therefore `/man/jc.1.gz` should also be considered deprecated and will no longer be available
after v1.17.0. Please use `/man/jc.1` and compress downstream if you would like.
- Include CHANGELOG in source distribution
- Fix Man page location in source packages
- Add sfdisk command parser tested on linux
- Update unit test files to change the timezone when needed (POSIX only)
- Binaries and DEB/RPM/MSI packages now include Python 3.9.5 interpreter
20210628 v1.15.6
- Fix issue to only load local plugin parsers that have filenames that end in .py
20210520 v1.15.5
- Fix issue where help and about information would not display if a 3rd party parser library was missing. (e.g. xmltodict)
- Add more error message detail when encountering ParseError and LibraryNotFound exceptions

View File

@@ -15,11 +15,12 @@ Pull requests are the best way to propose changes to the codebase (we use [Githu
1. Open an issue to discuss the new feature, bug fix, or parser before opening a pull request. For new parsers, it is important to agree upon a schema before developing the parser.
2. Fork the repo and create your branch from `dev`, if available, otherwise `master`.
3. If you've added code that should be tested, add tests. All new parsers should have several sample outputs and tests.
4. Documentation is auto-generated from docstrings, so ensure they are clear and accurate.
5. Ensure the test suite passes. (Note: "**America/Los_Angeles**" timezone should be configured on the test system)
6. Make sure your code lints.
7. Issue that pull request!
3. For new parsers: Use the `jc/parsers/foo.py` parser as a [template](https://github.com/kellyjonbrazil/jc/blob/master/jc/parsers/foo.py) to get started. You can even place a new parser python module file in the [parser plugin directory](https://github.com/kellyjonbrazil/jc#custom-parsers) to get started right away with just a standard `jc` installation.
4. If you've added code that should be tested, add tests. All new parsers should have several sample outputs and tests.
5. Documentation is auto-generated from docstrings, so ensure they are clear and accurate.
6. Ensure the test suite passes. (Note: "**America/Los_Angeles**" timezone should be configured on the test system)
7. Make sure your code lints.
8. Issue that pull request!
## Parser Schema Guidelines
- Try to keep the schema as flat as possible - typically a list of flat dictionaries
@@ -34,11 +35,11 @@ Bad:
```
{
"Interface 1": [
192.168.1.1,
172.16.1.1
"192.168.1.1",
"172.16.1.1"
],
"Wifi Interface 1": [
10.1.1.1
"10.1.1.1"
]
}
```
@@ -48,14 +49,14 @@ Good:
{
"interface": "Interface 1",
"ip_addresses": [
192.168.1.1,
172.16.1.1
"192.168.1.1",
"172.16.1.1"
]
},
{
"interface": "Wifi Interface 1",
"ip_addresses": [
10.1.1.1
"10.1.1.1"
]
}
]

View File

@@ -2556,6 +2556,75 @@ rpm_qia | jc --rpm_qi -p # or: jc -p rpm -qia
}
]
```
### sfdisk
```bash
sfdisk -l | jc --sfdisk -p # or jc -p sfdisk -l
```
```json
[
{
"disk": "/dev/sda",
"cylinders": 2610,
"heads": 255,
"sectors_per_track": 63,
"units": "cylinders of 8225280 bytes, blocks of 1024 bytes, counting from 0",
"partitions": [
{
"device": "/dev/sda1",
"boot": true,
"start": 0,
"end": 130,
"cyls": 131,
"blocks": 1048576,
"id": "83",
"system": "Linux"
},
{
"device": "/dev/sda2",
"boot": false,
"start": 130,
"end": 2610,
"cyls": 2481,
"blocks": 19921920,
"id": "8e",
"system": "Linux LVM"
},
{
"device": "/dev/sda3",
"boot": false,
"start": 0,
"end": null,
"cyls": 0,
"blocks": 0,
"id": "0",
"system": "Empty"
},
{
"device": "/dev/sda4",
"boot": false,
"start": 0,
"end": null,
"cyls": 0,
"blocks": 0,
"id": "0",
"system": "Empty"
}
]
},
{
"disk": "/dev/mapper/centos-root",
"cylinders": 2218,
"heads": 255,
"sectors_per_track": 63
},
{
"disk": "/dev/mapper/centos-swap",
"cylinders": 261,
"heads": 255,
"sectors_per_track": 63
}
]
```
### /etc/shadow file
```bash
cat /etc/shadow | jc --shadow -p

View File

@@ -1 +1,3 @@
include jc/man/jc.1.gz
include man/jc.1
include CHANGELOG

View File

@@ -1,7 +1,7 @@
![Tests](https://github.com/kellyjonbrazil/jc/workflows/Tests/badge.svg?branch=master)
![Pypi](https://img.shields.io/pypi/v/jc.svg)
> Try the new `jc` [web demo](https://jc-web-demo.herokuapp.com/)!
> Try the `jc` [web demo](https://jc-web-demo.herokuapp.com/)
> JC is [now available](https://galaxy.ansible.com/community/general) as an Ansible filter plugin in the `community.general` collection! See this [blog post](https://blog.kellybrazil.com/2020/08/30/parsing-command-output-in-ansible-with-jc/) for an example.
@@ -19,7 +19,7 @@ dig example.com | jc --dig
39049,"data":"93.184.216.34"}],"query_time":49,"server":"2600:1700:bab0:d40::1#53(2600:1700:bab0:d40::1)","when":
"Fri Apr 16 16:09:00 PDT 2021","rcvd":56,"when_epoch":1618614540,"when_epoch_utc":null}]
```
This allows further command-line processing of output with tools like `jq` by piping commands:
This allows further command-line processing of output with tools like `jq` or [`jello`](https://github.com/kellyjonbrazil/jello) by piping commands:
```bash
$ dig example.com | jc --dig | jq -r '.[].answer[].data'
93.184.216.34
@@ -60,7 +60,7 @@ The `jc` parsers can also be used as python modules. In this case the output wil
'2600:1700:bab0:d40::1#53(2600:1700:bab0:d40::1)', 'when': 'Fri Apr 16 16:13:00 PDT 2021', 'rcvd': 56,
'when_epoch': 1618614780, 'when_epoch_utc': None}]
```
Two representations of the data are possible. The default representation uses a strict schema per parser and converts known numbers to int/float JSON values. Certain known values of `None` are converted to JSON `null`, known boolean values are converted, and, in some cases, additional semantic context fields are added.
Two representations of the data are available. The default representation uses a strict schema per parser and converts known numbers to int/float JSON values. Certain known values of `None` are converted to JSON `null`, known boolean values are converted, and, in some cases, additional semantic context fields are added.
To access the raw, pre-processed JSON, use the `-r` cli option or the `raw=True` function parameter in `parse()`.
@@ -83,7 +83,7 @@ Use Cases:
- [Nornir command output parsing](https://blog.kellybrazil.com/2020/12/09/parsing-command-output-in-nornir-with-jc/)
## Installation
There are several ways to get `jc`. You can install via `pip`; other OS package repositories like `apt-get`, `dnf`, `zypper`, `pacman`, `nix-env`, `guix`, `brew`, or `portsnap`; via DEB, RPM, and MSI packaged binaries for linux and Windows; or by downloading the correct binary for your architecture and running it anywhere on your filesystem.
There are several ways to get `jc`. You can install via `pip`, OS package repositories, via DEB/RPM/MSI packaged binaries for linux and Windows, or by downloading the correct binary for your architecture and running it anywhere on your filesystem.
### Pip (macOS, linux, unix, Windows)
```bash
@@ -100,7 +100,7 @@ pip3 install jc
| Arch linux | `pacman -S jc` |
| NixOS linux | `nix-env -iA nixpkgs.jc` or `nix-env -iA nixos.jc` |
| Guix System linux | `guix install jc` |
| MacOS | `brew install jc` |
| macOS | `brew install jc` |
| FreeBSD | `portsnap fetch update && cd /usr/ports/textproc/py-jc && make install clean` |
| Ansible filter plugin | `ansible-galaxy collection install community.general` |
@@ -169,6 +169,7 @@ The JSON output can be compact (default) or pretty formatted with the `-p` optio
- `--ps` enables the `ps` command parser ([documentation](https://kellyjonbrazil.github.io/jc/docs/parsers/ps))
- `--route` enables the `route` command parser ([documentation](https://kellyjonbrazil.github.io/jc/docs/parsers/route))
- `--rpm-qi` enables the `rpm -qi` command parser ([documentation](https://kellyjonbrazil.github.io/jc/docs/parsers/rpm_qi))
- `--sfdisk` enables the `sfdisk` command parser ([documentation](https://kellyjonbrazil.github.io/jc/docs/parsers/sfdisk))
- `--shadow` enables the `/etc/shadow` file parser ([documentation](https://kellyjonbrazil.github.io/jc/docs/parsers/shadow))
- `--ss` enables the `ss` command parser ([documentation](https://kellyjonbrazil.github.io/jc/docs/parsers/ss))
- `--stat` enables the `stat` command parser ([documentation](https://kellyjonbrazil.github.io/jc/docs/parsers/stat))

View File

@@ -38,7 +38,7 @@ Examples:
# but can be preserved with the -r argument
occupation:"Engineer"
$ cat keyvalue.txt | jc --ini -p
$ cat keyvalue.txt | jc --kv -p
{
"name": "John Doe",
"address": "555 California Drive",

View File

@@ -25,7 +25,7 @@ Schema:
"genmask": string,
"flags": string,
"flags_pretty": [
string,
string
]
"metric": integer,
"ref": integer,
@@ -76,7 +76,6 @@ Examples:
}
]
$ route -ee | jc --route -p -r
[
{
@@ -108,7 +107,6 @@ Examples:
]
## info
```python
info()

228
docs/parsers/sfdisk.md Normal file
View File

@@ -0,0 +1,228 @@
[Home](https://kellyjonbrazil.github.io/jc/)
# jc.parsers.sfdisk
jc - JSON CLI output utility `sfdisk` command output parser
Supports the following `sfdisk` options:
- `-l`
- `-F`
- `-d` (deprecated - only for older versions of util-linux)
- `-uM` (deprecated - only for older versions of util-linux)
- `-uC` (deprecated - only for older versions of util-linux)
- `-uS` (deprecated - only for older versions of util-linux)
- `-uB` (deprecated - only for older versions of util-linux)
Usage (cli):
# sfdisk -l | jc --sfdisk
or
# jc sfdisk -l
Usage (module):
import jc.parsers.sfdisk
result = jc.parsers.sfdisk.parse(sfdisk_command_output)
Schema:
[
{
"disk": string,
"disk_size": string,
"free_disk_size": string,
"bytes": integer,
"free_bytes": integer,
"sectors": integer,
"free_sectors": integer,
"cylinders": integer,
"heads": integer,
"sectors_per_track": integer,
"units": string,
"logical_sector_size": integer,
"physical_sector_size": integer,
"min_io_size": integer,
"optimal_io_size": integer,
"disk_label_type": string,
"disk_identifier": string,
"disk_model": string,
"partitions": [
{
"device": string,
"boot": boolean,
"start": integer,
"end": integer,
"size": string, # Note: will be integer when using deprecated -d sfdisk option
"cyls": integer,
"mib": integer,
"blocks": integer,
"sectors": integer,
"id": string,
"system": string,
"type": string
}
]
}
]
Examples:
# sfdisk -l | jc --sfdisk -p
[
{
"disk": "/dev/sda",
"cylinders": 2610,
"heads": 255,
"sectors_per_track": 63,
"units": "cylinders of 8225280 bytes, blocks of 1024 bytes, counting from 0",
"partitions": [
{
"device": "/dev/sda1",
"boot": true,
"start": 0,
"end": 130,
"cyls": 131,
"blocks": 1048576,
"id": "83",
"system": "Linux"
},
{
"device": "/dev/sda2",
"boot": false,
"start": 130,
"end": 2610,
"cyls": 2481,
"blocks": 19921920,
"id": "8e",
"system": "Linux LVM"
},
{
"device": "/dev/sda3",
"boot": false,
"start": 0,
"end": null,
"cyls": 0,
"blocks": 0,
"id": "0",
"system": "Empty"
},
{
"device": "/dev/sda4",
"boot": false,
"start": 0,
"end": null,
"cyls": 0,
"blocks": 0,
"id": "0",
"system": "Empty"
}
]
},
{
"disk": "/dev/mapper/centos-root",
"cylinders": 2218,
"heads": 255,
"sectors_per_track": 63
},
{
"disk": "/dev/mapper/centos-swap",
"cylinders": 261,
"heads": 255,
"sectors_per_track": 63
}
]
# sfdisk -l | jc --sfdisk -p -r
[
{
"disk": "/dev/sda",
"cylinders": "2610",
"heads": "255",
"sectors_per_track": "63",
"units": "cylinders of 8225280 bytes, blocks of 1024 bytes, counting from 0",
"partitions": [
{
"device": "/dev/sda1",
"boot": "*",
"start": "0+",
"end": "130-",
"cyls": "131-",
"blocks": "1048576",
"id": "83",
"system": "Linux"
},
{
"device": "/dev/sda2",
"boot": null,
"start": "130+",
"end": "2610-",
"cyls": "2481-",
"blocks": "19921920",
"id": "8e",
"system": "Linux LVM"
},
{
"device": "/dev/sda3",
"boot": null,
"start": "0",
"end": "-",
"cyls": "0",
"blocks": "0",
"id": "0",
"system": "Empty"
},
{
"device": "/dev/sda4",
"boot": null,
"start": "0",
"end": "-",
"cyls": "0",
"blocks": "0",
"id": "0",
"system": "Empty"
}
]
},
{
"disk": "/dev/mapper/centos-root",
"cylinders": "2218",
"heads": "255",
"sectors_per_track": "63"
},
{
"disk": "/dev/mapper/centos-swap",
"cylinders": "261",
"heads": "255",
"sectors_per_track": "63"
}
]
## info
```python
info()
```
Provides parser metadata (version, author, etc.)
## 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.
## Parser Information
Compatibility: linux
Version 1.1 by Kelly Brazil (kellyjonbrazil@gmail.com)

View File

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

View File

@@ -86,4 +86,4 @@ Module Example:
"""
name = 'jc'
__version__ = '1.15.5'
__version__ = '1.16.2'

View File

@@ -6,10 +6,10 @@ import sys
import os
import os.path
import re
import shlex
import importlib
import textwrap
import signal
import shlex
import subprocess
import json
import jc
@@ -94,6 +94,7 @@ parsers = [
'ps',
'route',
'rpm-qi',
'sfdisk',
'shadow',
'ss',
'stat',
@@ -130,7 +131,7 @@ local_parsers_dir = os.path.join(data_dir, 'jcparsers')
if os.path.isdir(local_parsers_dir):
sys.path.append(data_dir)
for name in os.listdir(local_parsers_dir):
if re.match(r'\w+\.py', name) and os.path.isfile(os.path.join(local_parsers_dir, name)):
if re.match(r'\w+\.py$', name) and os.path.isfile(os.path.join(local_parsers_dir, name)):
plugin_name = name[0:-3]
local_parsers.append(plugin_name)
if plugin_name not in parsers:
@@ -359,11 +360,10 @@ def help_doc(options):
# load parser module just in time so we don't need to load all modules
parser = parser_module(arg)
compatible = ', '.join(parser.info.compatible)
doc_text = f'''{parser.__doc__}
Compatibility: {compatible}
Version {parser.info.version} by {parser.info.author} ({parser.info.author_email})
'''
doc_text = \
f'{parser.__doc__}\n'\
f'Compatibility: {compatible}\n\n'\
f'Version {parser.info.version} by {parser.info.author} ({parser.info.author_email})\n'
return doc_text
@@ -414,8 +414,7 @@ def magic_parser(args):
if len(args) <= 1 or args[1].startswith('--'):
return False, None, None, []
# correctly parse escape characters and spaces with shlex
args_given = ' '.join(map(shlex.quote, args[1:])).split()
args_given = args[1:]
options = []
# find the options
@@ -465,7 +464,11 @@ def magic_parser(args):
def run_user_command(command):
"""Use subprocess to run the user's command. Returns the STDOUT, STDERR, and the Exit Code as a tuple."""
proc = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True)
proc = subprocess.Popen(command,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
close_fds=False, # Allows inheriting file descriptors. Useful for process substitution
universal_newlines=True)
stdout, stderr = proc.communicate()
return (
@@ -544,7 +547,10 @@ def main():
# if magic syntax used, try to run the command and error if it's not found, etc.
magic_stdout, magic_stderr, magic_exit_code = None, None, 0
if run_command:
run_command_str = ' '.join(run_command)
try:
run_command_str = shlex.join(run_command) # python 3.8+
except AttributeError:
run_command_str = ' '.join(run_command) # older python versions
if valid_command:
try:
@@ -559,6 +565,13 @@ def main():
jc.utils.error_message(f'"{run_command_str}" command could not be found. For details use the -d or -dd option.')
sys.exit(combined_exit_code(magic_exit_code, JC_ERROR_EXIT))
except OSError:
if debug:
raise
else:
jc.utils.error_message(f'"{run_command_str}" command could not be run due to too many open files. For details use the -d or -dd option.')
sys.exit(combined_exit_code(magic_exit_code, JC_ERROR_EXIT))
except Exception:
if debug:
raise

Binary file not shown.

View File

@@ -35,7 +35,7 @@ Examples:
# but can be preserved with the -r argument
occupation:"Engineer"
$ cat keyvalue.txt | jc --ini -p
$ cat keyvalue.txt | jc --kv -p
{
"name": "John Doe",
"address": "555 California Drive",

View File

@@ -22,7 +22,7 @@ Schema:
"genmask": string,
"flags": string,
"flags_pretty": [
string,
string
]
"metric": integer,
"ref": integer,
@@ -73,7 +73,6 @@ Examples:
}
]
$ route -ee | jc --route -p -r
[
{
@@ -103,7 +102,6 @@ Examples:
"irtt": "0"
}
]
"""
import jc.utils
import jc.parsers.universal

407
jc/parsers/sfdisk.py Normal file
View File

@@ -0,0 +1,407 @@
"""jc - JSON CLI output utility `sfdisk` command output parser
Supports the following `sfdisk` options:
- `-l`
- `-F`
- `-d` (deprecated - only for older versions of util-linux)
- `-uM` (deprecated - only for older versions of util-linux)
- `-uC` (deprecated - only for older versions of util-linux)
- `-uS` (deprecated - only for older versions of util-linux)
- `-uB` (deprecated - only for older versions of util-linux)
Usage (cli):
# sfdisk -l | jc --sfdisk
or
# jc sfdisk -l
Usage (module):
import jc.parsers.sfdisk
result = jc.parsers.sfdisk.parse(sfdisk_command_output)
Schema:
[
{
"disk": string,
"disk_size": string,
"free_disk_size": string,
"bytes": integer,
"free_bytes": integer,
"sectors": integer,
"free_sectors": integer,
"cylinders": integer,
"heads": integer,
"sectors_per_track": integer,
"units": string,
"logical_sector_size": integer,
"physical_sector_size": integer,
"min_io_size": integer,
"optimal_io_size": integer,
"disk_label_type": string,
"disk_identifier": string,
"disk_model": string,
"partitions": [
{
"device": string,
"boot": boolean,
"start": integer,
"end": integer,
"size": string, # Note: will be integer when using deprecated -d sfdisk option
"cyls": integer,
"mib": integer,
"blocks": integer,
"sectors": integer,
"id": string,
"system": string,
"type": string
}
]
}
]
Examples:
# sfdisk -l | jc --sfdisk -p
[
{
"disk": "/dev/sda",
"cylinders": 2610,
"heads": 255,
"sectors_per_track": 63,
"units": "cylinders of 8225280 bytes, blocks of 1024 bytes, counting from 0",
"partitions": [
{
"device": "/dev/sda1",
"boot": true,
"start": 0,
"end": 130,
"cyls": 131,
"blocks": 1048576,
"id": "83",
"system": "Linux"
},
{
"device": "/dev/sda2",
"boot": false,
"start": 130,
"end": 2610,
"cyls": 2481,
"blocks": 19921920,
"id": "8e",
"system": "Linux LVM"
},
{
"device": "/dev/sda3",
"boot": false,
"start": 0,
"end": null,
"cyls": 0,
"blocks": 0,
"id": "0",
"system": "Empty"
},
{
"device": "/dev/sda4",
"boot": false,
"start": 0,
"end": null,
"cyls": 0,
"blocks": 0,
"id": "0",
"system": "Empty"
}
]
},
{
"disk": "/dev/mapper/centos-root",
"cylinders": 2218,
"heads": 255,
"sectors_per_track": 63
},
{
"disk": "/dev/mapper/centos-swap",
"cylinders": 261,
"heads": 255,
"sectors_per_track": 63
}
]
# sfdisk -l | jc --sfdisk -p -r
[
{
"disk": "/dev/sda",
"cylinders": "2610",
"heads": "255",
"sectors_per_track": "63",
"units": "cylinders of 8225280 bytes, blocks of 1024 bytes, counting from 0",
"partitions": [
{
"device": "/dev/sda1",
"boot": "*",
"start": "0+",
"end": "130-",
"cyls": "131-",
"blocks": "1048576",
"id": "83",
"system": "Linux"
},
{
"device": "/dev/sda2",
"boot": null,
"start": "130+",
"end": "2610-",
"cyls": "2481-",
"blocks": "19921920",
"id": "8e",
"system": "Linux LVM"
},
{
"device": "/dev/sda3",
"boot": null,
"start": "0",
"end": "-",
"cyls": "0",
"blocks": "0",
"id": "0",
"system": "Empty"
},
{
"device": "/dev/sda4",
"boot": null,
"start": "0",
"end": "-",
"cyls": "0",
"blocks": "0",
"id": "0",
"system": "Empty"
}
]
},
{
"disk": "/dev/mapper/centos-root",
"cylinders": "2218",
"heads": "255",
"sectors_per_track": "63"
},
{
"disk": "/dev/mapper/centos-swap",
"cylinders": "261",
"heads": "255",
"sectors_per_track": "63"
}
]
"""
import jc.utils
import jc.parsers.universal
class info():
"""Provides parser metadata (version, author, etc.)"""
version = '1.1'
description = '`sfdisk` command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
compatible = ['linux']
magic_commands = ['sfdisk']
__version__ = info.version
def _process(proc_data):
"""
Final processing to conform to the schema.
Parameters:
proc_data: (List of Dictionaries) raw structured data to process
Returns:
List of Dictionaries. Structured to conform to the schema.
"""
int_list = ['cylinders', 'heads', 'sectors_per_track', 'start', 'end', 'cyls', 'mib',
'blocks', 'sectors', 'bytes', 'logical_sector_size', 'physical_sector_size',
'min_io_size', 'optimal_io_size', 'free_bytes', 'free_sectors']
bool_list = ['boot']
for entry in proc_data:
for key in entry:
if key in int_list:
entry[key] = jc.utils.convert_to_int(entry[key].replace('-', ''))
if 'partitions' in entry:
for p in entry['partitions']:
for key in p:
# legacy conversion for -d option
if key == 'size':
if p[key].isnumeric():
p[key] = jc.utils.convert_to_int(p[key])
# normal conversions
if key in int_list:
p[key] = jc.utils.convert_to_int(p[key].replace('-', ''))
if key in bool_list:
p[key] = jc.utils.convert_to_bool(p[key])
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 = []
item = {}
partitions = []
option = ''
section = ''
if jc.utils.has_data(data):
for line in data.splitlines():
# deprecated - only for older versions of util-linux
if line.startswith('# partition table of'):
if item:
raw_output.append(item)
item = {}
partitions = []
option = 'd'
item['disk'] = line.split()[4]
continue
# deprecated - only for older versions of util-linux
if option == 'd':
if line.startswith('unit: '):
item['units'] = line.split()[1]
section = 'partitions'
continue
if section == 'partitions' and line:
part = {}
part['device'] = line.split()[0]
line = line.replace(',', ' ').replace('=', ' ')
part['start'] = line.split()[3]
part['size'] = line.split()[5]
part['id'] = line.split()[7]
part['boot'] = '*' if 'bootable' in line else None
partitions.append(part)
item['partitions'] = partitions
continue
else:
# older versions of util-linux
# Disk /dev/sda: 2610 cylinders, 255 heads, 63 sectors/track
if line.startswith('Disk ') and 'sectors/track' in line:
if item:
raw_output.append(item)
item = {}
partitions = []
line = line.replace(':', '').replace(',', '')
fields = line.split()
item['disk'] = fields[1]
item['cylinders'] = fields[2]
item['heads'] = fields[4]
item['sectors_per_track'] = fields[6]
continue
# util-linux v2.32.0+ (?)
# Disk /dev/sda: 20 GiB, 21474836480 bytes, 41943040 sectors
if line.startswith('Disk ') and line.endswith('sectors'):
if item:
raw_output.append(item)
item = {}
partitions = []
line = line.replace(':', '').replace(',', '')
fields = line.split()
item['disk'] = fields[1]
item['disk_size'] = ' '.join(fields[2:4])
item['bytes'] = fields[4]
item['sectors'] = fields[6]
continue
if line.startswith('Disk model: '):
item['disk_model'] = line.split(':', maxsplit=1)[1].strip()
continue
if line.startswith('Sector size (logical/physical)'):
fields = line.split()
item['logical_sector_size'] = fields[3]
item['physical_sector_size'] = fields[6]
continue
if line.startswith('I/O size (minimum/optimal)'):
fields = line.split()
item['min_io_size'] = fields[3]
item['optimal_io_size'] = fields[6]
continue
if line.startswith('Disklabel type'):
item['disk_label_type'] = line.split(':', maxsplit=1)[1].strip()
continue
if line.startswith('Disk identifier'):
item['disk_identifier'] = line.split(':', maxsplit=1)[1].strip()
continue
if line.startswith('Units: '):
item['units'] = line.split(':')[1].strip()
continue
# sfdisk -F
if line.startswith('Unpartitioned space'):
line = line.replace(':', '').replace(',', '')
fields = line.split()
item['disk'] = fields[2]
item['free_disk_size'] = ' '.join(fields[3:5])
item['free_bytes'] = fields[5]
item['free_sectors'] = fields[7]
continue
# partition lines
if 'Start' in line and 'End' in line and ('Sectors' in line or 'Device' in line):
section = 'partitions'
partitions.append(line.lower().replace('#', ' '))
continue
if section == 'partitions' and line:
partitions.append(line)
continue
if section == 'partitions' and line == '':
item['partitions'] = jc.parsers.universal.sparse_table_parse(partitions)
section = ''
partitions = []
continue
# get final partitions if there are any left over
if section == 'partitions' and option != 'd' and partitions:
item['partitions'] = jc.parsers.universal.sparse_table_parse(partitions)
if item:
raw_output.append(item)
if raw:
return raw_output
else:
return _process(raw_output)

View File

@@ -169,7 +169,7 @@ import jc.utils
class info():
"""Provides parser metadata (version, author, etc.)"""
version = '1.8'
version = '1.9'
description = '`stat` command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
@@ -322,7 +322,7 @@ def parse(data, raw=False, quiet=False):
for line in cleandata:
value = shlex.split(line)
output_line = {
'file': value[15],
'file': ' '.join(value[15:]),
'unix_device': value[0],
'inode': value[1],
'flags': value[2],

View File

@@ -154,7 +154,7 @@ def convert_to_bool(value):
# if float converts, then bool the result
# if float does not convert then look for truthy string and bool True
# else False
truthy = ['y', 'yes', 'true']
truthy = ['y', 'yes', 'true', '*']
if isinstance(value, (int, float)):
return bool(value)

515
man/jc.1 Normal file
View File

@@ -0,0 +1,515 @@
.TH jc 1 2021-08-31 1.16.2 "JSON CLI output utility"
.SH NAME
jc \- JSONifies the output of many CLI tools and file-types
.SH SYNOPSIS
COMMAND | jc PARSER [OPTIONS]
or "Magic" syntax:
jc [OPTIONS] COMMAND
.SH DESCRIPTION
jc JSONifies the output of many CLI tools and file-types for easier parsing in scripts. jc accepts piped input from \fBSTDIN\fP and outputs a JSON representation of the previous command's output to \fBSTDOUT\fP. Alternatively, the "Magic" syntax can be used by prepending jc to the command to be converted. Options can be passed to jc immediately before the command is given. (Note: "Magic" syntax does not support shell builtins or command aliases)
.SH OPTIONS
.B
Parsers:
.RS
.TP
.B
\fB--acpi\fP
`acpi` command parser
.TP
.B
\fB--airport\fP
`airport -I` command parser
.TP
.B
\fB--airport-s\fP
`airport -s` command parser
.TP
.B
\fB--arp\fP
`arp` command parser
.TP
.B
\fB--blkid\fP
`blkid` command parser
.TP
.B
\fB--cksum\fP
`cksum` and `sum` command parser
.TP
.B
\fB--crontab\fP
`crontab` command and file parser
.TP
.B
\fB--crontab-u\fP
`crontab` file parser with user support
.TP
.B
\fB--csv\fP
CSV file parser
.TP
.B
\fB--date\fP
`date` command parser
.TP
.B
\fB--df\fP
`df` command parser
.TP
.B
\fB--dig\fP
`dig` command parser
.TP
.B
\fB--dir\fP
`dir` command parser
.TP
.B
\fB--dmidecode\fP
`dmidecode` command parser
.TP
.B
\fB--dpkg-l\fP
`dpkg -l` command parser
.TP
.B
\fB--du\fP
`du` command parser
.TP
.B
\fB--env\fP
`env` command parser
.TP
.B
\fB--file\fP
`file` command parser
.TP
.B
\fB--finger\fP
`finger` command parser
.TP
.B
\fB--free\fP
`free` command parser
.TP
.B
\fB--fstab\fP
`/etc/fstab` file parser
.TP
.B
\fB--group\fP
`/etc/group` file parser
.TP
.B
\fB--gshadow\fP
`/etc/gshadow` file parser
.TP
.B
\fB--hash\fP
`hash` command parser
.TP
.B
\fB--hashsum\fP
hashsum command parser (`md5sum`, `shasum`, etc.)
.TP
.B
\fB--hciconfig\fP
`hciconfig` command parser
.TP
.B
\fB--history\fP
`history` command parser
.TP
.B
\fB--hosts\fP
`/etc/hosts` file parser
.TP
.B
\fB--id\fP
`id` command parser
.TP
.B
\fB--ifconfig\fP
`ifconfig` command parser
.TP
.B
\fB--ini\fP
INI file parser
.TP
.B
\fB--iptables\fP
`iptables` command parser
.TP
.B
\fB--iw-scan\fP
`iw dev [device] scan` command parser
.TP
.B
\fB--jobs\fP
`jobs` command parser
.TP
.B
\fB--kv\fP
Key/Value file parser
.TP
.B
\fB--last\fP
`last` and `lastb` command parser
.TP
.B
\fB--ls\fP
`ls` command parser
.TP
.B
\fB--lsblk\fP
`lsblk` command parser
.TP
.B
\fB--lsmod\fP
`lsmod` command parser
.TP
.B
\fB--lsof\fP
`lsof` command parser
.TP
.B
\fB--mount\fP
`mount` command parser
.TP
.B
\fB--netstat\fP
`netstat` command parser
.TP
.B
\fB--ntpq\fP
`ntpq -p` command parser
.TP
.B
\fB--passwd\fP
`/etc/passwd` file parser
.TP
.B
\fB--ping\fP
`ping` and `ping6` command parser
.TP
.B
\fB--pip-list\fP
`pip list` command parser
.TP
.B
\fB--pip-show\fP
`pip show` command parser
.TP
.B
\fB--ps\fP
`ps` command parser
.TP
.B
\fB--route\fP
`route` command parser
.TP
.B
\fB--rpm-qi\fP
`rpm -qi` command parser
.TP
.B
\fB--sfdisk\fP
`sfdisk` command parser
.TP
.B
\fB--shadow\fP
`/etc/shadow` file parser
.TP
.B
\fB--ss\fP
`ss` command parser
.TP
.B
\fB--stat\fP
`stat` command parser
.TP
.B
\fB--sysctl\fP
`sysctl` command parser
.TP
.B
\fB--systemctl\fP
`systemctl` command parser
.TP
.B
\fB--systemctl-lj\fP
`systemctl list-jobs` command parser
.TP
.B
\fB--systemctl-ls\fP
`systemctl list-sockets` command parser
.TP
.B
\fB--systemctl-luf\fP
`systemctl list-unit-files` command parser
.TP
.B
\fB--systeminfo\fP
`systeminfo` command parser
.TP
.B
\fB--time\fP
`/usr/bin/time` command parser
.TP
.B
\fB--timedatectl\fP
`timedatectl status` command parser
.TP
.B
\fB--tracepath\fP
`tracepath` and `tracepath6` command parser
.TP
.B
\fB--traceroute\fP
`traceroute` and `traceroute6` command parser
.TP
.B
\fB--ufw\fP
`ufw status` command parser
.TP
.B
\fB--ufw-appinfo\fP
`ufw app info [application]` command parser
.TP
.B
\fB--uname\fP
`uname -a` command parser
.TP
.B
\fB--upower\fP
`upower` command parser
.TP
.B
\fB--uptime\fP
`uptime` command parser
.TP
.B
\fB--w\fP
`w` command parser
.TP
.B
\fB--wc\fP
`wc` command parser
.TP
.B
\fB--who\fP
`who` command parser
.TP
.B
\fB--xml\fP
XML file parser
.TP
.B
\fB--yaml\fP
YAML file parser
.RE
.PP
.B
Options:
.RS
.TP
.B
\fB-a\fP
about jc (JSON output)
.TP
.B
\fB-d\fP
debug - show traceback (\fB-dd\fP for verbose traceback)
.TP
.B
\fB-h\fP
help (\fB-h --parser_name\fP for parser documentation)
.TP
.B
\fB-m\fP
monochrome output
.TP
.B
\fB-p\fP
pretty print output
.TP
.B
\fB-q\fP
quiet - suppress warnings
.TP
.B
\fB-r\fP
raw JSON output
.TP
.B
\fB-v\fP
version information
.SH EXIT CODES
Any fatal errors within jc will generate an exit code of \fB100\fP, otherwise the exit code will be \fB0\fP. When using the "Magic" syntax (e.g. \fBjc ifconfig eth0\fP), jc will store the exit code of the program being parsed and add it to the jc exit code. This way it is easier to determine if an error was from the parsed program or jc.
Consider the following examples using `ifconfig`:
.RS
ifconfig exit code = \fB0\fP, jc exit code = \fB0\fP, combined exit code = \fB0\fP (no errors)
ifconfig exit code = \fB1\fP, jc exit code = \fB0\fP, combined exit code = \fB1\fP (error in ifconfig)
ifconfig exit code = \fB0\fP, jc exit code = \fB100\fP, combined exit code = \fB100\fP (error in jc)
ifconfig exit code = \fB1\fP, jc exit code = \fB100\fP, combined exit code = \fB101\fP (error in both ifconfig and jc)
.RE
.SH ENVIRONMENT
You can specify custom colors via the \fBJC_COLORS\fP environment variable. The \fBJC_COLORS\fP environment variable takes four comma separated string values in the following format:
JC_COLORS=<keyname_color>,<keyword_color>,<number_color>,<string_color>
Where colors are: \fBblack\fP, \fBred\fP, \fBgreen\fP, \fByellow\fP, \fBblue\fP, \fBmagenta\fP, \fBcyan\fP, \fBgray\fP, \fBbrightblack\fP, \fBbrightred\fP, \fBbrightgreen\fP, \fBbrightyellow\fP, \fBbrightblue\fP, \fBbrightmagenta\fP, \fBbrightcyan\fP, \fBwhite\fP, or \fBdefault\fP
For example, to set to the default colors:
.RS
JC_COLORS=blue,brightblack,magenta,green
or
JC_COLORS=default,default,default,default
.RE
.SH CUSTOM PARSERS
Custom local parser plugins may be placed in a \fBjc/jcparsers\fP folder in your local "App data directory":
.RS
- Linux/unix: \fB$HOME/.local/share/jc/jcparsers\fP
- macOS: \fB$HOME/Library/Application Support/jc/jcparsers\fP
- Windows: \fB$LOCALAPPDATA\\jc\\jc\\jcparsers\fP
.RE
Local parser plugins are standard python module files. Use the \fBjc/parsers/foo.py\fP parser as a template and simply place a \fB.py\fP file in the \fBjcparsers\fP subfolder.
Local plugin filenames must be valid python module names, therefore must consist entirely of alphanumerics and start with a letter. Local plugins may override default plugins.
Note: The application data directory follows the XDG Base Directory Specification
.SH CAVEATS
\fBLocale:\fP For best results set the \fBLANG\fP locale environment variable to \fBC\fP or \fBen_US.UTF-8\fP. For example, either by setting directly on the command-line:
\fB$ LANG=C date | jc --date\fP
or by exporting to the environment before running commands:
\fB$ export LANG=C\fP
\fBTimezones:\fP Some parsers have calculated epoch timestamp fields added to the output. Unless a timestamp field name has a \fB_utc\fP suffix it is considered naive. (i.e. based on the local timezone of the system the \fBjc\fP parser was run on).
If a UTC timezone can be detected in the text of the command output, the timestamp will be timezone aware and have a \fB_utc\fP suffix on the key name. (e.g. \fBepoch_utc\fP) No other timezones are supported for aware timestamps.
.SH EXAMPLES
Standard Syntax:
.RS
$ dig www.google.com | jc \fB--dig\fP \fB-p\fP
.RE
Magic Syntax:
.RS
$ jc \fB-p\fP dig www.google.com
.RE
For parser documentation:
.RS
$ jc \fB-h\fP \fB--dig\fP
.RE
.SH AUTHOR
Kelly Brazil (kellyjonbrazil@gmail.com)
https://github.com/kellyjonbrazil/jc
.SH COPYRIGHT
Copyright (c) 2019-2021 Kelly Brazil
License: MIT License

Binary file not shown.

View File

@@ -22,4 +22,4 @@ with open('man/jc.1', 'rb') as f_in:
shutil.copyfile('man/jc.1.gz', 'jc/man/jc.1.gz')
os.remove('man/jc.1')
# os.remove('man/jc.1')

View File

@@ -5,7 +5,7 @@ with open('README.md', 'r') as f:
setuptools.setup(
name='jc',
version='1.15.5',
version='1.16.2',
author='Kelly Brazil',
author_email='kellyjonbrazil@gmail.com',
description='Converts the output of popular command-line tools and file-types to JSON.',

View File

@@ -1,7 +1,7 @@
![Tests](https://github.com/kellyjonbrazil/jc/workflows/Tests/badge.svg?branch=master)
![Pypi](https://img.shields.io/pypi/v/jc.svg)
> Try the new `jc` [web demo](https://jc-web-demo.herokuapp.com/)!
> Try the `jc` [web demo](https://jc-web-demo.herokuapp.com/)
> JC is [now available](https://galaxy.ansible.com/community/general) as an Ansible filter plugin in the `community.general` collection! See this [blog post](https://blog.kellybrazil.com/2020/08/30/parsing-command-output-in-ansible-with-jc/) for an example.
@@ -19,7 +19,7 @@ dig example.com | jc --dig
39049,"data":"93.184.216.34"}],"query_time":49,"server":"2600:1700:bab0:d40::1#53(2600:1700:bab0:d40::1)","when":
"Fri Apr 16 16:09:00 PDT 2021","rcvd":56,"when_epoch":1618614540,"when_epoch_utc":null}]
```
This allows further command-line processing of output with tools like `jq` by piping commands:
This allows further command-line processing of output with tools like `jq` or [`jello`](https://github.com/kellyjonbrazil/jello) by piping commands:
```bash
$ dig example.com | jc --dig | jq -r '.[].answer[].data'
93.184.216.34
@@ -60,7 +60,7 @@ The `jc` parsers can also be used as python modules. In this case the output wil
'2600:1700:bab0:d40::1#53(2600:1700:bab0:d40::1)', 'when': 'Fri Apr 16 16:13:00 PDT 2021', 'rcvd': 56,
'when_epoch': 1618614780, 'when_epoch_utc': None}]
```
Two representations of the data are possible. The default representation uses a strict schema per parser and converts known numbers to int/float JSON values. Certain known values of `None` are converted to JSON `null`, known boolean values are converted, and, in some cases, additional semantic context fields are added.
Two representations of the data are available. The default representation uses a strict schema per parser and converts known numbers to int/float JSON values. Certain known values of `None` are converted to JSON `null`, known boolean values are converted, and, in some cases, additional semantic context fields are added.
To access the raw, pre-processed JSON, use the `-r` cli option or the `raw=True` function parameter in `parse()`.
@@ -83,7 +83,7 @@ Use Cases:
- [Nornir command output parsing](https://blog.kellybrazil.com/2020/12/09/parsing-command-output-in-nornir-with-jc/)
## Installation
There are several ways to get `jc`. You can install via `pip`; other OS package repositories like `apt-get`, `dnf`, `zypper`, `pacman`, `nix-env`, `guix`, `brew`, or `portsnap`; via DEB, RPM, and MSI packaged binaries for linux and Windows; or by downloading the correct binary for your architecture and running it anywhere on your filesystem.
There are several ways to get `jc`. You can install via `pip`, OS package repositories, via DEB/RPM/MSI packaged binaries for linux and Windows, or by downloading the correct binary for your architecture and running it anywhere on your filesystem.
### Pip (macOS, linux, unix, Windows)
```bash
@@ -100,7 +100,7 @@ pip3 install jc
| Arch linux | `pacman -S jc` |
| NixOS linux | `nix-env -iA nixpkgs.jc` or `nix-env -iA nixos.jc` |
| Guix System linux | `guix install jc` |
| MacOS | `brew install jc` |
| macOS | `brew install jc` |
| FreeBSD | `portsnap fetch update && cd /usr/ports/textproc/py-jc && make install clean` |
| Ansible filter plugin | `ansible-galaxy collection install community.general` |

View File

@@ -0,0 +1 @@
[{"disk":"/dev/sda","units":"sectors","partitions":[{"device":"/dev/sda1","start":2048,"size":2097152,"id":"83","boot":true},{"device":"/dev/sda2","start":2099200,"size":39843840,"id":"8e","boot":false},{"device":"/dev/sda3","start":0,"size":0,"id":"0","boot":false},{"device":"/dev/sda4","start":0,"size":0,"id":"0","boot":false}]},{"disk":"/dev/sdb","units":"sectors","partitions":[{"device":"/dev/sdb1","start":2048,"size":2097152,"id":"83","boot":true},{"device":"/dev/sdb2","start":2099200,"size":39843840,"id":"8e","boot":false},{"device":"/dev/sdb3","start":0,"size":0,"id":"0","boot":false},{"device":"/dev/sdb4","start":0,"size":0,"id":"0","boot":false}]}]

View File

@@ -0,0 +1,16 @@
# partition table of /dev/sda
unit: sectors
/dev/sda1 : start= 2048, size= 2097152, Id=83, bootable
/dev/sda2 : start= 2099200, size= 39843840, Id=8e
/dev/sda3 : start= 0, size= 0, Id= 0
/dev/sda4 : start= 0, size= 0, Id= 0
# partition table of /dev/sdb
unit: sectors
/dev/sdb1 : start= 2048, size= 2097152, Id=83, bootable
/dev/sdb2 : start= 2099200, size= 39843840, Id=8e
/dev/sdb3 : start= 0, size= 0, Id= 0
/dev/sdb4 : start= 0, size= 0, Id= 0

View File

@@ -0,0 +1 @@
[{"disk":"/dev/sda","units":"sectors","partitions":[{"device":"/dev/sda1","start":2048,"size":2097152,"id":"83","boot":true},{"device":"/dev/sda2","start":2099200,"size":39843840,"id":"8e","boot":false},{"device":"/dev/sda3","start":0,"size":0,"id":"0","boot":false},{"device":"/dev/sda4","start":0,"size":0,"id":"0","boot":false}]}]

View File

@@ -0,0 +1,7 @@
# partition table of /dev/sda
unit: sectors
/dev/sda1 : start= 2048, size= 2097152, Id=83, bootable
/dev/sda2 : start= 2099200, size= 39843840, Id=8e
/dev/sda3 : start= 0, size= 0, Id= 0
/dev/sda4 : start= 0, size= 0, Id= 0

View File

@@ -0,0 +1 @@
[{"disk":"/dev/sda","cylinders":2610,"heads":255,"sectors_per_track":63,"units":"cylinders of 8225280 bytes, blocks of 1024 bytes, counting from 0","partitions":[{"device":"/dev/sda1","boot":true,"start":0,"end":130,"cyls":131,"blocks":1048576,"id":"83","system":"Linux"},{"device":"/dev/sda2","boot":false,"start":130,"end":2610,"cyls":2481,"blocks":19921920,"id":"8e","system":"Linux LVM"},{"device":"/dev/sda3","boot":false,"start":0,"end":null,"cyls":0,"blocks":0,"id":"0","system":"Empty"},{"device":"/dev/sda4","boot":false,"start":0,"end":null,"cyls":0,"blocks":0,"id":"0","system":"Empty"}]},{"disk":"/dev/mapper/centos-root","cylinders":2218,"heads":255,"sectors_per_track":63},{"disk":"/dev/mapper/centos-swap","cylinders":261,"heads":255,"sectors_per_track":63,"units":"cylinders of 8225280 bytes, blocks of 1024 bytes, counting from 0","partitions":[{"device":"/dev/sda1","boot":true,"start":0,"end":130,"cyls":131,"blocks":1048576,"id":"83","system":"Linux"},{"device":"/dev/sda2","boot":false,"start":130,"end":2610,"cyls":2481,"blocks":19921920,"id":"8e","system":"Linux LVM"},{"device":"/dev/sda3","boot":false,"start":0,"end":null,"cyls":0,"blocks":0,"id":"0","system":"Empty"},{"device":"/dev/sda4","boot":false,"start":0,"end":null,"cyls":0,"blocks":0,"id":"0","system":"Empty"}]}]

View File

@@ -0,0 +1,21 @@
Disk /dev/sda: 2610 cylinders, 255 heads, 63 sectors/track
Units: cylinders of 8225280 bytes, blocks of 1024 bytes, counting from 0
Device Boot Start End #cyls #blocks Id System
/dev/sda1 * 0+ 130- 131- 1048576 83 Linux
/dev/sda2 130+ 2610- 2481- 19921920 8e Linux LVM
/dev/sda3 0 - 0 0 0 Empty
/dev/sda4 0 - 0 0 0 Empty
Disk /dev/mapper/centos-root: 2218 cylinders, 255 heads, 63 sectors/track
Disk /dev/mapper/centos-swap: 261 cylinders, 255 heads, 63 sectors/track
Units: cylinders of 8225280 bytes, blocks of 1024 bytes, counting from 0
Device Boot Start End #cyls #blocks Id System
/dev/sda1 * 0+ 130- 131- 1048576 83 Linux
/dev/sda2 130+ 2610- 2481- 19921920 8e Linux LVM
/dev/sda3 0 - 0 0 0 Empty
/dev/sda4 0 - 0 0 0 Empty

View File

@@ -0,0 +1 @@
[{"disk":"/dev/sda","cylinders":2610,"heads":255,"sectors_per_track":63,"units":"cylinders of 8225280 bytes, blocks of 1024 bytes, counting from 0","partitions":[{"device":"/dev/sda1","boot":true,"start":0,"end":130,"cyls":131,"blocks":1048576,"id":"83","system":"Linux"},{"device":"/dev/sda2","boot":false,"start":130,"end":2610,"cyls":2481,"blocks":19921920,"id":"8e","system":"Linux LVM"},{"device":"/dev/sda3","boot":false,"start":0,"end":null,"cyls":0,"blocks":0,"id":"0","system":"Empty"},{"device":"/dev/sda4","boot":false,"start":0,"end":null,"cyls":0,"blocks":0,"id":"0","system":"Empty"}]},{"disk":"/dev/mapper/centos-root","cylinders":2218,"heads":255,"sectors_per_track":63},{"disk":"/dev/mapper/centos-swap","cylinders":261,"heads":255,"sectors_per_track":63}]

14
tests/fixtures/centos-7.7/sfdisk-l.out vendored Normal file
View File

@@ -0,0 +1,14 @@
Disk /dev/sda: 2610 cylinders, 255 heads, 63 sectors/track
Units: cylinders of 8225280 bytes, blocks of 1024 bytes, counting from 0
Device Boot Start End #cyls #blocks Id System
/dev/sda1 * 0+ 130- 131- 1048576 83 Linux
/dev/sda2 130+ 2610- 2481- 19921920 8e Linux LVM
/dev/sda3 0 - 0 0 0 Empty
/dev/sda4 0 - 0 0 0 Empty
Disk /dev/mapper/centos-root: 2218 cylinders, 255 heads, 63 sectors/track
Disk /dev/mapper/centos-swap: 261 cylinders, 255 heads, 63 sectors/track

View File

@@ -0,0 +1 @@
[{"disk":"/dev/sda","cylinders":2610,"heads":255,"sectors_per_track":63,"units":"blocks of 1024 bytes, counting from 0","partitions":[{"device":"/dev/sda1","boot":true,"start":1024,"end":1049599,"blocks":1048576,"id":"83","system":"Linux"},{"device":"/dev/sda2","boot":false,"start":1049600,"end":20971519,"blocks":19921920,"id":"8e","system":"Linux LVM"},{"device":"/dev/sda3","boot":false,"start":0,"end":null,"blocks":0,"id":"0","system":"Empty"},{"device":"/dev/sda4","boot":false,"start":0,"end":null,"blocks":0,"id":"0","system":"Empty"}]},{"disk":"/dev/mapper/centos-root","cylinders":2218,"heads":255,"sectors_per_track":63},{"disk":"/dev/mapper/centos-swap","cylinders":261,"heads":255,"sectors_per_track":63}]

View File

@@ -0,0 +1,14 @@
Disk /dev/sda: 2610 cylinders, 255 heads, 63 sectors/track
Units: blocks of 1024 bytes, counting from 0
Device Boot Start End #blocks Id System
/dev/sda1 * 1024 1049599 1048576 83 Linux
/dev/sda2 1049600 20971519 19921920 8e Linux LVM
/dev/sda3 0 - 0 0 Empty
/dev/sda4 0 - 0 0 Empty
Disk /dev/mapper/centos-root: 2218 cylinders, 255 heads, 63 sectors/track
Disk /dev/mapper/centos-swap: 261 cylinders, 255 heads, 63 sectors/track

View File

@@ -0,0 +1 @@
[{"disk":"/dev/sda","cylinders":2610,"heads":255,"sectors_per_track":63,"units":"1MiB = 1024*1024 bytes, blocks of 1024 bytes, counting from 0","partitions":[{"device":"/dev/sda1","boot":true,"start":1,"end":1024,"mib":1024,"blocks":1048576,"id":"83","system":"Linux"},{"device":"/dev/sda2","boot":false,"start":1025,"end":20479,"mib":19455,"blocks":19921920,"id":"8e","system":"Linux LVM"},{"device":"/dev/sda3","boot":false,"start":0,"end":null,"mib":0,"blocks":0,"id":"0","system":"Empty"},{"device":"/dev/sda4","boot":false,"start":0,"end":null,"mib":0,"blocks":0,"id":"0","system":"Empty"}]},{"disk":"/dev/mapper/centos-root","cylinders":2218,"heads":255,"sectors_per_track":63},{"disk":"/dev/mapper/centos-swap","cylinders":261,"heads":255,"sectors_per_track":63}]

View File

@@ -0,0 +1,14 @@
Disk /dev/sda: 2610 cylinders, 255 heads, 63 sectors/track
Units: 1MiB = 1024*1024 bytes, blocks of 1024 bytes, counting from 0
Device Boot Start End MiB #blocks Id System
/dev/sda1 * 1 1024 1024 1048576 83 Linux
/dev/sda2 1025 20479 19455 19921920 8e Linux LVM
/dev/sda3 0 - 0 0 0 Empty
/dev/sda4 0 - 0 0 0 Empty
Disk /dev/mapper/centos-root: 2218 cylinders, 255 heads, 63 sectors/track
Disk /dev/mapper/centos-swap: 261 cylinders, 255 heads, 63 sectors/track

View File

@@ -0,0 +1 @@
[{"disk":"/dev/sda","cylinders":2610,"heads":255,"sectors_per_track":63,"units":"sectors of 512 bytes, counting from 0","partitions":[{"device":"/dev/sda1","boot":true,"start":2048,"end":2099199,"sectors":2097152,"id":"83","system":"Linux"},{"device":"/dev/sda2","boot":false,"start":2099200,"end":41943039,"sectors":39843840,"id":"8e","system":"Linux LVM"},{"device":"/dev/sda3","boot":false,"start":0,"end":null,"sectors":0,"id":"0","system":"Empty"},{"device":"/dev/sda4","boot":false,"start":0,"end":null,"sectors":0,"id":"0","system":"Empty"}]},{"disk":"/dev/mapper/centos-root","cylinders":2218,"heads":255,"sectors_per_track":63},{"disk":"/dev/mapper/centos-swap","cylinders":261,"heads":255,"sectors_per_track":63}]

View File

@@ -0,0 +1,14 @@
Disk /dev/sda: 2610 cylinders, 255 heads, 63 sectors/track
Units: sectors of 512 bytes, counting from 0
Device Boot Start End #sectors Id System
/dev/sda1 * 2048 2099199 2097152 83 Linux
/dev/sda2 2099200 41943039 39843840 8e Linux LVM
/dev/sda3 0 - 0 0 Empty
/dev/sda4 0 - 0 0 Empty
Disk /dev/mapper/centos-root: 2218 cylinders, 255 heads, 63 sectors/track
Disk /dev/mapper/centos-swap: 261 cylinders, 255 heads, 63 sectors/track

1
tests/fixtures/centos-8/sfdisk-F.json vendored Normal file
View File

@@ -0,0 +1 @@
[{"disk":"/dev/sda","free_disk_size":"0 B","free_bytes":0,"free_sectors":0,"units":"sectors of 1 * 512 = 512 bytes","logical_sector_size":512,"physical_sector_size":512}]

9
tests/fixtures/centos-8/sfdisk-F.out vendored Normal file
View File

@@ -0,0 +1,9 @@
Unpartitioned space /dev/sda: 0 B, 0 bytes, 0 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes

1
tests/fixtures/centos-8/sfdisk-l.json vendored Normal file
View File

@@ -0,0 +1 @@
[{"disk":"/dev/sda","disk_size":"20 GiB","bytes":21474836480,"sectors":41943040,"units":"sectors of 1 * 512 = 512 bytes","logical_sector_size":512,"physical_sector_size":512,"min_io_size":512,"optimal_io_size":512,"disk_label_type":"dos","disk_identifier":"0x94988ac4","partitions":[{"device":"/dev/sda1","boot":true,"start":2048,"end":2099199,"sectors":2097152,"size":"1G","id":"83","type":"Linux"},{"device":"/dev/sda2","boot":false,"start":2099200,"end":41943039,"sectors":39843840,"size":"19G","id":"8e","type":"Linux LVM"}]},{"disk":"/dev/mapper/cl-root","disk_size":"17 GiB","bytes":18249416704,"sectors":35643392,"units":"sectors of 1 * 512 = 512 bytes","logical_sector_size":512,"physical_sector_size":512,"min_io_size":512,"optimal_io_size":512},{"disk":"/dev/mapper/cl-swap","disk_size":"2 GiB","bytes":2147483648,"sectors":4194304,"units":"sectors of 1 * 512 = 512 bytes","logical_sector_size":512,"physical_sector_size":512,"min_io_size":512,"optimal_io_size":512}]

24
tests/fixtures/centos-8/sfdisk-l.out vendored Normal file
View File

@@ -0,0 +1,24 @@
Disk /dev/sda: 20 GiB, 21474836480 bytes, 41943040 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x94988ac4
Device Boot Start End Sectors Size Id Type
/dev/sda1 * 2048 2099199 2097152 1G 83 Linux
/dev/sda2 2099200 41943039 39843840 19G 8e Linux LVM
Disk /dev/mapper/cl-root: 17 GiB, 18249416704 bytes, 35643392 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk /dev/mapper/cl-swap: 2 GiB, 2147483648 bytes, 4194304 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes

1
tests/fixtures/debian10/sfdisk-F.json vendored Normal file
View File

@@ -0,0 +1 @@
[{"disk":"/dev/sda","free_disk_size":"0 B","free_bytes":0,"free_sectors":0,"units":"sectors of 1 * 512 = 512 bytes","logical_sector_size":512,"physical_sector_size":512}]

3
tests/fixtures/debian10/sfdisk-F.out vendored Normal file
View File

@@ -0,0 +1,3 @@
Unpartitioned space /dev/sda: 0 B, 0 bytes, 0 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes

View File

@@ -0,0 +1 @@
[{"disk":"/dev/sdc","free_disk_size":"1 GiB","free_bytes":1073741824,"free_sectors":2097152,"units":"sectors of 1 * 512 = 512 bytes","logical_sector_size":512,"physical_sector_size":512,"partitions":[{"start":4194304,"end":6291455,"sectors":2097152,"size":"1G"}]}]

6
tests/fixtures/debian10/sfdisk-F2.out vendored Normal file
View File

@@ -0,0 +1,6 @@
Unpartitioned space /dev/sdc: 1 GiB, 1073741824 bytes, 2097152 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
Start End Sectors Size
4194304 6291455 2097152 1G

1
tests/fixtures/debian10/sfdisk-l.json vendored Normal file
View File

@@ -0,0 +1 @@
[{"disk":"/dev/sda","disk_size":"20 GiB","bytes":21474836480,"sectors":41943040,"disk_model":"VMware Virtual S","units":"sectors of 1 * 512 = 512 bytes","logical_sector_size":512,"physical_sector_size":512,"min_io_size":512,"optimal_io_size":512,"disk_label_type":"dos","disk_identifier":"0x3719ef0f","partitions":[{"device":"/dev/sda1","boot":true,"start":2048,"end":39942143,"sectors":39940096,"size":"19G","id":"83","type":"Linux"},{"device":"/dev/sda2","boot":false,"start":39944190,"end":41940991,"sectors":1996802,"size":"975M","id":"5","type":"Extended"},{"device":"/dev/sda5","boot":false,"start":39944192,"end":41940991,"sectors":1996800,"size":"975M","id":"82","type":"Linux swap / Solaris"}]}]

12
tests/fixtures/debian10/sfdisk-l.out vendored Normal file
View File

@@ -0,0 +1,12 @@
Disk /dev/sda: 20 GiB, 21474836480 bytes, 41943040 sectors
Disk model: VMware Virtual S
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x3719ef0f
Device Boot Start End Sectors Size Id Type
/dev/sda1 * 2048 39942143 39940096 19G 83 Linux
/dev/sda2 39944190 41940991 1996802 975M 5 Extended
/dev/sda5 39944192 41940991 1996800 975M 82 Linux swap / Solaris

View File

@@ -0,0 +1 @@
[{"disk":"/dev/sda","disk_size":"32 GiB","bytes":34359738368,"sectors":67108864,"disk_model":"QEMU HARDDISK","units":"sectors of 1 * 512 = 512 bytes","logical_sector_size":512,"physical_sector_size":512,"min_io_size":512,"optimal_io_size":512,"disk_label_type":"dos","disk_identifier":"0x4f1ce2bf","partitions":[{"device":"/dev/sda1","boot":true,"start":2048,"end":13260799,"sectors":13258752,"size":"6.3G","id":"83","type":"Linux"},{"device":"/dev/sda2","boot":false,"start":13262846,"end":67106815,"sectors":53843970,"size":"25.7G","id":"5","type":"Extended"},{"device":"/dev/sda5","boot":false,"start":13262848,"end":18331647,"sectors":5068800,"size":"2.4G","id":"83","type":"Linux"},{"device":"/dev/sda6","boot":false,"start":18333696,"end":20332543,"sectors":1998848,"size":"976M","id":"82","type":"Linux swap / Solaris"},{"device":"/dev/sda7","boot":false,"start":20334592,"end":21352447,"sectors":1017856,"size":"497M","id":"83","type":"Linux"},{"device":"/dev/sda8","boot":false,"start":21354496,"end":67106815,"sectors":45752320,"size":"21.8G","id":"83","type":"Linux"}]}]

15
tests/fixtures/debian10/sfdisk-l2.out vendored Normal file
View File

@@ -0,0 +1,15 @@
Disk /dev/sda: 32 GiB, 34359738368 bytes, 67108864 sectors
Disk model: QEMU HARDDISK
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x4f1ce2bf
Device Boot Start End Sectors Size Id Type
/dev/sda1 * 2048 13260799 13258752 6.3G 83 Linux
/dev/sda2 13262846 67106815 53843970 25.7G 5 Extended
/dev/sda5 13262848 18331647 5068800 2.4G 83 Linux
/dev/sda6 18333696 20332543 1998848 976M 82 Linux swap / Solaris
/dev/sda7 20334592 21352447 1017856 497M 83 Linux
/dev/sda8 21354496 67106815 45752320 21.8G 83 Linux

View File

@@ -0,0 +1 @@
[{"disk":"/dev/sda","disk_size":"238.5 GiB","bytes":256060514304,"sectors":500118192,"disk_model":"NT-256","units":"sectors of 1 * 512 = 512 bytes","logical_sector_size":512,"physical_sector_size":512,"min_io_size":512,"optimal_io_size":512,"disk_label_type":"gpt","disk_identifier":"50DEF501-A333-48D1-B1FC-C2F1CCFD5715","partitions":[{"device":"/dev/sda1","start":2048,"end":526335,"sectors":524288,"size":"256M","type":"Linux root (x86)"},{"device":"/dev/sda2","start":526336,"end":138545151,"sectors":138018816,"size":"65.8G","type":"Linux root (x86)"},{"device":"/dev/sda3","start":483684352,"end":500117503,"sectors":16433152,"size":"7.9G","type":"Linux swap"},{"device":"/dev/sda4","start":138545152,"end":483684351,"sectors":345139200,"size":"164.6G","type":"Linux filesystem"}]}]

13
tests/fixtures/debian10/sfdisk-l3.out vendored Normal file
View File

@@ -0,0 +1,13 @@
Disk /dev/sda: 238.5 GiB, 256060514304 bytes, 500118192 sectors
Disk model: NT-256
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: gpt
Disk identifier: 50DEF501-A333-48D1-B1FC-C2F1CCFD5715
Device Start End Sectors Size Type
/dev/sda1 2048 526335 524288 256M Linux root (x86)
/dev/sda2 526336 138545151 138018816 65.8G Linux root (x86)
/dev/sda3 483684352 500117503 16433152 7.9G Linux swap
/dev/sda4 138545152 483684351 345139200 164.6G Linux filesystem

View File

@@ -0,0 +1 @@
[{"file":"file name with spaces.txt","unix_device":16777220,"inode":161929661,"flags":"-rw-r--r--","links":1,"user":"kbrazil","group":"staff","rdev":0,"size":0,"access_time":"Aug 13 15:03:52 2021","modify_time":"Aug 13 14:37:03 2021","change_time":"Aug 13 14:37:03 2021","birth_time":"Aug 13 14:37:03 2021","block_size":4096,"blocks":0,"unix_flags":"0","access_time_epoch":1628892232,"access_time_epoch_utc":null,"modify_time_epoch":1628890623,"modify_time_epoch_utc":null,"change_time_epoch":1628890623,"change_time_epoch_utc":null,"birth_time_epoch":1628890623,"birth_time_epoch_utc":null}]

View File

@@ -0,0 +1,2 @@
16777220 161929661 -rw-r--r-- 1 kbrazil staff 0 0 "Aug 13 15:03:52 2021" "Aug 13 14:37:03 2021" "Aug 13 14:37:03 2021" "Aug 13 14:37:03 2021" 4096 0 0 file name with spaces.txt

View File

@@ -1,10 +1,17 @@
import os
import sys
import time
import json
import unittest
import jc.parsers.date
THIS_DIR = os.path.dirname(os.path.abspath(__file__))
# Set the timezone on POSIX systems. Need to manually set for Windows tests
if not sys.platform.startswith('win32'):
os.environ['TZ'] = 'America/Los_Angeles'
time.tzset()
class MyTests(unittest.TestCase):

View File

@@ -1,10 +1,17 @@
import os
import sys
import time
import json
import unittest
import jc.parsers.dig
THIS_DIR = os.path.dirname(os.path.abspath(__file__))
# Set the timezone on POSIX systems. Need to manually set for Windows tests
if not sys.platform.startswith('win32'):
os.environ['TZ'] = 'America/Los_Angeles'
time.tzset()
class MyTests(unittest.TestCase):

View File

@@ -1,10 +1,17 @@
import os
import sys
import time
import json
import unittest
import jc.parsers.dir
THIS_DIR = os.path.dirname(os.path.abspath(__file__))
# Set the timezone on POSIX systems. Need to manually set for Windows tests
if not sys.platform.startswith('win32'):
os.environ['TZ'] = 'America/Los_Angeles'
time.tzset()
class MyTests(unittest.TestCase):

View File

@@ -1,10 +1,17 @@
import os
import sys
import time
import json
import unittest
import jc.parsers.last
THIS_DIR = os.path.dirname(os.path.abspath(__file__))
# Set the timezone on POSIX systems. Need to manually set for Windows tests
if not sys.platform.startswith('win32'):
os.environ['TZ'] = 'America/Los_Angeles'
time.tzset()
class MyTests(unittest.TestCase):

View File

@@ -1,10 +1,17 @@
import os
import sys
import time
import json
import unittest
import jc.parsers.ls
THIS_DIR = os.path.dirname(os.path.abspath(__file__))
# Set the timezone on POSIX systems. Need to manually set for Windows tests
if not sys.platform.startswith('win32'):
os.environ['TZ'] = 'America/Los_Angeles'
time.tzset()
class MyTests(unittest.TestCase):

View File

@@ -1,10 +1,17 @@
import os
import sys
import time
import unittest
import json
import jc.parsers.rpm_qi
THIS_DIR = os.path.dirname(os.path.abspath(__file__))
# Set the timezone on POSIX systems. Need to manually set for Windows tests
if not sys.platform.startswith('win32'):
os.environ['TZ'] = 'America/Los_Angeles'
time.tzset()
class MyTests(unittest.TestCase):

192
tests/test_sfdisk.py Normal file
View File

@@ -0,0 +1,192 @@
import os
import json
import unittest
import jc.parsers.sfdisk
THIS_DIR = os.path.dirname(os.path.abspath(__file__))
class MyTests(unittest.TestCase):
def setUp(self):
# input
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/centos-7.7/sfdisk-l.out'), 'r', encoding='utf-8') as f:
self.centos_7_7_sfdisk_l = f.read()
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/centos-7.7/sfdisk-l-multi.out'), 'r', encoding='utf-8') as f:
self.centos_7_7_sfdisk_l_multi = f.read()
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/centos-7.7/sfdisk-d.out'), 'r', encoding='utf-8') as f:
self.centos_7_7_sfdisk_d = f.read()
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/centos-7.7/sfdisk-d-multi.out'), 'r', encoding='utf-8') as f:
self.centos_7_7_sfdisk_d_multi = f.read()
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/centos-7.7/sfdisk-luB.out'), 'r', encoding='utf-8') as f:
self.centos_7_7_sfdisk_luB = f.read()
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/centos-7.7/sfdisk-luM.out'), 'r', encoding='utf-8') as f:
self.centos_7_7_sfdisk_luM = f.read()
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/centos-7.7/sfdisk-luS.out'), 'r', encoding='utf-8') as f:
self.centos_7_7_sfdisk_luS = f.read()
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/centos-8/sfdisk-l.out'), 'r', encoding='utf-8') as f:
self.centos_8_sfdisk_l = f.read()
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/centos-8/sfdisk-F.out'), 'r', encoding='utf-8') as f:
self.centos_8_sfdisk_F = f.read()
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/debian10/sfdisk-l.out'), 'r', encoding='utf-8') as f:
self.debian_10_sfdisk_l = f.read()
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/debian10/sfdisk-l2.out'), 'r', encoding='utf-8') as f:
self.debian_10_sfdisk_l2 = f.read()
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/debian10/sfdisk-l3.out'), 'r', encoding='utf-8') as f:
self.debian_10_sfdisk_l3 = f.read()
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/debian10/sfdisk-F.out'), 'r', encoding='utf-8') as f:
self.debian_10_sfdisk_F = f.read()
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/debian10/sfdisk-F2.out'), 'r', encoding='utf-8') as f:
self.debian_10_sfdisk_F2 = f.read()
# output
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/centos-7.7/sfdisk-l.json'), 'r', encoding='utf-8') as f:
self.centos_7_7_sfdisk_l_json = json.loads(f.read())
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/centos-7.7/sfdisk-l-multi.json'), 'r', encoding='utf-8') as f:
self.centos_7_7_sfdisk_l_multi_json = json.loads(f.read())
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/centos-7.7/sfdisk-d.json'), 'r', encoding='utf-8') as f:
self.centos_7_7_sfdisk_d_json = json.loads(f.read())
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/centos-7.7/sfdisk-d-multi.json'), 'r', encoding='utf-8') as f:
self.centos_7_7_sfdisk_d_multi_json = json.loads(f.read())
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/centos-7.7/sfdisk-luB.json'), 'r', encoding='utf-8') as f:
self.centos_7_7_sfdisk_luB_json = json.loads(f.read())
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/centos-7.7/sfdisk-luM.json'), 'r', encoding='utf-8') as f:
self.centos_7_7_sfdisk_luM_json = json.loads(f.read())
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/centos-7.7/sfdisk-luS.json'), 'r', encoding='utf-8') as f:
self.centos_7_7_sfdisk_luS_json = json.loads(f.read())
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/centos-8/sfdisk-l.json'), 'r', encoding='utf-8') as f:
self.centos_8_sfdisk_l_json = json.loads(f.read())
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/centos-8/sfdisk-F.json'), 'r', encoding='utf-8') as f:
self.centos_8_sfdisk_F_json = json.loads(f.read())
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/debian10/sfdisk-l.json'), 'r', encoding='utf-8') as f:
self.debian_10_sfdisk_l_json = json.loads(f.read())
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/debian10/sfdisk-l2.json'), 'r', encoding='utf-8') as f:
self.debian_10_sfdisk_l2_json = json.loads(f.read())
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/debian10/sfdisk-l3.json'), 'r', encoding='utf-8') as f:
self.debian_10_sfdisk_l3_json = json.loads(f.read())
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/debian10/sfdisk-F.json'), 'r', encoding='utf-8') as f:
self.debian_10_sfdisk_F_json = json.loads(f.read())
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/debian10/sfdisk-F2.json'), 'r', encoding='utf-8') as f:
self.debian_10_sfdisk_F2_json = json.loads(f.read())
def test_sfdisk_nodata(self):
"""
Test 'sfdisk' with no data
"""
self.assertEqual(jc.parsers.sfdisk.parse('', quiet=True), [])
def test_sfdisk_l_centos_7_7(self):
"""
Test 'sfdisk -l' on Centos 7.7
"""
self.assertEqual(jc.parsers.sfdisk.parse(self.centos_7_7_sfdisk_l, quiet=True), self.centos_7_7_sfdisk_l_json)
def test_sfdisk_l_multi_centos_7_7(self):
"""
Test 'sfdisk -l' with multiple disk data on Centos 7.7
"""
self.assertEqual(jc.parsers.sfdisk.parse(self.centos_7_7_sfdisk_l_multi, quiet=True), self.centos_7_7_sfdisk_l_multi_json)
def test_sfdisk_d_centos_7_7(self):
"""
Test 'sfdisk -d' on Centos 7.7
"""
self.assertEqual(jc.parsers.sfdisk.parse(self.centos_7_7_sfdisk_d, quiet=True), self.centos_7_7_sfdisk_d_json)
def test_sfdisk_d_multi_centos_7_7(self):
"""
Test 'sfdisk -d' with multiple disk data on Centos 7.7
"""
self.assertEqual(jc.parsers.sfdisk.parse(self.centos_7_7_sfdisk_d_multi, quiet=True), self.centos_7_7_sfdisk_d_multi_json)
def test_sfdisk_luB_centos_7_7(self):
"""
Test 'sfdisk -luB' on Centos 7.7
"""
self.assertEqual(jc.parsers.sfdisk.parse(self.centos_7_7_sfdisk_luB, quiet=True), self.centos_7_7_sfdisk_luB_json)
def test_sfdisk_luM_centos_7_7(self):
"""
Test 'sfdisk -luM' on Centos 7.7
"""
self.assertEqual(jc.parsers.sfdisk.parse(self.centos_7_7_sfdisk_luM, quiet=True), self.centos_7_7_sfdisk_luM_json)
def test_sfdisk_luS_centos_7_7(self):
"""
Test 'sfdisk -luS' on Centos 7.7
"""
self.assertEqual(jc.parsers.sfdisk.parse(self.centos_7_7_sfdisk_luS, quiet=True), self.centos_7_7_sfdisk_luS_json)
def test_sfdisk_l_centos_8(self):
"""
Test 'sfdisk -l' on Centos 8
"""
self.assertEqual(jc.parsers.sfdisk.parse(self.centos_8_sfdisk_l, quiet=True), self.centos_8_sfdisk_l_json)
def test_sfdisk_F_centos_8(self):
"""
Test 'sfdisk -F' on Centos 8
"""
self.assertEqual(jc.parsers.sfdisk.parse(self.centos_8_sfdisk_F, quiet=True), self.centos_8_sfdisk_F_json)
def test_sfdisk_l_debian_10(self):
"""
Test 'sfdisk -l' on Debian 10
"""
self.assertEqual(jc.parsers.sfdisk.parse(self.debian_10_sfdisk_l, quiet=True), self.debian_10_sfdisk_l_json)
def test_sfdisk_l2_debian_10(self):
"""
Test 'sfdisk -l' on Debian 10 (second example)
"""
self.assertEqual(jc.parsers.sfdisk.parse(self.debian_10_sfdisk_l2, quiet=True), self.debian_10_sfdisk_l2_json)
def test_sfdisk_l3_debian_10(self):
"""
Test 'sfdisk -l' on Debian 10 (third example)
"""
self.assertEqual(jc.parsers.sfdisk.parse(self.debian_10_sfdisk_l3, quiet=True), self.debian_10_sfdisk_l3_json)
def test_sfdisk_F_debian10(self):
"""
Test 'sfdisk -F' on Debian 10
"""
self.assertEqual(jc.parsers.sfdisk.parse(self.debian_10_sfdisk_F, quiet=True), self.debian_10_sfdisk_F_json)
def test_sfdisk_F2_debian10(self):
"""
Test 'sfdisk -F' on Debian 10 (second example)
"""
self.assertEqual(jc.parsers.sfdisk.parse(self.debian_10_sfdisk_F2, quiet=True), self.debian_10_sfdisk_F2_json)
if __name__ == '__main__':
unittest.main()

View File

@@ -1,10 +1,17 @@
import os
import sys
import time
import json
import unittest
import jc.parsers.stat
THIS_DIR = os.path.dirname(os.path.abspath(__file__))
# Set the timezone on POSIX systems. Need to manually set for Windows tests
if not sys.platform.startswith('win32'):
os.environ['TZ'] = 'America/Los_Angeles'
time.tzset()
class MyTests(unittest.TestCase):
@@ -19,6 +26,9 @@ class MyTests(unittest.TestCase):
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/osx-10.14.6/stat.out'), 'r', encoding='utf-8') as f:
self.osx_10_14_6_stat = f.read()
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/osx-10.14.6/stat-filename-with-spaces.out'), 'r', encoding='utf-8') as f:
self.osx_10_14_6_stat_filename_with_spaces = f.read()
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/freebsd12/stat.out'), 'r', encoding='utf-8') as f:
self.freebsd12_stat = f.read()
@@ -32,6 +42,9 @@ class MyTests(unittest.TestCase):
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/osx-10.14.6/stat.json'), 'r', encoding='utf-8') as f:
self.osx_10_14_6_stat_json = json.loads(f.read())
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/osx-10.14.6/stat-filename-with-spaces.json'), 'r', encoding='utf-8') as f:
self.osx_10_14_6_stat_filename_with_spaces_json = json.loads(f.read())
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/freebsd12/stat.json'), 'r', encoding='utf-8') as f:
self.freebsd12_stat_json = json.loads(f.read())
@@ -59,6 +72,12 @@ class MyTests(unittest.TestCase):
"""
self.assertEqual(jc.parsers.stat.parse(self.osx_10_14_6_stat, quiet=True), self.osx_10_14_6_stat_json)
def test_stat_filename_with_spaces_osx_10_14_6(self):
"""
Test 'stat' filename with spaces on OSX 10.14.6
"""
self.assertEqual(jc.parsers.stat.parse(self.osx_10_14_6_stat_filename_with_spaces, quiet=True), self.osx_10_14_6_stat_filename_with_spaces_json)
def test_stat_freebsd12(self):
"""
Test 'stat /foo/*' on FreeBSD12

View File

@@ -1,10 +1,17 @@
import json
import os
import sys
import time
import unittest
import jc.parsers.systeminfo
THIS_DIR = os.path.dirname(os.path.abspath(__file__))
# Set the timezone on POSIX systems. Need to manually set for Windows tests
if not sys.platform.startswith('win32'):
os.environ['TZ'] = 'America/Los_Angeles'
time.tzset()
class MyTests(unittest.TestCase):
test_files = [

View File

@@ -1,10 +1,17 @@
import os
import sys
import time
import json
import unittest
import jc.parsers.upower
THIS_DIR = os.path.dirname(os.path.abspath(__file__))
# Set the timezone on POSIX systems. Need to manually set for Windows tests
if not sys.platform.startswith('win32'):
os.environ['TZ'] = 'America/Los_Angeles'
time.tzset()
class MyTests(unittest.TestCase):

View File

@@ -1,6 +1,14 @@
import os
import sys
import time
import unittest
import jc.utils
# Set the timezone on POSIX systems. Need to manually set for Windows tests
if not sys.platform.startswith('win32'):
os.environ['TZ'] = 'America/Los_Angeles'
time.tzset()
class MyTests(unittest.TestCase):

View File

@@ -1,10 +1,17 @@
import os
import sys
import time
import json
import unittest
import jc.parsers.who
THIS_DIR = os.path.dirname(os.path.abspath(__file__))
# Set the timezone on POSIX systems. Need to manually set for Windows tests
if not sys.platform.startswith('win32'):
os.environ['TZ'] = 'America/Los_Angeles'
time.tzset()
class MyTests(unittest.TestCase):