sd/README.md

110 lines
2.8 KiB
Markdown
Raw Normal View History

2018-12-24 00:24:43 +00:00
# sd - s[earch] & d[isplace]
2018-12-26 00:01:11 +00:00
`sd` is an intuitive find & replace CLI.
2018-12-24 00:24:43 +00:00
2018-12-26 00:01:11 +00:00
## The Pitch
Why use it over any existing tools?
2018-12-24 00:24:43 +00:00
**Painless regular expressions**
2018-12-26 00:01:11 +00:00
`sd` uses regex syntax that you already know from JavaScript and Python. Forget about dealing with quirks of `sed` or `awk` - get productive immediately.
2018-12-24 00:24:43 +00:00
**String-literal mode**
2018-12-26 07:32:26 +00:00
Non-regex find & replace. No more backslashes or remembering which characters are special and need to be escaped.
2018-12-24 00:24:43 +00:00
**Easy to read, easy to write**
2018-12-26 07:32:26 +00:00
Find & replace expressions are split up, which makes them easy to read and write.
2018-12-24 00:24:43 +00:00
## Comparison to sed
2018-12-26 00:01:11 +00:00
While sed does a whole lot more, `sd` focuses on doing just one thing and doing it well.
2018-12-24 00:46:02 +00:00
2018-12-24 00:24:43 +00:00
Some cherry-picked examples, where `sd` shines:
- Replace newlines with commas:
2018-12-26 19:23:13 +00:00
- sd: `sd '\r' ','`
2018-12-26 07:32:26 +00:00
- sed: `sed ':a;N;$!ba;s/\r/,/g'`
2018-12-24 00:44:57 +00:00
- Extracting stuff out of strings with special characters
2018-12-26 19:23:13 +00:00
- sd: `echo "{((sample with /path/))}" | sd '\{\(\(.*(/.*/)\)\)\}' '$1'`
2018-12-24 00:44:57 +00:00
- sed
- incorrect, but closest I could get after 15 minutes of struggle
2018-12-24 00:55:48 +00:00
- `echo "{((sample with /path/))}" | sed 's/{((\.\*\(\/.*\/\)))}/\1/g'`
2018-12-24 00:44:57 +00:00
2018-12-26 07:32:26 +00:00
Note: although `sed` does have a nicer regex syntax with `-r`, it is a non-portable GNU-ism and thus doesn't work on MacOS, BSD, or Solaris.
2018-12-24 00:24:43 +00:00
2018-12-26 07:32:26 +00:00
## Quick Guide
2018-12-24 03:57:36 +00:00
2018-12-26 19:23:13 +00:00
1. **String-literal mode**. By default, expressions are treated as regex. Use `-s` or `--string-mode` to disable regex.
2018-12-24 03:57:36 +00:00
```sh
2018-12-26 19:23:13 +00:00
> echo 'lots((([]))) of special chars' | sd -s '((([])))' ''
2018-12-24 03:57:36 +00:00
lots of special chars
```
2018-12-24 04:01:20 +00:00
2. **Basic regex use** - let's trim some trailing whitespace
2018-12-24 03:57:36 +00:00
```sh
2018-12-26 19:23:13 +00:00
> echo 'lorem ipsum 23 ' | sd '\s+$' ''
2018-12-24 03:57:36 +00:00
lorem ipsum 23
```
2018-12-24 04:01:20 +00:00
3. **Capture groups**
2018-12-24 03:57:36 +00:00
Indexed capture groups:
```sh
2018-12-26 19:23:13 +00:00
> echo 'cargo +nightly watch' | sd '(\w+)\s+\+(\w+)\s+(\w+)' 'cmd: $1, channel: $2, subcmd: $3'
2018-12-24 03:57:36 +00:00
cmd: cargo, channel: nightly, subcmd: watch
```
Named capture groups:
```sh
2018-12-26 19:23:13 +00:00
> echo "123.45" | sd '(?P<dollars>\d+)\.(?P<cents>\d+)' '$dollars dollars and $cents cents'
2018-12-24 03:57:36 +00:00
123 dollars and 45 cents
```
2018-12-26 00:01:11 +00:00
In the unlikely case you stumble upon ambiguities, resolve them by using `${var}` instead of `$var`. Here's an example:
2018-12-24 03:57:36 +00:00
```sh
2018-12-26 19:23:13 +00:00
> echo '123.45' | sd '(?P<dollars>\d+)\.(?P<cents>\d+)' '$dollars_dollars and $cents_cents'
2018-12-24 03:57:36 +00:00
and
2018-12-26 19:23:13 +00:00
> echo '123.45' | sd '(?P<dollars>\d+)\.(?P<cents>\d+)' '${dollars}_dollars and ${cents}_cents'
2018-12-24 03:57:36 +00:00
123_dollars and 45_cents
```
2018-12-26 00:01:11 +00:00
4. **Find & replace in a file**
2018-12-24 03:57:36 +00:00
```sh
2018-12-26 19:23:13 +00:00
> sd 'window.fetch' 'fetch' -i http.js
2018-12-24 03:57:36 +00:00
```
2018-12-26 00:01:11 +00:00
That's it. The file is modified in-place.
2018-12-24 03:57:36 +00:00
2018-12-26 00:01:11 +00:00
To do a dry run, just use stdin/stdout:
2018-12-24 03:57:36 +00:00
```sh
2018-12-26 19:23:13 +00:00
> sd 'window.fetch' 'fetch' < http.js
2018-12-24 03:57:36 +00:00
```
2018-12-24 04:01:20 +00:00
5. **Find & replace across project**
2018-12-24 03:57:36 +00:00
Good ol' unix philosophy to the rescue.
```sh
fd -t f --exec sd 'from "react"' 'from "preact"' -i {}
```
Same, but with backups (consider version control).
```bash
for file in $(fd -t f); do
cp "$file" "$file.bk"
sd 'from "react"' 'from "preact"' -i "$file";
done
```