{"id":15991981,"url":"https://github.com/aanastasiou/pyjunix","last_synced_at":"2026-01-28T05:20:15.429Z","repository":{"id":44863861,"uuid":"213108405","full_name":"aanastasiou/pyjunix","owner":"aanastasiou","description":"An implementation of Eric Fischer's `junix` (Unix as if JSON mattered).","archived":false,"fork":false,"pushed_at":"2022-12-08T06:41:01.000Z","size":99,"stargazers_count":3,"open_issues_count":6,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-10-27T21:45:05.365Z","etag":null,"topics":["json","python","unix-command","unix-philosophy"],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/aanastasiou.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-10-06T04:25:45.000Z","updated_at":"2024-12-24T03:40:29.000Z","dependencies_parsed_at":"2023-01-24T16:45:16.580Z","dependency_job_id":null,"html_url":"https://github.com/aanastasiou/pyjunix","commit_stats":null,"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"purl":"pkg:github/aanastasiou/pyjunix","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aanastasiou%2Fpyjunix","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aanastasiou%2Fpyjunix/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aanastasiou%2Fpyjunix/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aanastasiou%2Fpyjunix/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/aanastasiou","download_url":"https://codeload.github.com/aanastasiou/pyjunix/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aanastasiou%2Fpyjunix/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28840088,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-28T02:10:51.810Z","status":"ssl_error","status_checked_at":"2026-01-28T02:10:50.806Z","response_time":57,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["json","python","unix-command","unix-philosophy"],"created_at":"2024-10-08T06:04:51.817Z","updated_at":"2026-01-28T05:20:15.396Z","avatar_url":"https://github.com/aanastasiou.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# PyJUnix\n\nScripts implemented so far:\n\n* [`pyjkeys`](#pyjkeys)\n* [`pyjarray`](#pyjarray--pyjunarray)\n* [`pyjunarray`](#pyjarray--pyjunarray)\n* [`pyjls`](#pyjls)\n* [`pyjgrep`](#pyjgrep)\n* [`pyjprtprn`](#pyjprtprn)\n* [`pyjsort`](#pyjsort)\n* [`pyjlast`](#pyjlast)\n* [`pyjps`](#pyjps)\n* [`pyjjoin`](#pyjjoin)\n* [`pyjcat`](#pyjcat)\n* [`pyjpaste`](#pyjpaste)\n* [`pyjsplit`](#pyjsplit)\n* [`pyjdiff`](#pyjdiff)\n* [`pyjuniq`](#pyjuniq)\n\n## Installation\n\n1. Clone the repository\n2. Create a `virtualenv` with Python 3.6\n3. Install the requirements with `pip install -r requirements.txt`\n4. Try with `./pyjbox.py pyjls` and so on (from the project's root folder).\n\n### Launching scripts\n\nAs of version 0.2, the project includes a `pyjbox.py` script that launches all others. This enables symbolic links \nto `pyjbox` and plain simple launching with `pyjbox pyjls -maxdepth 4` (for example).\n\nIn what follows, sripts are launched through `pyjbox` and are readily available directly from the cloned repository.\n\n## Examples\n\n### PyJArray \u0026 PyJUnArray\n\nThese two functions operate as a \"bridge\" between JSON and \"mixed content\". \n\nMixed content is any newline delineated list of items that is interpreted as a JSON list as long as JSON items are \nin \"compact format\" (which minimises whitespace).\n\nFor example, let's create a JSON document from a list of numbers:\n\n```\n    \u003e seq 1 10|./pyjbox.py pyjarray\n```\n\nWill emit `[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]` (Notice here: The list elements are numeric).\n\nPassing this through \"unarray\" brings us back to the original document in this example:\n\n```\n    \u003e seq 1 10|./pyjbox.py pyjarray|./pyjbox.py pyjunarray\n```\n\nWill emit\n\n```\n    1\n    2\n    3\n    4\n    5\n    6\n    7\n    8\n    9\n    10\n    \n```\n\n### PyJKeys\n\n```\n    \u003e echo \"{\\\"Alpha\\\":1, \\\"Beta\\\":2, \\\"Gamma\\\":3}\"|./pyjbox.py pyjkeys\n```\n\nWill emit `[\"Alpha\", \"Beta\", \"Gamma\"]`.\n\n### PyJLs\n\nA very simple `ls` that produces a nested list JSON document with a directory listing.\n\nStraightforward invocation lists all items in the current directory. For example, called in `pyjunix` directory:\n\n```\n\n    \u003e ./pyjbox.py pyjls\n    \n```\n\nWill emit:\n\n```\n\n    [\n        {\"group\": \"somegroup\", \n         \"accessed\": \"2019-10-06T01:41:09.685035\", \n         \"modified\": \"2019-10-06T01:41:09.912035\", \n         \"permissions\": \"-rw-rw-r--\", \n         \"bytes\": 4080, \n         \"created\": \"2019-10-06T01:41:09.912035\", \n         \"item\": \"somefile.txt\", \n         \"user\": \"someuser\"\n        }, \n        \n        . . .\n    ]\n\n```\n\nBy default the recursion level is set to 1. It can be controlled with `-maxdepth \u003cN\u003e` where `N` is the maximum depth \nto descend to. Setting `maxdepth` to `-1` will perform an exhaustive list.\n\n### PyJGrep\n\n```\n    \u003e ./pyjbox.py pyjls|./pyjbox.py pyjgrep '$[*][?(@.permissions.charAt(0) = \\\"d\\\")].item'\n```\n\nWill emit all directories in the current directory.\n\nThis is translated as _\"From all items for which the permissions attribute's first character is d, return the attribute\nitem\"_.\n\nA more long winded way of expressing this would be to retrieve all items that contain ``entries`` as:\n\n```\n    \u003e ./pyjbox.py pyjls -maxdepth 2|./pyjbox.py pyjgrep '$[*][?(@.entries.length()\u003e0)].item'\n```\n\n**Notice here** `pyjls` was invoked with a `-maxdepth` to enable `pyjls` to descend into lower directories and \nproduce items with `entries` elements in their mapping.\n\nOr, the same thing but returning the number of items in each directory as :\n\n```\n    \u003e ./pyjbox.py pyjls -maxdepth -1|./pyjbox.py pyjgrep '$[*][?(@.entries.length()\u003e0)].entries.length()'\n```\n\n### PyJSort\n\n```\n    \u003e seq 1 10|./pyjbox.py pyjarray|./pyjbox.py pyjsort -r\n```\n\nProduces a sequence of numbers from 1 to 10, packs them in a JSON array and sorts them in reverse (`-r`).\n\nSorting more complex data structures is possible with `jsonpath`:\n\n```\n    \u003e ./pyjbox.py pyjls|./pyjbox.py pyjsort -k '$[*].item'\n```\n\nHere, `pyjls` will produce a directory listing which `pyjsort` will sort by the attribute `item`.\n\n### PyJLast\n\n```\n    \u003e ./pyjbox.py pyjlast\n```\n\nReturns information about the users last logged in to the system as a JSON list of records with full information on \nwhich username was logged in to the system from which device / host and at what time.\n\n### PyJPs\n\nSimilarly to `ps`, returns a list of the currently running processes.\n\n```\n    \u003e ./pyjbox.py pyjps\n```\n\n### PyJJoin\n\nJoin two JSON files that are formated as lists of lists on a common (zero-based) index.\n\nGiven files:\n\n* **file_1.json**:\n\n```\n  [\n      [1, \"Alpha\"],\n      [2, \"Beta\"],\n      [2, \"Gamma\"],\n      [42, \"Gamma\"]\n  ]\n```\n\nand\n\n* **file_2.json**:\n\n```\n  [\n      [1, \"Pingo\"],\n      [2, \"Pango\"],\n      [24, \"Flop\"]\n  ]\n```\n\nThen:\n\n```\n    \u003e ./pyjbox pyjjoin file_1.json file_2.json\n```\n\nWould result in:\n\n```\n    [\n        [1, \"Alpha\", \"Pingo\"],\n        [2, \"Beta\", \"Pango\"],\n        [2, \"Gamma\", \"Pango\"]\n    ]\n```\n\nAdding `-v 1` would suppress the matched entries and only output:\n\n```\n    [\n        [42, \"Gamma\"]\n    ]\n```\n\n(Conversely, `[42, \"Gamma\"]` would become `[24, \"Flop\"]` if `-v 2`)\n\nAdding `-a 1` or `-a 2` would simply add the above unmatched entries to the array of the matched ones.\n\nBy default the attribute the join is performed on is 0, add `-1 NUM` and/or `-2 NUM` to change that.\n\n**Note:** At the moment the script operates over lists of lists and will likely also work over lists of objects with \n`-1 attribute` denoting the attribute to join on. However, I would like to add a generic way to join on arbitrary \n`jsonpath` exceptions, irrespectively of the data type of either of the matched items. Will have a better idea by \nthe next release. See [doc/source/junix_notes.rst](doc/source/junix_notes.rst) for more details\n\n### PyJCat\n\nSimply concatenates the contents of two or more JSON files that should contain lists.\n\n### PyJPaste\n\nGiven two or more JSON files that are formatted as list-of-lists or list-of-objects, it returns on list where each \nelement is the concatenation (or extension) of each list element (or object).\n\n### PyJDiff\n\nRuns a ``diff`` over two data structures described by the JSON files and returns a JSON data structure describing their\ndifferences.\n\nGiven files:\n\n* **file_1.json**:\n\n```\n  {\n      \"Alpha\":1,\n      \"Beta\":[1,2],\n      \"Gamma\":{}\n  }\n```\n\nand\n\n* **file_2.json:**:\n\n```\n  {\n      \"Alpha\":1,\n      \"Beta\":[1,2,3],\n      \"Gamma\":{}\n  }\n```\n\nrunning ``pyjdiff`` with:\n\n```\n    \u003e ./pyjbox.py pyjdiff file_./pyjbox.py pyjdiff file_1.json\n```\n\nwould return:\n\n```\n{\n    \"iterable_item_added\": {\n        \"root['Beta'][2]\": 3\n    }\n\n}\n```\n\n### PyJSplit\n\nSplits a JSON file that is formated as a `list\u003cany\u003e` to one or more files containing a least number of items.\n\n```\n    \u003e seq 0 100|./pyjbox.py pyjarray|./pyjbox.py pyjsplit - -l 10 --additional-suffix .json -d --prefix result_ \n```\n\nThis sequence of commands will produce 11 files, named `result_\u003cdd\u003e.json` (where dd is a number from `00` to `10`) \ncontaining a list of at least 10 numbers.\n\nNotice the use of `-` to take input from `stdin`.\n\n### PyJUniq\n\nReturns unique items from a list of items. It expects its input formatted as a list and it can operate either via an \nimplicit list of arguments or a list JSON object loaded from ``stdin``.\n\n```\n    \u003e ./pyjbox.py pyjuniq \"Alpha\" \"Beta\" \"Gamma\" \"Gamma\" \"Alpha\" \"Beta\"\n```\n\nWould produce\n\n```\n    [\"Alpha\", \"Beta\",\"Gamma\"]\n```\n\nThe script can also output only duplicate or unique items and a form that also includes the number of items encountered\nin the list.\n\n### PyJPrtPrn\n\nPretty print output. This is usually the last script in a piped chain of scripts.\n\nA plain invocation of `pyjls`, will return the results in compact form, which is very difficult to read, especially \nif the result is too long.\n\nCompact JSON form:\n\n```\n    \u003e ./pyjbox.py pyjls\n```\n\nHuman readable form:\n\n```\n    \u003e ./pyjbox.py pyjls|./pyjbox.py pyjprtprn\n```\n\n## Where to next?\n\nFor many more details on each script checkout the documentation in 'doc/' as well as \nEric Fischer's original [junix spec repository](https://github.com/ericfischer/junix).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faanastasiou%2Fpyjunix","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Faanastasiou%2Fpyjunix","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faanastasiou%2Fpyjunix/lists"}