{"id":15674431,"url":"https://github.com/jedwards1211/lambduh","last_synced_at":"2025-05-06T22:59:25.198Z","repository":{"id":143884915,"uuid":"94303295","full_name":"jedwards1211/lambduh","owner":"jedwards1211","description":"Turing-complete JS commandline JSON/text transformer and file renamer","archived":false,"fork":false,"pushed_at":"2018-10-02T15:30:48.000Z","size":79,"stargazers_count":14,"open_issues_count":5,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-05-06T22:59:12.690Z","etag":null,"topics":["batch-rename","batch-rename-files","batch-renamer","es6","file-rename","io","json","lambda","lambda-functions","stdio","transforming-files"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jedwards1211.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-06-14T07:38:52.000Z","updated_at":"2022-12-16T17:42:17.000Z","dependencies_parsed_at":null,"dependency_job_id":"843a9724-2f14-49ec-95ca-f34ccef7795c","html_url":"https://github.com/jedwards1211/lambduh","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jedwards1211%2Flambduh","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jedwards1211%2Flambduh/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jedwards1211%2Flambduh/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jedwards1211%2Flambduh/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jedwards1211","download_url":"https://codeload.github.com/jedwards1211/lambduh/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252782482,"owners_count":21803383,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":["batch-rename","batch-rename-files","batch-renamer","es6","file-rename","io","json","lambda","lambda-functions","stdio","transforming-files"],"created_at":"2024-10-03T15:44:37.727Z","updated_at":"2025-05-06T22:59:25.175Z","avatar_url":"https://github.com/jedwards1211.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# lambduh\n\n[![Build Status](https://travis-ci.org/jedwards1211/lambduh.svg?branch=master)](https://travis-ci.org/jedwards1211/lambduh)\n[![Coverage Status](https://coveralls.io/repos/github/jedwards1211/lambduh/badge.svg?branch=master)](https://coveralls.io/github/jedwards1211/lambduh?branch=master)\n[![semantic-release](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg)](https://github.com/semantic-release/semantic-release)\n[![Commitizen friendly](https://img.shields.io/badge/commitizen-friendly-brightgreen.svg)](http://commitizen.github.io/cz-cli/)\n\n## Turing-complete JS commandline JSON/text transformer and file renamer\n\nCan't remember how `sed` works.\nCan't remember how `cut` works.\nI just don't have to use them often enough to commit them to memory.\nWhen I first read about `jq`, I thought *Hmm, this is cool, but I love ES6.  I know it well.\nCan't I just do all of this stuff in JS?*\n\nThese days I'm too lazy to read manpages and learn tools like `jq`, `sed`, `awk`, etc. for things that I could\nI could write a JS expression for in ten seconds.  All that was lacking was a basic command to handle the I/O.\nJust look how frigging easy stuff is:\n\n```\nnpm install --global lambduh\n```\n##### Print lines of file in reverse order:\n```\n\u003e duh 'lines =\u003e lines.reverse()' somefile\n```\n\n#####  Swap first and last name:\n```\n\u003e duh 'line =\u003e line.split(/\\s+/g).reverse().join(\", \")' names\nEdwards, Andy\nDoe, John\n```\n\n##### Put all `lodash` functions on global scope for `duh` commands for the next few examples\n```\n\u003e cd ~ \u0026\u0026 npm install lodash\n\u003e echo 'Object.assign(global, require(\"lodash\"))' \u003e\u003e ~/.lambduh.js\n```\n\n##### Shuffle lines of a file\n```\n\u003e duh 'lines =\u003e shuffle(lines)' somefile\n```\n\n##### See who's been committing to `react` recently:\n```\n\u003e curl 'https://api.github.com/repos/facebook/react/commits?per_page=100' \\\n  | duh 'json =\u003e mapValues(groupBy(json, \"commit.committer.name\"), size)'\n{\n  \"GitHub\": 60,\n  \"Dan Abramov\": 17,\n  \"Flarnie Marchan\": 2,\n  \"Brian Vaughn\": 3,\n  \"Nathan Hunzaker\": 5,\n  \"Brandon Dail\": 8,\n  \"Ben Alpert\": 1,\n  \"Andrew Clark\": 1,\n  \"Dominic Gannaway\": 3\n}\n```\n##### Destructure and restructure JSON subdocuments\n```\n\u003e curl 'https://api.github.com/repos/stedolan/jq/commits?per_page=2' \\\n  | duh 'json =\u003e json.map(({ commit: { message, committer: { name } } }) =\u003e ({ message, name }))'\n[\n  {\n    \"message\": \"Deal with strptime() on OS X and *BSD (fix #1415)\\n\\nstrptime() on OS X and *BSDs (reputedly) does not set tm_wday and\\ntm_yday unless corresponding %U and %j format specifiers were used.\\nThat can be... surprising when one parsed year, month, and day anyways.\\nGlibc's strptime() conveniently sets tm_wday and tm_yday in those cases,\\nbut OS X's does not, ignoring them completely.\\n\\nThis commit makes jq compute those where possible, though the day of\\nweek computation may be wrong for dates before 1900-03-01 or after\\n2099-12-31.\",\n    \"name\": \"Nicolas Williams\"\n  },\n  {\n    \"message\": \"Attempt to fix #1415\\n\\nOS X (and *BSD) strptime() does not set tm_wday nor tm_yday unless\\ncorresponding format options are used.  That means we must call timegm()\\nto set them.\",\n    \"name\": \"Nicolas Williams\"\n  }\n]\n```\n\n##### Make AWS CLI Output easier to use\n```\n\u003e aws cloudformation describe-stacks --stack-name clarity-db --query 'Stacks[0].Outputs' \\\n  | duh 'json =\u003e require(\"lodash\").fromPairs(json.map(({OutputKey, OutputValue}) =\u003e [OutputKey, OutputValue]))'\n{\n  \"DBInstance\": \"clarity\",\n  \"RecordSet\": \"db.clarity.jcore.io\",\n  \"SecurityGroup\": \"sg-31bbd643\"\n}\n```\n\n##### Rename .jsx files in the current directory to .js:\n```\nduh mv 'file =\u003e file.replace(/\\.jsx$/g, \".js\")' *.jsx\n```\n\n##### Rename all .jsx files in the current subtree to .js:\n```\nfind . -name *.jsx | duh mv 'file =\u003e file.replace(/\\.jsx$/g, \".js\")'\n```\n\n##### Flatten music stored as artist/album/filename into a single directory:\n```\nduh mv 'file =\u003e { var [artist, album, file] = file.split(/\\//g); return `${artist} - ${album} - ${file}` }' */*/*\n```\n\n## Node version note\n\nThe syntax that `duh` accepts for your function depends on the version of Node you're running.  If you're running a\nreally old version, you may not be able to use lambdas, and will have to resort to `function () { return ... }` instead.\n\n## Command\n\nYou can use `lambduh` or just `duh`.\n\n## Modes\n\nYou may have noticed above that you can apply a transformation to each line of a plain text file.\nThis is pretty much the only magic built into the `duh` command:\n* If the first argument is `mv` (rather than a function), it runs in **move mode**.\n* If the first argument of your function is `lines` (ignoring case), it runs in **lines mode**.\n* Otherwise if the first argument of your function starts with `l` or `L`, it runs in **line mode**.\n* If the first argument of your function starts with `t` or `T`, it runs in **text mode**.\n* Otherwise, it runs in **json mode**.\n\n:star: :+1: **Nice!!** :punch: :sparkles:\n\n### JSON Mode\n\n`JSON.parse`s `stdin` or each file in the arguments, calls your function on it, writes `JSON.stringify` to `stdout`\n(or overwrites the input file with the result if you use the `-i` option).\nDuh!\n\n### Line Mode\n\nReads lines from `stdin` or each file in the arguments, one-by-one, calls your function on each with\n`(text, lineNumber)`, and writes what it returns to `stdout` (or overwrites the input file with the result if you use\nthe `-i` option), except:\n\n* If your function returns `false`, it quits reading lines.\n* If your function returns `null` or `undefined`, it skips to the next line without printing anything.\n\n#### Options\n* `--eol=EOL`: (default: system line separator) specify line separator\n\n### Lines Mode\n\nCalls your function with an array of lines from stdin or each file in the arguments.  If your function returns an array\nof lines, it writes them in order to `stdout` (or overwrites the input file with the result if you use the `-i` option).\nOtherwise, it calls `String` on it and outputs that string.\n\n#### Options\n* `--eol=EOL`: (default: system line separator) specify line separator\n\n### Text Mode\n\nCalls your function with the entire text from stdin or each file in the arguments in a single string, and writes what\nyour function returns to `stdout` (or overwrites the input file with the result if you use the `-i` option).\n\n### Options for all above modes\n* `-i`: (in place) overwrite input files with the result of the transformation\n* `--encoding=ENCODING`: (default: `utf8`) specify file encoding\n\n### Move Mode\n\nMove mode renames each file (or directory) in the arguments by applying your provided function\n(which must be the last argument) to each filename.  If you don't provide any files in the arguments, or one of your\n arguments is a single dash (`-`), it will also read from stdin, treating each line as a filename.\n\nBy default it will print the old and new filenames and then ask\nfor confirmation before actually moving them.\n\n#### Options\n* `-y`: move the files without asking you to confirm the new names.\n* `--dry-run`: just print out the old and new file names, then exit.\n\n## Customization\n\n`lambduh` requires any `.lambduh.js` files in the working directory or its ancestors (from root to deepest), as well\nas `.lambduh.js` in your home directory.  You can use these files to put functions on `global` scope so that you can\nuse them in your function argument to `lambduh`.  For example, here are some of the things I put in my `~/.lambduh.js`:\n\n```js\nObject.assign(global, require('lodash')) // put all lodash functions on context\nglobal.replace = require('preserve-case') // case-preserving string.replace, often handy\n```\n\nWith this I can do powerful stuff like:\n```\n\u003e echo '{ \"a\": \"foo bar\", \"b\": \"FOO BAR\", \"c\": \"it's all about the fooBar\", \"d\": \"foo-bar\" }' \\\n  | duh 'json =\u003e mapValues(json, value =\u003e replace(value, \"foo bar\", \"baz qux\"))'\n{\n  \"a\": \"baz qux\",\n  \"b\": \"BAZ QUX\",\n  \"c\": \"it's all about the bazQux\",\n  \"d\": \"baz-qux\"\n}\n```\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjedwards1211%2Flambduh","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjedwards1211%2Flambduh","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjedwards1211%2Flambduh/lists"}