1
0
mirror of https://github.com/kellyjonbrazil/jc.git synced 2025-07-13 01:20:24 +02:00

Merge pull request #29 from kellyjonbrazil/dev

Dev v1.7.5
This commit is contained in:
Kelly Brazil
2020-02-27 10:59:14 -08:00
committed by GitHub
32 changed files with 682 additions and 44 deletions

View File

@ -1,7 +1,7 @@
# JC # JC
JSON CLI output utility JSON CLI output utility
`jc` is used to JSONify the output of many standard linux cli tools and file types for easier parsing in scripts. See the **Parsers** section for supported commands. `jc` is used to JSONify the output of many standard linux cli tools and file types for easier parsing in scripts. See the [**Parsers**](#parsers) section for supported commands and file types.
This allows further command line processing of output with tools like `jq` simply by piping commands: This allows further command line processing of output with tools like `jq` simply by piping commands:
``` ```
@ -29,7 +29,7 @@ $ jc ls -l /usr/bin | jq '.[] | select(.size > 50000000)'
"date": "Aug 14 19:41" "date": "Aug 14 19:41"
} }
``` ```
The `jc` parsers can also be used as python modules. In this case the output will be a python dictionary instead of JSON: The `jc` parsers can also be used as python modules. In this case the output will be a python dictionary, or list of dictionaries, instead of JSON:
``` ```
>>> import jc.parsers.ls >>> import jc.parsers.ls
>>> >>>
@ -129,6 +129,32 @@ The JSON output can be compact (default) or pretty formatted with the `-p` optio
- `-q` quiet mode. Suppresses warning messages - `-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 text and no additional sematic processing
## 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.
## Compatibility
Some parsers like `ls`, `ps`, `dig`, etc. will work on any platform. Other parsers that are platform-specific will generate a warning message if they are used on an unsupported platform. To see all parser information, including compatibility, run `jc -ap`.
You may still use a parser on an unsupported platform - for example, you may want to parse a file with linux `lsof` output on an OSX laptop. In that case you can suppress the warning message with the `-q` cli option or the `quiet=True` function parameter in `parse()`:
```
$ cat lsof.out | jc --lsof -q
```
Tested on:
- Centos 7.7
- Ubuntu 18.4
- OSX 10.11.6
- OSX 10.14.6
## Acknowledgments
- `ifconfig-parser` module from https://github.com/KnightWhoSayNi/ifconfig-parser
- `xmltodict` module from https://github.com/martinblech/xmltodict by Martín Blech
- `ruamel.yaml` library from https://pypi.org/project/ruamel.yaml by Anthon van der Neut
- Parsing code from Conor Heine at https://gist.github.com/cahna/43a1a3ff4d075bcd71f9d7120037a501 adapted for some parsers
- Excellent constructive feedback from Ilya Sher (https://github.com/ilyash-b)
## Examples ## Examples
### arp ### arp
``` ```
@ -1867,34 +1893,4 @@ $ cat istio.yaml | jc --yaml -p
} }
} }
] ]
``` ```
## TODO
Future parsers:
- /proc files
- /sys files
## Contributions
Feel free to add/improve code or parsers! You can use the `jc/parsers/foo.py` parser as a template and submit your parser with a pull request.
## Compatibility
Some parsers like `ls`, `ps`, `dig`, etc. will work on any platform. Other parsers that are platform-specific will generate a warning message if they are used on an unsupported platform. To see all parser information, including compatibility, run `jc -ap`.
You may still use a parser on an unsupported platform - for example, you may want to parse a file with linux `lsof` output on an OSX laptop. In that case you can suppress the warning message with the `-q` cli option or the `quiet=True` function parameter in `parse()`:
```
$ cat lsof.out | jc --lsof -q
```
Tested on:
- Centos 7.7
- Ubuntu 18.4
- OSX 10.11.6
- OSX 10.14.6
## Acknowledgments
- `ifconfig-parser` module from https://github.com/KnightWhoSayNi/ifconfig-parser
- `xmltodict` module from https://github.com/martinblech/xmltodict by Martín Blech
- `ruamel.yaml` library from https://pypi.org/project/ruamel.yaml by Anthon van der Neut
- Parsing code from Conor Heine at https://gist.github.com/cahna/43a1a3ff4d075bcd71f9d7120037a501 adapted for some parsers
- Excellent constructive feedback from Ilya Sher (https://github.com/ilyash-b)

View File

@ -1,5 +1,8 @@
jc changelog jc changelog
20200227 v1.7.5
- Updated ls parser to support filenames with newline characters
20200219 v1.7.4 20200219 v1.7.4
- Updated ls parser to support multiple directories, globbing, and -R (recursive) - Updated ls parser to support multiple directories, globbing, and -R (recursive)

View File

@ -1,12 +1,15 @@
# jc.parsers.ls # jc.parsers.ls
jc - JSON CLI output utility ls Parser jc - JSON CLI output utility ls Parser
Note: The -l option of ls should be used to correctly parse filenames that include newline characters.
Since ls does not encode newlines in filenames when outputting to a pipe it will cause jc to see
multiple files instead of a single file if -l is not used.
Usage: Usage:
specify --ls as the first argument if the piped input is coming from ls specify --ls as the first argument if the piped input is coming from ls
ls options supported: ls options supported:
- None
- laR - laR
--time-style=full-iso --time-style=full-iso
- h file sizes will be available in text form with -r but larger file sizes - h file sizes will be available in text form with -r but larger file sizes

View File

@ -13,7 +13,7 @@ import jc.utils
class info(): class info():
version = '1.7.4' version = '1.7.5'
description = 'jc cli output JSON conversion tool' description = 'jc cli output JSON conversion tool'
author = 'Kelly Brazil' author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com' author_email = 'kellyjonbrazil@gmail.com'

View File

@ -1,11 +1,14 @@
"""jc - JSON CLI output utility ls Parser """jc - JSON CLI output utility ls Parser
Note: The -l option of ls should be used to correctly parse filenames that include newline characters.
Since ls does not encode newlines in filenames when outputting to a pipe it will cause jc to see
multiple files instead of a single file if -l is not used.
Usage: Usage:
specify --ls as the first argument if the piped input is coming from ls specify --ls as the first argument if the piped input is coming from ls
ls options supported: ls options supported:
- None
- laR - laR
--time-style=full-iso --time-style=full-iso
- h file sizes will be available in text form with -r but larger file sizes - h file sizes will be available in text form with -r but larger file sizes
@ -145,7 +148,7 @@ import jc.utils
class info(): class info():
version = '1.1' version = '1.2'
description = 'ls command parser' description = 'ls command parser'
author = 'Kelly Brazil' author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com' author_email = 'kellyjonbrazil@gmail.com'
@ -215,6 +218,10 @@ def parse(data, raw=False, quiet=False):
jc.utils.compatibility(__name__, info.compatible) jc.utils.compatibility(__name__, info.compatible)
raw_output = [] raw_output = []
warned = False
parent = ''
next_is_parent = False
new_section = False
linedata = data.splitlines() linedata = data.splitlines()
@ -223,9 +230,6 @@ def parse(data, raw=False, quiet=False):
if re.match('^total [0-9]+', linedata[0]): if re.match('^total [0-9]+', linedata[0]):
linedata.pop(0) linedata.pop(0)
parent = ''
next_is_parent = False
# Look for parent line if glob or -R is used # Look for parent line if glob or -R is used
if not re.match('^[-dclpsbDCMnP?]([-r][-w][-xsS]){2}([-r][-w][-xtT])[+]?', linedata[0]) \ if not re.match('^[-dclpsbDCMnP?]([-r][-w][-xsS]){2}([-r][-w][-xtT])[+]?', linedata[0]) \
and linedata[0].endswith(':'): and linedata[0].endswith(':'):
@ -244,16 +248,28 @@ def parse(data, raw=False, quiet=False):
if not re.match('^[-dclpsbDCMnP?]([-r][-w][-xsS]){2}([-r][-w][-xtT])[+]?', entry) \ if not re.match('^[-dclpsbDCMnP?]([-r][-w][-xsS]){2}([-r][-w][-xtT])[+]?', entry) \
and entry.endswith(':'): and entry.endswith(':'):
parent = entry[:-1] parent = entry[:-1]
new_section = True
# fixup to remove trailing \n in previous entry
raw_output[-1]['filename'] = raw_output[-1]['filename'][:-1]
continue continue
if re.match('^total [0-9]+', entry): if re.match('^total [0-9]+', entry):
new_section = False
continue continue
if entry == '': # fixup for filenames with newlines
if not new_section \
and not re.match('^[-dclpsbDCMnP?]([-r][-w][-xsS]){2}([-r][-w][-xtT])[+]?', entry):
raw_output[-1]['filename'] = raw_output[-1]['filename'] + '\n' + entry
continue continue
# split filenames and links # split filenames and links
filename_field = parsed_line[8].split(' -> ') if len(parsed_line) == 9:
filename_field = parsed_line[8].split(' -> ')
else:
# in case of filenames starting with a newline character
filename_field = ['']
# create list of dictionaries # create list of dictionaries
output_line['filename'] = filename_field[0] output_line['filename'] = filename_field[0]
@ -279,11 +295,15 @@ def parse(data, raw=False, quiet=False):
next_is_parent = True next_is_parent = True
continue continue
if next_is_parent: if next_is_parent and entry.endswith(':'):
parent = entry[:-1] parent = entry[:-1]
next_is_parent = False next_is_parent = False
continue continue
if not quiet and next_is_parent and not entry.endswith(':') and not warned:
jc.utils.warning_message('Newline characters detected. Filenames probably corrupted. Use ls -l instead.')
warned = True
output_line['filename'] = entry output_line['filename'] = entry
if parent: if parent:

View File

@ -5,7 +5,7 @@ with open('README.md', 'r') as f:
setuptools.setup( setuptools.setup(
name='jc', name='jc',
version='1.7.4', version='1.7.5',
author='Kelly Brazil', author='Kelly Brazil',
author_email='kellyjonbrazil@gmail.com', author_email='kellyjonbrazil@gmail.com',
description='This tool serializes the output of popular command line tools and filetypes to structured JSON output.', description='This tool serializes the output of popular command line tools and filetypes to structured JSON output.',

View File

@ -0,0 +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"}]

View File

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

View File

@ -0,0 +1 @@
[{"filename": "a regular filename", "flags": "-rw-rw-r--.", "links": 1, "owner": "kbrazil", "group": "kbrazil", "size": 0, "date": "Feb 22 00:12"}, {"filename": "\n\nthis file has\na combination\n\n\nof everything\n\n\n\n", "flags": "-rw-rw-r--.", "links": 1, "owner": "kbrazil", "group": "kbrazil", "size": 0, "date": "Feb 22 00:17"}, {"filename": "this file has\na newline inside", "flags": "-rw-rw-r--.", "links": 1, "owner": "kbrazil", "group": "kbrazil", "size": 0, "date": "Feb 22 00:14"}, {"filename": "this file has\n\n\n\nfour contiguous newlines inside", "flags": "-rw-rw-r--.", "links": 1, "owner": "kbrazil", "group": "kbrazil", "size": 0, "date": "Feb 22 00:14"}, {"filename": "this file\nhas\nsix\n\nnewlines\n\nwithin", "flags": "-rw-rw-r--.", "links": 1, "owner": "kbrazil", "group": "kbrazil", "size": 0, "date": "Feb 22 00:16"}, {"filename": "\n\n\n\nthis file starts with four newlines", "flags": "-rw-rw-r--.", "links": 1, "owner": "kbrazil", "group": "kbrazil", "size": 0, "date": "Feb 22 00:40"}, {"filename": "\nthis file starts with one newline", "flags": "-rw-rw-r--.", "links": 1, "owner": "kbrazil", "group": "kbrazil", "size": 0, "date": "Feb 22 00:13"}]

View File

@ -0,0 +1,34 @@
total 0
-rw-rw-r--. 1 kbrazil kbrazil 0 Feb 22 00:12 a regular filename
-rw-rw-r--. 1 kbrazil kbrazil 0 Feb 22 00:17
this file has
a combination
of everything
-rw-rw-r--. 1 kbrazil kbrazil 0 Feb 22 00:14 this file has
a newline inside
-rw-rw-r--. 1 kbrazil kbrazil 0 Feb 22 00:14 this file has
four contiguous newlines inside
-rw-rw-r--. 1 kbrazil kbrazil 0 Feb 22 00:16 this file
has
six
newlines
within
-rw-rw-r--. 1 kbrazil kbrazil 0 Feb 22 00:40
this file starts with four newlines
-rw-rw-r--. 1 kbrazil kbrazil 0 Feb 22 00:13
this file starts with one newline

View File

@ -0,0 +1 @@
[{"filename": "lstest", "parent": ".", "flags": "drwxrwxr-x.", "links": 2, "owner": "kbrazil", "group": "kbrazil", "size": 4096, "date": "Feb 22 00:40"}, {"filename": "systemd-private-016de60725a3426792b93fc9f120b8f0-chronyd.service-oZqq4u", "parent": ".", "flags": "drwx------.", "links": 3, "owner": "root", "group": "root", "size": 17, "date": "Feb 13 16:44"}, {"filename": "systemd-private-a30a5a178daa4042b42dfaf5ff9e5f68-chronyd.service-a1tpxv", "parent": ".", "flags": "drwx------.", "links": 3, "owner": "root", "group": "root", "size": 17, "date": "Feb 17 17:48"}, {"filename": "systemd-private-af69d7360f3e40cfa947358c0fb5a6f8-chronyd.service-T3MQ4j", "parent": ".", "flags": "drwx------.", "links": 3, "owner": "root", "group": "root", "size": 17, "date": "Feb 20 14:41"}, {"filename": "tmp.CvALl2jE6u", "parent": ".", "flags": "drwx------.", "links": 2, "owner": "root", "group": "root", "size": 6, "date": "Feb 13 16:44"}, {"filename": "tmp.e7AlxSxY5a", "parent": ".", "flags": "drwx------.", "links": 2, "owner": "root", "group": "root", "size": 6, "date": "Feb 20 14:41"}, {"filename": "tmp.uXm9yegjwj", "parent": ".", "flags": "drwx------.", "links": 2, "owner": "root", "group": "root", "size": 6, "date": "Feb 17 17:48"}, {"filename": "a regular filename", "parent": "./lstest", "flags": "-rw-rw-r--.", "links": 1, "owner": "kbrazil", "group": "kbrazil", "size": 0, "date": "Feb 22 00:12"}, {"filename": "\n\nthis file has\na combination\n\n\nof everything\n\n\n\n", "parent": "./lstest", "flags": "-rw-rw-r--.", "links": 1, "owner": "kbrazil", "group": "kbrazil", "size": 0, "date": "Feb 22 00:17"}, {"filename": "this file has\na newline inside", "parent": "./lstest", "flags": "-rw-rw-r--.", "links": 1, "owner": "kbrazil", "group": "kbrazil", "size": 0, "date": "Feb 22 00:14"}, {"filename": "this file has\n\n\n\nfour contiguous newlines inside", "parent": "./lstest", "flags": "-rw-rw-r--.", "links": 1, "owner": "kbrazil", "group": "kbrazil", "size": 0, "date": "Feb 22 00:14"}, {"filename": "this file\nhas\nsix\n\nnewlines\n\nwithin", "parent": "./lstest", "flags": "-rw-rw-r--.", "links": 1, "owner": "kbrazil", "group": "kbrazil", "size": 0, "date": "Feb 22 00:16"}, {"filename": "\n\n\n\nthis file starts with four newlines", "parent": "./lstest", "flags": "-rw-rw-r--.", "links": 1, "owner": "kbrazil", "group": "kbrazil", "size": 0, "date": "Feb 22 00:40"}, {"filename": "\nthis file starts with one newline", "parent": "./lstest", "flags": "-rw-rw-r--.", "links": 1, "owner": "kbrazil", "group": "kbrazil", "size": 0, "date": "Feb 22 00:13"}]

View File

@ -0,0 +1,45 @@
.:
total 4
drwxrwxr-x. 2 kbrazil kbrazil 4096 Feb 22 00:40 lstest
drwx------. 3 root root 17 Feb 13 16:44 systemd-private-016de60725a3426792b93fc9f120b8f0-chronyd.service-oZqq4u
drwx------. 3 root root 17 Feb 17 17:48 systemd-private-a30a5a178daa4042b42dfaf5ff9e5f68-chronyd.service-a1tpxv
drwx------. 3 root root 17 Feb 20 14:41 systemd-private-af69d7360f3e40cfa947358c0fb5a6f8-chronyd.service-T3MQ4j
drwx------. 2 root root 6 Feb 13 16:44 tmp.CvALl2jE6u
drwx------. 2 root root 6 Feb 20 14:41 tmp.e7AlxSxY5a
drwx------. 2 root root 6 Feb 17 17:48 tmp.uXm9yegjwj
./lstest:
total 0
-rw-rw-r--. 1 kbrazil kbrazil 0 Feb 22 00:12 a regular filename
-rw-rw-r--. 1 kbrazil kbrazil 0 Feb 22 00:17
this file has
a combination
of everything
-rw-rw-r--. 1 kbrazil kbrazil 0 Feb 22 00:14 this file has
a newline inside
-rw-rw-r--. 1 kbrazil kbrazil 0 Feb 22 00:14 this file has
four contiguous newlines inside
-rw-rw-r--. 1 kbrazil kbrazil 0 Feb 22 00:16 this file
has
six
newlines
within
-rw-rw-r--. 1 kbrazil kbrazil 0 Feb 22 00:40
this file starts with four newlines
-rw-rw-r--. 1 kbrazil kbrazil 0 Feb 22 00:13
this file starts with one newline

View File

@ -0,0 +1 @@
[{"filename": "a regular filename"}, {"filename": "this file has"}, {"filename": "a combination"}, {"filename": "of everything"}, {"filename": "this file has"}, {"filename": "a newline inside"}, {"filename": "this file has"}, {"filename": "four contiguous newlines inside"}, {"filename": "this file"}, {"filename": "has"}, {"filename": "six"}, {"filename": "newlines"}, {"filename": "within"}, {"filename": "this file starts with four newlines"}, {"filename": "this file starts with one newline"}]

View File

@ -0,0 +1,33 @@
a regular filename
this file has
a combination
of everything
this file has
a newline inside
this file has
four contiguous newlines inside
this file
has
six
newlines
within
this file starts with four newlines
this file starts with one newline

View File

@ -40,6 +40,21 @@ ls -R /usr > ls-R.out
ls -alR /usr > ls-alR.out ls -alR /usr > ls-alR.out
ls /usr/* > ls-glob.out ls /usr/* > ls-glob.out
cd /tmp/lstest
touch 'a regular filename'
touch $'\nthis file starts with one newline'
touch $'\n\n\n\nthis file starts with four newlines'
touch $'this file has\na newline inside'
touch $'this file has\n\n\n\nfour contiguous newlines inside'
touch $'this file\nhas\nsix\n\nnewlines\n\nwithin'
touch $'\n\nthis file has\na combination\n\n\nof everything\n\n\n\n'
cd /tmp
ls -R > ~/utils/ls-R-newlines.out
ls -lR > ~/utils/ls-lR-newlines.out
cd lstest
ls > ~/utils/ls-newlines.out
ls -l > ~/utils/ls-l-newlines.out
lsblk > lsblk.out lsblk > lsblk.out
lsblk -o +KNAME,FSTYPE,LABEL,UUID,PARTLABEL,PARTUUID,RA,MODEL,SERIAL,STATE,OWNER,GROUP,MODE,ALIGNMENT,MIN-IO,OPT-IO,PHY-SEC,LOG-SEC,ROTA,SCHED,RQ-SIZE,DISC-ALN,DISC-GRAN,DISC-MAX,DISC-ZERO,WSAME,WWN,RAND,PKNAME,HCTL,TRAN,REV,VENDOR > lsblk-allcols.out lsblk -o +KNAME,FSTYPE,LABEL,UUID,PARTLABEL,PARTUUID,RA,MODEL,SERIAL,STATE,OWNER,GROUP,MODE,ALIGNMENT,MIN-IO,OPT-IO,PHY-SEC,LOG-SEC,ROTA,SCHED,RQ-SIZE,DISC-ALN,DISC-GRAN,DISC-MAX,DISC-ZERO,WSAME,WWN,RAND,PKNAME,HCTL,TRAN,REV,VENDOR > lsblk-allcols.out
lsmod > lsmod.out lsmod > lsmod.out

View File

@ -0,0 +1 @@
[{"filename": "lstest"}, {"filename": "this file starts with four newlines", "parent": "./lstest"}, {"filename": "this file has", "parent": "./lstest"}, {"filename": "a combination", "parent": "./lstest"}, {"filename": "of everything", "parent": "./lstest"}, {"filename": "this file starts with one newline", "parent": "./lstest"}, {"filename": "a regular filename", "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 has", "parent": "./lstest"}, {"filename": "four contiguous newlines inside", "parent": "./lstest"}, {"filename": "this file has", "parent": "./lstest"}, {"filename": "a newline inside", "parent": "./lstest"}]

View File

@ -0,0 +1,36 @@
lstest
./lstest:
this file starts with four newlines
this file has
a combination
of everything
this file starts with one newline
a regular filename
this file
has
six
newlines
within
this file has
four contiguous newlines inside
this file has
a newline inside

View File

@ -0,0 +1 @@
[{"filename": "\n\n\n\nthis file starts with four newlines", "flags": "-rw-r--r--", "links": 1, "owner": "kbrazil", "group": "staff", "size": 0, "date": "Feb 27 10:19"}, {"filename": "\n\nthis file has\na combination\n\n\nof everything\n\n\n\n", "flags": "-rw-r--r--", "links": 1, "owner": "kbrazil", "group": "staff", "size": 0, "date": "Feb 27 10:19"}, {"filename": "\nthis file starts with one newline", "flags": "-rw-r--r--", "links": 1, "owner": "kbrazil", "group": "staff", "size": 0, "date": "Feb 27 10:19"}, {"filename": "a regular filename", "flags": "-rw-r--r--", "links": 1, "owner": "kbrazil", "group": "staff", "size": 0, "date": "Feb 27 10:19"}, {"filename": "this file\nhas\nsix\n\nnewlines\n\nwithin", "flags": "-rw-r--r--", "links": 1, "owner": "kbrazil", "group": "staff", "size": 0, "date": "Feb 27 10:19"}, {"filename": "this file has\n\n\n\nfour contiguous newlines inside", "flags": "-rw-r--r--", "links": 1, "owner": "kbrazil", "group": "staff", "size": 0, "date": "Feb 27 10:19"}, {"filename": "this file has\na newline inside", "flags": "-rw-r--r--", "links": 1, "owner": "kbrazil", "group": "staff", "size": 0, "date": "Feb 27 10:19"}]

View File

@ -0,0 +1,34 @@
total 0
-rw-r--r-- 1 kbrazil staff 0 Feb 27 10:19
this file starts with four newlines
-rw-r--r-- 1 kbrazil staff 0 Feb 27 10:19
this file has
a combination
of everything
-rw-r--r-- 1 kbrazil staff 0 Feb 27 10:19
this file starts with one newline
-rw-r--r-- 1 kbrazil staff 0 Feb 27 10:19 a regular filename
-rw-r--r-- 1 kbrazil staff 0 Feb 27 10:19 this file
has
six
newlines
within
-rw-r--r-- 1 kbrazil staff 0 Feb 27 10:19 this file has
four contiguous newlines inside
-rw-r--r-- 1 kbrazil staff 0 Feb 27 10:19 this file has
a newline inside

View File

@ -0,0 +1 @@
[{"filename": "lstest", "flags": "drwxr-xr-x", "links": 9, "owner": "kbrazil", "group": "staff", "size": 288, "date": "Feb 27 10:19"}, {"filename": "\n\n\n\nthis file starts with four newlines", "parent": "./lstest", "flags": "-rw-r--r--", "links": 1, "owner": "kbrazil", "group": "staff", "size": 0, "date": "Feb 27 10:19"}, {"filename": "\n\nthis file has\na combination\n\n\nof everything\n\n\n\n", "parent": "./lstest", "flags": "-rw-r--r--", "links": 1, "owner": "kbrazil", "group": "staff", "size": 0, "date": "Feb 27 10:19"}, {"filename": "\nthis file starts with one newline", "parent": "./lstest", "flags": "-rw-r--r--", "links": 1, "owner": "kbrazil", "group": "staff", "size": 0, "date": "Feb 27 10:19"}, {"filename": "a regular filename", "parent": "./lstest", "flags": "-rw-r--r--", "links": 1, "owner": "kbrazil", "group": "staff", "size": 0, "date": "Feb 27 10:19"}, {"filename": "this file\nhas\nsix\n\nnewlines\n\nwithin", "parent": "./lstest", "flags": "-rw-r--r--", "links": 1, "owner": "kbrazil", "group": "staff", "size": 0, "date": "Feb 27 10:19"}, {"filename": "this file has\n\n\n\nfour contiguous newlines inside", "parent": "./lstest", "flags": "-rw-r--r--", "links": 1, "owner": "kbrazil", "group": "staff", "size": 0, "date": "Feb 27 10:19"}, {"filename": "this file has\na newline inside", "parent": "./lstest", "flags": "-rw-r--r--", "links": 1, "owner": "kbrazil", "group": "staff", "size": 0, "date": "Feb 27 10:19"}]

View File

@ -0,0 +1,38 @@
total 0
drwxr-xr-x 9 kbrazil staff 288 Feb 27 10:19 lstest
./lstest:
total 0
-rw-r--r-- 1 kbrazil staff 0 Feb 27 10:19
this file starts with four newlines
-rw-r--r-- 1 kbrazil staff 0 Feb 27 10:19
this file has
a combination
of everything
-rw-r--r-- 1 kbrazil staff 0 Feb 27 10:19
this file starts with one newline
-rw-r--r-- 1 kbrazil staff 0 Feb 27 10:19 a regular filename
-rw-r--r-- 1 kbrazil staff 0 Feb 27 10:19 this file
has
six
newlines
within
-rw-r--r-- 1 kbrazil staff 0 Feb 27 10:19 this file has
four contiguous newlines inside
-rw-r--r-- 1 kbrazil staff 0 Feb 27 10:19 this file has
a newline inside

View File

@ -0,0 +1 @@
[{"filename": "this file starts with four newlines"}, {"filename": "this file has"}, {"filename": "a combination"}, {"filename": "of everything"}, {"filename": "this file starts with one newline"}, {"filename": "a regular filename"}, {"filename": "this file"}, {"filename": "has"}, {"filename": "six"}, {"filename": "newlines"}, {"filename": "within"}, {"filename": "this file has"}, {"filename": "four contiguous newlines inside"}, {"filename": "this file has"}, {"filename": "a newline inside"}]

View File

@ -0,0 +1,33 @@
this file starts with four newlines
this file has
a combination
of everything
this file starts with one newline
a regular filename
this file
has
six
newlines
within
this file has
four contiguous newlines inside
this file has
a newline inside

View File

@ -0,0 +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"}]

View File

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

View File

@ -0,0 +1 @@
[{"filename": "a regular filename", "flags": "-rw-rw-r--", "links": 1, "owner": "kbrazil", "group": "kbrazil", "size": 0, "date": "Feb 27 18:08"}, {"filename": "\n\nthis file has\na combination\n\n\nof everything\n\n\n\n", "flags": "-rw-rw-r--", "links": 1, "owner": "kbrazil", "group": "kbrazil", "size": 0, "date": "Feb 27 18:08"}, {"filename": "this file has\na newline inside", "flags": "-rw-rw-r--", "links": 1, "owner": "kbrazil", "group": "kbrazil", "size": 0, "date": "Feb 27 18:08"}, {"filename": "this file has\n\n\n\nfour contiguous newlines inside", "flags": "-rw-rw-r--", "links": 1, "owner": "kbrazil", "group": "kbrazil", "size": 0, "date": "Feb 27 18:08"}, {"filename": "this file\nhas\nsix\n\nnewlines\n\nwithin", "flags": "-rw-rw-r--", "links": 1, "owner": "kbrazil", "group": "kbrazil", "size": 0, "date": "Feb 27 18:08"}, {"filename": "\n\n\n\nthis file starts with four newlines", "flags": "-rw-rw-r--", "links": 1, "owner": "kbrazil", "group": "kbrazil", "size": 0, "date": "Feb 27 18:08"}, {"filename": "\nthis file starts with one newline", "flags": "-rw-rw-r--", "links": 1, "owner": "kbrazil", "group": "kbrazil", "size": 0, "date": "Feb 27 18:08"}]

View File

@ -0,0 +1,34 @@
total 0
-rw-rw-r-- 1 kbrazil kbrazil 0 Feb 27 18:08 a regular filename
-rw-rw-r-- 1 kbrazil kbrazil 0 Feb 27 18:08
this file has
a combination
of everything
-rw-rw-r-- 1 kbrazil kbrazil 0 Feb 27 18:08 this file has
a newline inside
-rw-rw-r-- 1 kbrazil kbrazil 0 Feb 27 18:08 this file has
four contiguous newlines inside
-rw-rw-r-- 1 kbrazil kbrazil 0 Feb 27 18:08 this file
has
six
newlines
within
-rw-rw-r-- 1 kbrazil kbrazil 0 Feb 27 18:08
this file starts with four newlines
-rw-rw-r-- 1 kbrazil kbrazil 0 Feb 27 18:08
this file starts with one newline

View File

@ -0,0 +1 @@
[{"filename": "lstest", "parent": ".", "flags": "drwxrwxr-x", "links": 2, "owner": "kbrazil", "group": "kbrazil", "size": 4096, "date": "Feb 27 18:08"}, {"filename": "systemd-private-65c1089f1d4c4cf5bc50ca55478abfde-systemd-resolved.service-La9AqY", "parent": ".", "flags": "drwx------", "links": 3, "owner": "root", "group": "root", "size": 4096, "date": "Feb 27 18:07"}, {"filename": "systemd-private-65c1089f1d4c4cf5bc50ca55478abfde-systemd-timesyncd.service-L7q4cJ", "parent": ".", "flags": "drwx------", "links": 3, "owner": "root", "group": "root", "size": 4096, "date": "Feb 27 18:07"}, {"filename": "vmware-root_670-2722828838", "parent": ".", "flags": "drwx------", "links": 2, "owner": "root", "group": "root", "size": 4096, "date": "Feb 27 18:07"}, {"filename": "a regular filename", "parent": "./lstest", "flags": "-rw-rw-r--", "links": 1, "owner": "kbrazil", "group": "kbrazil", "size": 0, "date": "Feb 27 18:08"}, {"filename": "\n\nthis file has\na combination\n\n\nof everything\n\n\n\n", "parent": "./lstest", "flags": "-rw-rw-r--", "links": 1, "owner": "kbrazil", "group": "kbrazil", "size": 0, "date": "Feb 27 18:08"}, {"filename": "this file has\na newline inside", "parent": "./lstest", "flags": "-rw-rw-r--", "links": 1, "owner": "kbrazil", "group": "kbrazil", "size": 0, "date": "Feb 27 18:08"}, {"filename": "this file has\n\n\n\nfour contiguous newlines inside", "parent": "./lstest", "flags": "-rw-rw-r--", "links": 1, "owner": "kbrazil", "group": "kbrazil", "size": 0, "date": "Feb 27 18:08"}, {"filename": "this file\nhas\nsix\n\nnewlines\n\nwithin", "parent": "./lstest", "flags": "-rw-rw-r--", "links": 1, "owner": "kbrazil", "group": "kbrazil", "size": 0, "date": "Feb 27 18:08"}, {"filename": "\n\n\n\nthis file starts with four newlines", "parent": "./lstest", "flags": "-rw-rw-r--", "links": 1, "owner": "kbrazil", "group": "kbrazil", "size": 0, "date": "Feb 27 18:08"}, {"filename": "\nthis file starts with one newline", "parent": "./lstest", "flags": "-rw-rw-r--", "links": 1, "owner": "kbrazil", "group": "kbrazil", "size": 0, "date": "Feb 27 18:08"}]

View File

@ -0,0 +1,42 @@
.:
total 16
drwxrwxr-x 2 kbrazil kbrazil 4096 Feb 27 18:08 lstest
drwx------ 3 root root 4096 Feb 27 18:07 systemd-private-65c1089f1d4c4cf5bc50ca55478abfde-systemd-resolved.service-La9AqY
drwx------ 3 root root 4096 Feb 27 18:07 systemd-private-65c1089f1d4c4cf5bc50ca55478abfde-systemd-timesyncd.service-L7q4cJ
drwx------ 2 root root 4096 Feb 27 18:07 vmware-root_670-2722828838
./lstest:
total 0
-rw-rw-r-- 1 kbrazil kbrazil 0 Feb 27 18:08 a regular filename
-rw-rw-r-- 1 kbrazil kbrazil 0 Feb 27 18:08
this file has
a combination
of everything
-rw-rw-r-- 1 kbrazil kbrazil 0 Feb 27 18:08 this file has
a newline inside
-rw-rw-r-- 1 kbrazil kbrazil 0 Feb 27 18:08 this file has
four contiguous newlines inside
-rw-rw-r-- 1 kbrazil kbrazil 0 Feb 27 18:08 this file
has
six
newlines
within
-rw-rw-r-- 1 kbrazil kbrazil 0 Feb 27 18:08
this file starts with four newlines
-rw-rw-r-- 1 kbrazil kbrazil 0 Feb 27 18:08
this file starts with one newline

View File

@ -0,0 +1 @@
[{"filename": "a regular filename"}, {"filename": "this file has"}, {"filename": "a combination"}, {"filename": "of everything"}, {"filename": "this file has"}, {"filename": "a newline inside"}, {"filename": "this file has"}, {"filename": "four contiguous newlines inside"}, {"filename": "this file"}, {"filename": "has"}, {"filename": "six"}, {"filename": "newlines"}, {"filename": "within"}, {"filename": "this file starts with four newlines"}, {"filename": "this file starts with one newline"}]

View File

@ -0,0 +1,33 @@
a regular filename
this file has
a combination
of everything
this file has
a newline inside
this file has
four contiguous newlines inside
this file
has
six
newlines
within
this file starts with four newlines
this file starts with one newline

View File

@ -73,6 +73,42 @@ class MyTests(unittest.TestCase):
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/osx-10.14.6/ls-glob.out'), 'r') as f: with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/osx-10.14.6/ls-glob.out'), 'r') as f:
self.osx_10_14_6_ls_glob = f.read() self.osx_10_14_6_ls_glob = f.read()
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/centos-7.7/ls-R-newlines.out'), 'r') as f:
self.centos_7_7_ls_R_newlines = f.read()
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/ubuntu-18.04/ls-R-newlines.out'), 'r') as f:
self.ubuntu_18_4_ls_R_newlines = f.read()
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/osx-10.14.6/ls-R-newlines.out'), 'r') as f:
self.osx_10_14_6_ls_R_newlines = f.read()
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/centos-7.7/ls-l-newlines.out'), 'r') as f:
self.centos_7_7_ls_l_newlines = f.read()
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/ubuntu-18.04/ls-l-newlines.out'), 'r') as f:
self.ubuntu_18_4_ls_l_newlines = f.read()
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/osx-10.14.6/ls-l-newlines.out'), 'r') as f:
self.osx_10_14_6_ls_l_newlines = f.read()
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/centos-7.7/ls-lR-newlines.out'), 'r') as f:
self.centos_7_7_ls_lR_newlines = f.read()
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/ubuntu-18.04/ls-lR-newlines.out'), 'r') as f:
self.ubuntu_18_4_ls_lR_newlines = f.read()
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/osx-10.14.6/ls-lR-newlines.out'), 'r') as f:
self.osx_10_14_6_ls_lR_newlines = f.read()
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/centos-7.7/ls-newlines.out'), 'r') as f:
self.centos_7_7_ls_newlines = f.read()
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/ubuntu-18.04/ls-newlines.out'), 'r') as f:
self.ubuntu_18_4_ls_newlines = f.read()
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/osx-10.14.6/ls-newlines.out'), 'r') as f:
self.osx_10_14_6_ls_newlines = f.read()
# output # output
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/centos-7.7/ls.json'), 'r') as f: with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/centos-7.7/ls.json'), 'r') as f:
self.centos_7_7_ls_json = json.loads(f.read()) self.centos_7_7_ls_json = json.loads(f.read())
@ -137,6 +173,42 @@ class MyTests(unittest.TestCase):
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/osx-10.14.6/ls-glob.json'), 'r') as f: with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/osx-10.14.6/ls-glob.json'), 'r') as f:
self.osx_10_14_6_ls_glob_json = json.loads(f.read()) self.osx_10_14_6_ls_glob_json = json.loads(f.read())
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/centos-7.7/ls-R-newlines.json'), 'r') as f:
self.centos_7_7_ls_R_newlines_json = json.loads(f.read())
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/ubuntu-18.04/ls-R-newlines.json'), 'r') as f:
self.ubuntu_18_4_ls_R_newlines_json = json.loads(f.read())
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/osx-10.14.6/ls-R-newlines.json'), 'r') as f:
self.osx_10_14_6_ls_R_newlines_json = json.loads(f.read())
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/centos-7.7/ls-l-newlines.json'), 'r') as f:
self.centos_7_7_ls_l_newlines_json = json.loads(f.read())
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/ubuntu-18.04/ls-l-newlines.json'), 'r') as f:
self.ubuntu_18_4_ls_l_newlines_json = json.loads(f.read())
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/osx-10.14.6/ls-l-newlines.json'), 'r') as f:
self.osx_10_14_6_ls_l_newlines_json = json.loads(f.read())
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/centos-7.7/ls-lR-newlines.json'), 'r') as f:
self.centos_7_7_ls_lR_newlines_json = json.loads(f.read())
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/ubuntu-18.04/ls-lR-newlines.json'), 'r') as f:
self.ubuntu_18_4_ls_lR_newlines_json = json.loads(f.read())
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/osx-10.14.6/ls-lR-newlines.json'), 'r') as f:
self.osx_10_14_6_ls_lR_newlines_json = json.loads(f.read())
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/centos-7.7/ls-newlines.json'), 'r') as f:
self.centos_7_7_ls_newlines_json = json.loads(f.read())
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/ubuntu-18.04/ls-newlines.json'), 'r') as f:
self.ubuntu_18_4_ls_newlines_json = json.loads(f.read())
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/osx-10.14.6/ls-newlines.json'), 'r') as f:
self.osx_10_14_6_ls_newlines_json = json.loads(f.read())
def test_ls_centos_7_7(self): def test_ls_centos_7_7(self):
""" """
Test plain 'ls /' on Centos 7.7 Test plain 'ls /' on Centos 7.7
@ -263,6 +335,78 @@ class MyTests(unittest.TestCase):
""" """
self.assertEqual(jc.parsers.ls.parse(self.osx_10_14_6_ls_glob, quiet=True), self.osx_10_14_6_ls_glob_json) self.assertEqual(jc.parsers.ls.parse(self.osx_10_14_6_ls_glob, quiet=True), self.osx_10_14_6_ls_glob_json)
def test_ls_R_newlines_centos_7_7(self):
"""
Test 'ls -R' for filenames with newline characters on Centos 7.7
"""
self.assertEqual(jc.parsers.ls.parse(self.centos_7_7_ls_R_newlines, quiet=True), self.centos_7_7_ls_R_newlines_json)
def test_ls_R_newlines_ubuntu_18_4(self):
"""
Test 'ls -R' for filenames with newline characters on Ubuntu 18.4
"""
self.assertEqual(jc.parsers.ls.parse(self.ubuntu_18_4_ls_R_newlines, quiet=True), self.ubuntu_18_4_ls_R_newlines_json)
def test_ls_R_newlines_osx_10_14_6(self):
"""
Test 'ls -R' for filenames with newline characters on OSX 10.14.6
"""
self.assertEqual(jc.parsers.ls.parse(self.osx_10_14_6_ls_R_newlines, quiet=True), self.osx_10_14_6_ls_R_newlines_json)
def test_ls_l_newlines_centos_7_7(self):
"""
Test 'ls -l' for filenames with newline characters on Centos 7.7
"""
self.assertEqual(jc.parsers.ls.parse(self.centos_7_7_ls_l_newlines, quiet=True), self.centos_7_7_ls_l_newlines_json)
def test_ls_l_newlines_ubuntu_18_4(self):
"""
Test 'ls -l' for filenames with newline characters on Ubuntu 18.4
"""
self.assertEqual(jc.parsers.ls.parse(self.ubuntu_18_4_ls_l_newlines, quiet=True), self.ubuntu_18_4_ls_l_newlines_json)
def test_ls_l_newlines_osx_10_14_6(self):
"""
Test 'ls -l' for filenames with newline characters on OSX 10.14.6
"""
self.assertEqual(jc.parsers.ls.parse(self.osx_10_14_6_ls_l_newlines, quiet=True), self.osx_10_14_6_ls_l_newlines_json)
def test_ls_lR_newlines_centos_7_7(self):
"""
Test 'ls -lR' for filenames with newline characters on Centos 7.7
"""
self.assertEqual(jc.parsers.ls.parse(self.centos_7_7_ls_lR_newlines, quiet=True), self.centos_7_7_ls_lR_newlines_json)
def test_ls_lR_newlines_ubuntu_18_4(self):
"""
Test 'ls -lR' for filenames with newline characters on Ubuntu 18.4
"""
self.assertEqual(jc.parsers.ls.parse(self.ubuntu_18_4_ls_lR_newlines, quiet=True), self.ubuntu_18_4_ls_lR_newlines_json)
def test_ls_lR_newlines_osx_10_14_6(self):
"""
Test 'ls -lR' for filenames with newline characters on OSX 10.14.6
"""
self.assertEqual(jc.parsers.ls.parse(self.osx_10_14_6_ls_lR_newlines, quiet=True), self.osx_10_14_6_ls_lR_newlines_json)
def test_ls_newlines_centos_7_7(self):
"""
Test 'ls' for filenames with newline characters on Centos 7.7
"""
self.assertEqual(jc.parsers.ls.parse(self.centos_7_7_ls_newlines, quiet=True), self.centos_7_7_ls_newlines_json)
def test_ls_newlines_ubuntu_18_4(self):
"""
Test 'ls' for filenames with newline characters on Ubuntu 18.4
"""
self.assertEqual(jc.parsers.ls.parse(self.ubuntu_18_4_ls_newlines, quiet=True), self.ubuntu_18_4_ls_newlines_json)
def test_ls_newlines_osx_10_14_6(self):
"""
Test 'ls' for filenames with newline characters on OSX 10.14.6
"""
self.assertEqual(jc.parsers.ls.parse(self.osx_10_14_6_ls_newlines, quiet=True), self.osx_10_14_6_ls_newlines_json)
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()