{"id":17442573,"url":"https://github.com/jez/multi-sub","last_synced_at":"2025-04-13T08:21:02.154Z","repository":{"id":73798321,"uuid":"184620147","full_name":"jez/multi-sub","owner":"jez","description":"Substitute a pattern with a replacement on specific lines","archived":false,"fork":false,"pushed_at":"2019-08-28T22:40:06.000Z","size":29,"stargazers_count":6,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-03-27T00:11:06.826Z","etag":null,"topics":["cli"],"latest_commit_sha":null,"homepage":"https://blog.jez.io/surgery-on-code/","language":"OCaml","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/jez.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,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-05-02T17:07:08.000Z","updated_at":"2024-02-22T14:23:33.000Z","dependencies_parsed_at":"2023-03-25T11:17:06.435Z","dependency_job_id":null,"html_url":"https://github.com/jez/multi-sub","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jez%2Fmulti-sub","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jez%2Fmulti-sub/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jez%2Fmulti-sub/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jez%2Fmulti-sub/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jez","download_url":"https://codeload.github.com/jez/multi-sub/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248681598,"owners_count":21144715,"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":["cli"],"created_at":"2024-10-17T16:18:00.637Z","updated_at":"2025-04-13T08:21:02.133Z","avatar_url":"https://github.com/jez.png","language":"OCaml","funding_links":[],"categories":[],"sub_categories":[],"readme":"# multi-sub\n\n[![Build multi-sub Status](https://travis-ci.org/jez/multi-sub.svg?branch=master)](https://travis-ci.org/jez/multi-sub)\n\n\u003e Substitute a pattern with a replacement on specific lines\n\nBy default, `sed`'s `s/.../.../` command tries to perform a substitution on\n**all** lines in a file. Sometimes that is too coarse grained. By contrast,\n`multi-sub` limits the substitution to only happen on specified lines.\n\n`multi-sub` takes input that looks like\n\n```\nfilename.txt:17\nfilename.txt:42\nanother.md:12\nanother.md:23\n```\n\nand substitutes some pattern with some replacement only on the lines specified\nin the input. The replacement is always done in place. In this way, `multi-sub`\nis a Unix-style pipeline [sink].\n\nIf `sed` is like a chainsaw, `multi-sub` is more like a scalpel. Using a\ncombination of tools like `git grep -l` and `awk` with `multi-sub`, we can\nconstruct precise filters. Specifically, two other tools are particularly useful\nin conjunection with `multi-sub`:\n\n- [diff-locs] is a tool that converts a unified diff into input suitable for\n  `multi-sub`.\n\n- [multi-grep] is like `multi-sub`, but with `grep`.\n\n`multi-sub` is fast. It's implemented in OCaml, compiled with OCaml's\nimpressive optimizing compiler, and has been repeatedly profiled to improve\nperformance. It only does work that's absolutely needed.\n\n[diff-locs]: https://github.com/jez/diff-locs\n[multi-grep]: https://github.com/jez/multi-grep\n\n\n## Usage\n\nThis is the help for `multi-sub` version `0.0.0`. It might be out of date—run\n`multi-sub --help` locally for up-to-date help.\n\n```\n❯ multi-sub --help\nUsage:\n  multi-sub [options] \u003cpattern\u003e \u003creplace\u003e [\u003clocs.txt\u003e]\n\nSubstitutes the pattern with the replacement in the mentioned lines.\n\nArguments:\n  \u003cpattern\u003e      A valid OCaml regular expression[1].\n  \u003creplace\u003e      The expression to substitute when a match is found[2].\n  \u003clocs.txt\u003e     The name of a file with lines formatted like:\n                   filename.ext:20\n                 If omitted, reads from stdin.\n\nOptions:\n  -i, --ignore-case     Treat the pattern as case insensitive.\n  -s, --case-sensitive  Treat the pattern as case sensitive [default].\n  -g, --global          Replace all matches on a line, not just the first.\n  --version             Print version and exit.\n\n[1]: https://caml.inria.fr/pub/docs/manual-ocaml/libref/Str.html#VALregexp\n[2]: https://caml.inria.fr/pub/docs/manual-ocaml/libref/Str.html#VALglobal_replace\n```\n\n\n## Install\n\nThere are pre-compiled binaries for macOS and Linux.\nYou can also install from source (see [Contributing](#contributing)).\n\n### macOS\n\nUsing Homebrew:\n\n- `brew install jez/formulae/multi-sub`\n\nOr, download the binary directly from the releases:\n\n- \u003chttps://github.com/jez/multi-sub/releases/download/latest\u003e\n\n### Linux\n\nDownload the binary from the releases page:\n\n- \u003chttps://github.com/jez/multi-sub/releases/download/latest\u003e\n\n\n## Contributing\n\n### One-time setup\n\n1.  Make sure you have `opam` installed. OPAM manages both the OCaml compiler\n    version and any OCaml packages' versions.\n\n    [→ How to install opam](https://opam.ocaml.org/doc/Install.html)\n\n    The next instructions assume you're using `opam` 2.0+ (`opam --version`).\n\n1.  Create a switch for this project:\n\n    ```bash\n    ❯ git clone https://github.com/jez/multi-sub\n    ❯ cd multi-sub\n    ❯ opam switch create . ocaml-base-compiler.4.07.1\n    ```\n\n1.  Install `dune`:\n\n    ```bash\n    ❯ opam install dune\n    ```\n\n### Building\n\n```bash\n# To build for development:\n❯ dune build\n\n# To run:\n❯ dune exec -- multi-sub\n\n# To install from source:\n❯ dune install --prefix=\"$HOME/.local\"\n```\n\n\n## TODO\n\n- Add test suite\n  - Test that we preserve permissions\n  - Document how to run and add tests\n  - Test for out-of-order input lines\n  - Test for options (`-g`, `-i`, `-s`)\n  - Test that missing arguments gives error\n  - Test that we don't add a trailing newline if the file doesn't have one\n- Publish Homebrew formula\n  - Document how to bump the version\n- Set up ShellCheck\n- Set up source code formatter\n- Document how to set up editor tools\n  - \u003chttps://medium.com/@bobbypriambodo/starting-an-ocaml-app-project-using-dune-d4f74e291de8\u003e\n\n\n\n## License\n\n[![MIT License](https://img.shields.io/badge/license-MIT-blue.svg)](https://jez.io/MIT-LICENSE.txt)\n\n[sink]: https://homepage.cs.uri.edu/~thenry/resources/unix_art/ch11s06.html#id2958116\n[Dune]: https://dune.build/\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjez%2Fmulti-sub","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjez%2Fmulti-sub","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjez%2Fmulti-sub/lists"}