diff --git a/CHANGELOG b/CHANGELOG index 8b805bd8..1a8b8c1b 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,10 +1,11 @@ jc changelog -20231001 v1.23.5 +20231002 v1.23.5 - Add `host` command parser - Add `nsd-control` command parser - Add `lsb_release` command parser - Add `/etc/os-release` file parser +- Enhance `env` command parser to support multi-line values - Enhance `ping` and `ping-s` parsers to add error and corrupted support - Enhance `xml` parser to include comments in the JSON output - Fix `pidstat` command parser when using `-T ALL` diff --git a/docs/parsers/env.md b/docs/parsers/env.md index 02f406b6..4aa19317 100644 --- a/docs/parsers/env.md +++ b/docs/parsers/env.md @@ -90,10 +90,10 @@ Parameters: Returns: - Dictionary of raw structured data or - List of Dictionaries of processed structured data + Dictionary of raw structured data or (default) + List of Dictionaries of processed structured data (raw) ### Parser Information Compatibility: linux, darwin, cygwin, win32, aix, freebsd -Version 1.4 by Kelly Brazil (kellyjonbrazil@gmail.com) +Version 1.5 by Kelly Brazil (kellyjonbrazil@gmail.com) diff --git a/jc/parsers/env.py b/jc/parsers/env.py index 1d4bb68a..4f2280c8 100644 --- a/jc/parsers/env.py +++ b/jc/parsers/env.py @@ -67,12 +67,13 @@ Examples: "_": "/usr/bin/env" } """ +import re import jc.utils class info(): """Provides parser metadata (version, author, etc.)""" - version = '1.4' + version = '1.5' description = '`env` command parser' author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' @@ -83,6 +84,7 @@ class info(): __version__ = info.version +VAR_DEF_PATTERN = re.compile(r'^[a-zA-Z_][a-zA-Z0-9_]*=\S*.*$') def _process(proc_data): """ @@ -96,8 +98,6 @@ def _process(proc_data): List of Dictionaries. Structured data to conform to the schema. """ - - # rebuild output for added semantic information processed = [] for k, v in proc_data.items(): proc_line = {} @@ -120,24 +120,29 @@ def parse(data, raw=False, quiet=False): Returns: - Dictionary of raw structured data or - List of Dictionaries of processed structured data + Dictionary of raw structured data or (default) + List of Dictionaries of processed structured data (raw) """ jc.utils.compatibility(__name__, info.compatible, quiet) jc.utils.input_type_check(data) raw_output = {} - - # Clear any blank lines - cleandata = list(filter(None, data.splitlines())) + key = '' + value = None if jc.utils.has_data(data): + for line in data.splitlines(): + if VAR_DEF_PATTERN.match(line): + if not value is None: + raw_output[key] = value + key, value = line.split('=', maxsplit=1) + continue - for entry in cleandata: - parsed_line = entry.split('=', maxsplit=1) - raw_output[parsed_line[0]] = parsed_line[1] + if not value is None: + value = value + '\n' + line + + if not value is None: + raw_output[key] = value + + return raw_output if raw else _process(raw_output) - if raw: - return raw_output - else: - return _process(raw_output) diff --git a/tests/fixtures/generic/env-multiline-raw.json b/tests/fixtures/generic/env-multiline-raw.json new file mode 100644 index 00000000..9d311a66 --- /dev/null +++ b/tests/fixtures/generic/env-multiline-raw.json @@ -0,0 +1 @@ +{"TERM_PROGRAM":"Apple_Terminal","SHELL":"/bin/zsh","TERM":"xterm-256color","TMPDIR":"/var/folders/8g/r6tdh9kj6z35x9rfmgtt3x6r0000gn/T/","TERM_PROGRAM_VERSION":"447","TERM_SESSION_ID":"A7F0FFB1-C12A-42EA-A8D9-0757BB2D4DDE","USER":"kelly","SSH_AUTH_SOCK":"/private/tmp/com.apple.launchd.6IQJQijqVl/Listeners","PATH":"/Users/kelly/.pyenv/shims:/Users/kelly/.gem/ruby/2.6.0/bin:/Users/kelly/.local/bin:/Users/kelly/.cargo/bin:/usr/local/bin:/System/Cryptexes/App/usr/bin:/usr/bin:/bin:/usr/sbin:/sbin:/Library/Apple/usr/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/local/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/appleinternal/bin","LaunchInstanceID":"3D00D30B-5C8B-421F-958D-91B196167506","__CFBundleIdentifier":"com.apple.Terminal","PWD":"/Users/kelly/temp","XPC_FLAGS":"0x0","XPC_SERVICE_NAME":"0","SHLVL":"1","HOME":"/Users/kelly","LOGNAME":"kelly","SECURITYSESSIONID":"186a3","OLDPWD":"/Users/kelly/git/jc/tests/fixtures/ubuntu-22.04","JC_COLORS":"cyan,default,default,default","JELLO_COLORS":"cyan,default,default,default","PYENV_ROOT":"/Users/kelly/.pyenv","PYENV_SHELL":"zsh","MYVAR":"hello world","hello":"world","KELLYVAR":"This is a multiline\nvariable that has var=1 a definition\ninside of it","LANG":"en_US.UTF-8","_":"/usr/bin/env"} diff --git a/tests/fixtures/generic/env-multiline.json b/tests/fixtures/generic/env-multiline.json new file mode 100644 index 00000000..5abf6fe3 --- /dev/null +++ b/tests/fixtures/generic/env-multiline.json @@ -0,0 +1 @@ +[{"name":"TERM_PROGRAM","value":"Apple_Terminal"},{"name":"SHELL","value":"/bin/zsh"},{"name":"TERM","value":"xterm-256color"},{"name":"TMPDIR","value":"/var/folders/8g/r6tdh9kj6z35x9rfmgtt3x6r0000gn/T/"},{"name":"TERM_PROGRAM_VERSION","value":"447"},{"name":"TERM_SESSION_ID","value":"A7F0FFB1-C12A-42EA-A8D9-0757BB2D4DDE"},{"name":"USER","value":"kelly"},{"name":"SSH_AUTH_SOCK","value":"/private/tmp/com.apple.launchd.6IQJQijqVl/Listeners"},{"name":"PATH","value":"/Users/kelly/.pyenv/shims:/Users/kelly/.gem/ruby/2.6.0/bin:/Users/kelly/.local/bin:/Users/kelly/.cargo/bin:/usr/local/bin:/System/Cryptexes/App/usr/bin:/usr/bin:/bin:/usr/sbin:/sbin:/Library/Apple/usr/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/local/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/appleinternal/bin"},{"name":"LaunchInstanceID","value":"3D00D30B-5C8B-421F-958D-91B196167506"},{"name":"__CFBundleIdentifier","value":"com.apple.Terminal"},{"name":"PWD","value":"/Users/kelly/temp"},{"name":"XPC_FLAGS","value":"0x0"},{"name":"XPC_SERVICE_NAME","value":"0"},{"name":"SHLVL","value":"1"},{"name":"HOME","value":"/Users/kelly"},{"name":"LOGNAME","value":"kelly"},{"name":"SECURITYSESSIONID","value":"186a3"},{"name":"OLDPWD","value":"/Users/kelly/git/jc/tests/fixtures/ubuntu-22.04"},{"name":"JC_COLORS","value":"cyan,default,default,default"},{"name":"JELLO_COLORS","value":"cyan,default,default,default"},{"name":"PYENV_ROOT","value":"/Users/kelly/.pyenv"},{"name":"PYENV_SHELL","value":"zsh"},{"name":"MYVAR","value":"hello world"},{"name":"hello","value":"world"},{"name":"KELLYVAR","value":"This is a multiline\nvariable that has var=1 a definition\ninside of it"},{"name":"LANG","value":"en_US.UTF-8"},{"name":"_","value":"/usr/bin/env"}] diff --git a/tests/fixtures/generic/env-multiline.out b/tests/fixtures/generic/env-multiline.out new file mode 100644 index 00000000..9ddc021f --- /dev/null +++ b/tests/fixtures/generic/env-multiline.out @@ -0,0 +1,30 @@ +TERM_PROGRAM=Apple_Terminal +SHELL=/bin/zsh +TERM=xterm-256color +TMPDIR=/var/folders/8g/r6tdh9kj6z35x9rfmgtt3x6r0000gn/T/ +TERM_PROGRAM_VERSION=447 +TERM_SESSION_ID=A7F0FFB1-C12A-42EA-A8D9-0757BB2D4DDE +USER=kelly +SSH_AUTH_SOCK=/private/tmp/com.apple.launchd.6IQJQijqVl/Listeners +PATH=/Users/kelly/.pyenv/shims:/Users/kelly/.gem/ruby/2.6.0/bin:/Users/kelly/.local/bin:/Users/kelly/.cargo/bin:/usr/local/bin:/System/Cryptexes/App/usr/bin:/usr/bin:/bin:/usr/sbin:/sbin:/Library/Apple/usr/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/local/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/appleinternal/bin +LaunchInstanceID=3D00D30B-5C8B-421F-958D-91B196167506 +__CFBundleIdentifier=com.apple.Terminal +PWD=/Users/kelly/temp +XPC_FLAGS=0x0 +XPC_SERVICE_NAME=0 +SHLVL=1 +HOME=/Users/kelly +LOGNAME=kelly +SECURITYSESSIONID=186a3 +OLDPWD=/Users/kelly/git/jc/tests/fixtures/ubuntu-22.04 +JC_COLORS=cyan,default,default,default +JELLO_COLORS=cyan,default,default,default +PYENV_ROOT=/Users/kelly/.pyenv +PYENV_SHELL=zsh +MYVAR=hello world +hello=world +KELLYVAR=This is a multiline +variable that has var=1 a definition +inside of it +LANG=en_US.UTF-8 +_=/usr/bin/env diff --git a/tests/test_env.py b/tests/test_env.py index a0144dca..d76c61d2 100644 --- a/tests/test_env.py +++ b/tests/test_env.py @@ -7,6 +7,7 @@ THIS_DIR = os.path.dirname(os.path.abspath(__file__)) class MyTests(unittest.TestCase): + maxDiff = None # input with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/centos-7.7/env.out'), 'r', encoding='utf-8') as f: @@ -15,6 +16,9 @@ class MyTests(unittest.TestCase): with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/ubuntu-18.04/env.out'), 'r', encoding='utf-8') as f: ubuntu_18_4_env = f.read() + with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/generic/env-multiline.out'), 'r', encoding='utf-8') as f: + env_multiline = f.read() + # output with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/centos-7.7/env.json'), 'r', encoding='utf-8') as f: centos_7_7_env_json = json.loads(f.read()) @@ -22,6 +26,12 @@ class MyTests(unittest.TestCase): with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/ubuntu-18.04/env.json'), 'r', encoding='utf-8') as f: ubuntu_18_4_env_json = json.loads(f.read()) + with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/generic/env-multiline.json'), 'r', encoding='utf-8') as f: + env_multiline_json = json.loads(f.read()) + + with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/generic/env-multiline-raw.json'), 'r', encoding='utf-8') as f: + env_multiline_raw_json = json.loads(f.read()) + def test_env_nodata(self): """ @@ -41,6 +51,18 @@ class MyTests(unittest.TestCase): """ self.assertEqual(jc.parsers.env.parse(self.ubuntu_18_4_env, quiet=True), self.ubuntu_18_4_env_json) + def test_env_multiline(self): + """ + Test 'env' with multiline value + """ + self.assertEqual(jc.parsers.env.parse(self.env_multiline, quiet=True), self.env_multiline_json) + + def test_env_multiline_raw(self): + """ + Test 'env' with multiline value with raw output + """ + self.assertEqual(jc.parsers.env.parse(self.env_multiline, quiet=True, raw=True), self.env_multiline_raw_json) + if __name__ == '__main__': unittest.main()