{"id":13395170,"url":"https://github.com/chmln/sd","last_synced_at":"2025-05-13T17:04:07.130Z","repository":{"id":38361216,"uuid":"162863623","full_name":"chmln/sd","owner":"chmln","description":"Intuitive find \u0026 replace CLI (sed alternative)","archived":false,"fork":false,"pushed_at":"2025-04-18T23:12:38.000Z","size":424,"stargazers_count":6249,"open_issues_count":66,"forks_count":142,"subscribers_count":28,"default_branch":"master","last_synced_at":"2025-05-06T16:13:40.782Z","etag":null,"topics":["cli","command-line","regex","rust","terminal","text-processing"],"latest_commit_sha":null,"homepage":"","language":"Rust","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/chmln.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","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,"zenodo":null}},"created_at":"2018-12-23T05:00:16.000Z","updated_at":"2025-05-06T10:16:15.000Z","dependencies_parsed_at":"2023-10-24T02:42:10.359Z","dependency_job_id":"b9e42d36-013f-4d05-a494-fe81b73ac047","html_url":"https://github.com/chmln/sd","commit_stats":{"total_commits":268,"total_committers":37,"mean_commits":7.243243243243243,"dds":"0.42910447761194026","last_synced_commit":"2d287b9b1c21d6ca21e649f9d97e0b5e9e542e12"},"previous_names":[],"tags_count":24,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chmln%2Fsd","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chmln%2Fsd/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chmln%2Fsd/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chmln%2Fsd/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/chmln","download_url":"https://codeload.github.com/chmln/sd/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253990452,"owners_count":21995773,"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","command-line","regex","rust","terminal","text-processing"],"created_at":"2024-07-30T17:01:44.927Z","updated_at":"2025-05-13T17:04:07.066Z","avatar_url":"https://github.com/chmln.png","language":"Rust","readme":"# sd - `s`earch \u0026 `d`isplace\n\n`sd` is an intuitive find \u0026 replace CLI.\n\n## The Pitch\n\nWhy use it over any existing tools?\n\n*Painless regular expressions.* \u0026nbsp; `sd` uses regex syntax that you already know from JavaScript and Python. Forget about dealing with quirks of `sed` or `awk` - get productive immediately.\n\n*String-literal mode.* \u0026nbsp; Non-regex find \u0026 replace. No more backslashes or remembering which characters are special and need to be escaped.\n\n*Easy to read, easy to write.* \u0026nbsp; Find \u0026 replace expressions are split up, which makes them easy to read and write. No more messing with unclosed and escaped slashes.\n\n*Smart, common-sense defaults.* \u0026nbsp; Defaults follow common sense and are tailored for typical daily use.\n\n## Comparison to sed\n\nWhile sed does a whole lot more, sd focuses on doing just one thing and doing it well. Here are some cherry-picked examples where sd shines.\n\nSimpler syntax for replacing all occurrences:\n  - sd: `sd before after`\n  - sed: `sed s/before/after/g`\n\nReplace newlines with commas:\n  - sd: `sd '\\n' ','`\n  - sed: `sed ':a;N;$!ba;s/\\n/,/g'`\n\nExtracting stuff out of strings containing slashes:\n  - sd: `echo \"sample with /path/\" | sd '.*(/.*/)' '$1'`\n  - sed: `echo \"sample with /path/\" | sed -E 's/.*(\\\\/.*\\\\/)/\\1/g'`\n    \n    With sed, you can make it better with a different delimiter,\n    but it is still messy:\n    \n    `echo \"sample with /path/\" | sed -E 's|.*(/.*/)|\\1|g'`\n\nIn place modification of files:\n  - sd: `sd before after file.txt`\n  - sed: `sed -i -e 's/before/after/g' file.txt`\n    \n    With sed, you need to remember to use `-e` or else some\n    platforms will consider the next argument to be a backup suffix.\n\n## Benchmarks\n\n**Simple replacement on ~1.5 gigabytes of JSON**\n\n```sh\nhyperfine --warmup 3 --export-markdown out.md \\\n  'sed -E \"s/\\\"/'\"'\"'/g\" *.json \u003e /dev/null' \\\n  'sed    \"s/\\\"/'\"'\"'/g\" *.json \u003e /dev/null' \\\n  'sd     \"\\\"\" \"'\"'\"'\"   *.json \u003e /dev/null'\n```\n\n| Command | Mean [s] | Min…Max [s] |\n|:---|---:|---:|\n| `sed -E \"s/\\\"/'/g\" *.json \u003e /dev/null` | 2.338 ± 0.008 | 2.332…2.358 |\n| `sed    \"s/\\\"/'/g\" *.json \u003e /dev/null` | 2.365 ± 0.009 | 2.351…2.378 |\n| `sd     \"\\\"\" \"'\"   *.json \u003e /dev/null` | **0.997 ± 0.006** | 0.987…1.007 |\n\nResult: ~2.35 times faster\n\n**Regex replacement on a ~55M json file**:\n\n```sh\nhyperfine --warmup 3 --export-markdown out.md \\\n  'sed -E \"s:(\\w+):\\1\\1:g\"    dump.json \u003e /dev/null' \\\n  'sed    \"s:\\(\\w\\+\\):\\1\\1:g\" dump.json \u003e /dev/null' \\\n  'sd     \"(\\w+)\" \"$1$1\"      dump.json \u003e /dev/null'\n```\n\n| Command | Mean [s] | Min…Max [s] |\n|:---|---:|---:|\n| `sed -E \"s:(\\w+):\\1\\1:g\"    dump.json \u003e /dev/null` | 11.315 ± 0.215 | 11.102…11.725 |\n| `sed    \"s:\\(\\w\\+\\):\\1\\1:g\" dump.json \u003e /dev/null` | 11.239 ± 0.208 | 11.057…11.762 |\n| `sd     \"(\\w+)\" \"$1$1\"      dump.json \u003e /dev/null` | **0.942 ± 0.004** | 0.936…0.951 |\n\nResult: ~11.93 times faster\n\n## Installation\n\nInstall through\n[`cargo`](https://doc.rust-lang.org/cargo/getting-started/installation.html) with\n`cargo install sd`, or through various package managers\n\n[![Packaging status](https://repology.org/badge/vertical-allrepos/sd-find-replace.svg?exclude_unsupported=1)](https://repology.org/project/sd-find-replace/versions)\n\n## Quick Guide\n\n1. **String-literal mode**. By default, expressions are treated as regex. Use `-F` or `--fixed-strings` to disable regex.\n\n   ```sh\n   \u003e echo 'lots((([]))) of special chars' | sd -F '((([])))' ''\n   lots of special chars\n   ```\n\n2. **Basic regex use** - let's trim some trailing whitespace\n\n   ```sh\n   \u003e echo 'lorem ipsum 23   ' | sd '\\s+$' ''\n   lorem ipsum 23\n   ```\n\n3. **Capture groups**\n\n   Indexed capture groups:\n\n   ```sh\n   \u003e echo 'cargo +nightly watch' | sd '(\\w+)\\s+\\+(\\w+)\\s+(\\w+)' 'cmd: $1, channel: $2, subcmd: $3'\n   cmd: cargo, channel: nightly, subcmd: watch\n   ```\n\n   Named capture groups:\n\n   ```sh\n   \u003e echo \"123.45\" | sd '(?P\u003cdollars\u003e\\d+)\\.(?P\u003ccents\u003e\\d+)' '$dollars dollars and $cents cents'\n   123 dollars and 45 cents\n   ```\n\n   In the unlikely case you stumble upon ambiguities, resolve them by using `${var}` instead of `$var`. Here's an example:\n\n   ```sh\n   \u003e echo '123.45' | sd '(?P\u003cdollars\u003e\\d+)\\.(?P\u003ccents\u003e\\d+)' '$dollars_dollars and $cents_cents'\n    and\n\n   \u003e echo '123.45' | sd '(?P\u003cdollars\u003e\\d+)\\.(?P\u003ccents\u003e\\d+)' '${dollars}_dollars and ${cents}_cents'\n   123_dollars and 45_cents\n   ```\n\n4. **Find \u0026 replace in a file**\n\n   ```sh\n   \u003e sd 'window.fetch' 'fetch' http.js\n   ```\n\n   That's it. The file is modified in-place.\n\n   To preview changes:\n\n   ```sh\n   \u003e sd -p 'window.fetch' 'fetch' http.js\n   ```\n\n5. **Find \u0026 replace across project**\n\n   This example uses [fd](https://github.com/sharkdp/fd).\n\n   Good ol' unix philosophy to the rescue.\n\n   ```sh\n   fd --type file --exec sd 'from \"react\"' 'from \"preact\"'\n   ```\n\n   Same, but with backups (consider version control).\n\n   ```bash\n   fd --type file --exec cp {} {}.bk \\; --exec sd 'from \"react\"' 'from \"preact\"'\n   ```\n\n### Edge cases\nsd will interpret every argument starting with `-` as a (potentially unknown) flag.\nThe common convention of using `--` to signal the end of flags is respected:\n\n```bash\n$ echo \"./hello foo\" | sd \"foo\" \"-w\"\nerror: Found argument '-w' which wasn't expected, or isn't valid in this context\n\nUSAGE:\n    sd [OPTIONS] \u003cfind\u003e \u003creplace-with\u003e [files]...\n\nFor more information try --help\n$ echo \"./hello foo\" | sd \"foo\" -- \"-w\"\n./hello -w\n$ echo \"./hello --foo\" | sd -- \"--foo\" \"-w\"\n./hello -w\n```\n\n### Escaping special characters\nTo escape the `$` character, use `$$`:\n\n```bash\n❯ echo \"foo\" | sd 'foo' '$$bar'\n$bar\n```\n","funding_links":[],"categories":["Rust","Command Line Tools","Other","Text Processor","Text Processing","Command Line","Shell/Terminal","Tools","语言资源库","Sed Replacement","命令行工具","cli","Applications","\u003ca name=\"text-search-replace\"\u003e\u003c/a\u003eText search and replace (alternatives to sed)","Modern Unix Replacements","\\*nix/\\*nux","Table of contents","Command line - Linux"],"sub_categories":["Dependency Management","Rust","rust","Open USP Tsukubai","System tools","Text processing","Searching","CLI","Learning resources"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchmln%2Fsd","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fchmln%2Fsd","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchmln%2Fsd/lists"}