Skip to content
Substitute a pattern with a replacement on specific lines
OCaml
Branch: master
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
.ci
bin
lib
tests
.gitattributes
.gitignore
.travis.yml
LICENSE
README.md
dune-project
multi-sub.opam
run-tests.sh

README.md

multi-sub

Build multi-sub Status

Substitute a pattern with a replacement on specific lines

By default, sed's s/.../.../ command tries to perform a substitution on all lines in a file. Sometimes that is too coarse grained. By contrast, multi-sub limits the substitution to only happen on specified lines.

multi-sub takes input that looks like

filename.txt:17
filename.txt:42
another.md:12
another.md:23

and substitutes some pattern with some replacement only on the lines specified in the input. The replacement is always done in place. In this way, multi-sub is a Unix-style pipeline sink.

If sed is like a chainsaw, multi-sub is more like a scalpel. Using a combination of tools like git grep -l and awk with multi-sub, we can construct precise filters. Specifically, two other tools are particularly useful in conjunection with multi-sub:

  • diff-locs is a tool that converts a unified diff into input suitable for multi-sub.

  • multi-grep is like multi-sub, but with grep.

multi-sub is fast. It's implemented in OCaml, compiled with OCaml's impressive optimizing compiler, and has been repeatedly profiled to improve performance. It only does work that's absolutely needed.

Usage

This is the help for multi-sub version 0.0.0. It might be out of date—run multi-sub --help locally for up-to-date help.

❯ multi-sub --help
Usage:
  multi-sub [options] <pattern> <replace> [<locs.txt>]

Substitutes the pattern with the replacement in the mentioned lines.

Arguments:
  <pattern>      A valid OCaml regular expression[1].
  <replace>      The expression to substitute when a match is found[2].
  <locs.txt>     The name of a file with lines formatted like:
                   filename.ext:20
                 If omitted, reads from stdin.

Options:
  -i, --ignore-case     Treat the pattern as case insensitive.
  -s, --case-sensitive  Treat the pattern as case sensitive [default].
  -g, --global          Replace all matches on a line, not just the first.
  --version             Print version and exit.

[1]: https://caml.inria.fr/pub/docs/manual-ocaml/libref/Str.html#VALregexp
[2]: https://caml.inria.fr/pub/docs/manual-ocaml/libref/Str.html#VALglobal_replace

Install

There are pre-compiled binaries for macOS and Linux. You can also install from source (see Contributing).

macOS

Using Homebrew:

  • brew install jez/formulae/multi-sub

Or, download the binary directly from the releases:

Linux

Download the binary from the releases page:

Contributing

One-time setup

  1. Make sure you have opam installed. OPAM manages both the OCaml compiler version and any OCaml packages' versions.

    → How to install opam

    The next instructions assume you're using opam 2.0+ (opam --version).

  2. Create a switch for this project:

    ❯ git clone https://github.com/jez/multi-sub
    ❯ cd multi-sub
    ❯ opam switch create . ocaml-base-compiler.4.07.1
  3. Install dune:

    ❯ opam install dune

Building

# To build for development:
❯ dune build

# To run:
❯ dune exec -- multi-sub

# To install from source:
❯ dune install --prefix="$HOME/.local"

TODO

  • Add test suite
    • Test that we preserve permissions
    • Document how to run and add tests
    • Test for out-of-order input lines
    • Test for options (-g, -i, -s)
    • Test that missing arguments gives error
    • Test that we don't add a trailing newline if the file doesn't have one
  • Publish Homebrew formula
    • Document how to bump the version
  • Set up ShellCheck
  • Set up source code formatter
  • Document how to set up editor tools

License

MIT License

You can’t perform that action at this time.