skills: add shell-file-editing
Recipes for ed, sed, and ssam covering the common file editing patterns. Intended to be loaded when modifying files via execute_code now that file_read/file_write have been removed. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
ccc6f4d00c
commit
7da6494608
|
|
@ -0,0 +1,336 @@
|
||||||
|
---
|
||||||
|
name: shell-file-editing
|
||||||
|
intent: file editing, text editing, ed, sed, ssam
|
||||||
|
description: Recipes for reading and editing files using standard Unix tools (ed, sed) and plan9port ssam. Use when modifying files via execute_code.
|
||||||
|
user-invocable: false
|
||||||
|
---
|
||||||
|
|
||||||
|
# Shell File Editing
|
||||||
|
|
||||||
|
Use `execute_code` with standard tools. Never guess line numbers — always grep first.
|
||||||
|
|
||||||
|
## Locate Before Editing
|
||||||
|
|
||||||
|
```sh
|
||||||
|
grep -n 'pattern' file # line numbers for matches
|
||||||
|
cat -n file # full file with line numbers
|
||||||
|
grep -n 'pattern' file | head # narrow to area of interest
|
||||||
|
```
|
||||||
|
|
||||||
|
## ed — Scripted Line Editing
|
||||||
|
|
||||||
|
`ed` operates on whole lines. Drive it with a heredoc; use `-s` to suppress noise.
|
||||||
|
|
||||||
|
### Address syntax
|
||||||
|
|
||||||
|
```
|
||||||
|
n line number (1-based)
|
||||||
|
$ last line
|
||||||
|
. current line (last affected)
|
||||||
|
, whole file (shorthand for 1,$)
|
||||||
|
/pattern/ next line matching pattern
|
||||||
|
?pattern? previous line matching pattern
|
||||||
|
n,m line range inclusive
|
||||||
|
/pat1/,/pat2/ range from first match to second
|
||||||
|
```
|
||||||
|
|
||||||
|
### Substitute on a matching line
|
||||||
|
|
||||||
|
```sh
|
||||||
|
ed -s file <<'EOF'
|
||||||
|
/pattern/s/old/new/
|
||||||
|
w
|
||||||
|
q
|
||||||
|
EOF
|
||||||
|
```
|
||||||
|
|
||||||
|
### Global substitute (all occurrences in file)
|
||||||
|
|
||||||
|
```sh
|
||||||
|
ed -s file <<'EOF'
|
||||||
|
,s/old/new/g
|
||||||
|
w
|
||||||
|
q
|
||||||
|
EOF
|
||||||
|
```
|
||||||
|
|
||||||
|
### Delete lines matching a pattern
|
||||||
|
|
||||||
|
```sh
|
||||||
|
ed -s file <<'EOF'
|
||||||
|
g/pattern/d
|
||||||
|
w
|
||||||
|
q
|
||||||
|
EOF
|
||||||
|
```
|
||||||
|
|
||||||
|
### Delete a line range
|
||||||
|
|
||||||
|
```sh
|
||||||
|
ed -s file <<'EOF'
|
||||||
|
5,10d
|
||||||
|
w
|
||||||
|
q
|
||||||
|
EOF
|
||||||
|
```
|
||||||
|
|
||||||
|
### Insert text before a matching line
|
||||||
|
|
||||||
|
```sh
|
||||||
|
ed -s file <<'EOF'
|
||||||
|
/pattern/i
|
||||||
|
new line of text
|
||||||
|
.
|
||||||
|
w
|
||||||
|
q
|
||||||
|
EOF
|
||||||
|
```
|
||||||
|
|
||||||
|
### Append text after a matching line
|
||||||
|
|
||||||
|
```sh
|
||||||
|
ed -s file <<'EOF'
|
||||||
|
/pattern/a
|
||||||
|
new line of text
|
||||||
|
.
|
||||||
|
w
|
||||||
|
q
|
||||||
|
EOF
|
||||||
|
```
|
||||||
|
|
||||||
|
### Replace a range of lines
|
||||||
|
|
||||||
|
```sh
|
||||||
|
ed -s file <<'EOF'
|
||||||
|
/start_pattern/,/end_pattern/c
|
||||||
|
replacement line 1
|
||||||
|
replacement line 2
|
||||||
|
.
|
||||||
|
w
|
||||||
|
q
|
||||||
|
EOF
|
||||||
|
```
|
||||||
|
|
||||||
|
### Replace exact line range
|
||||||
|
|
||||||
|
```sh
|
||||||
|
ed -s file <<'EOF'
|
||||||
|
3,7c
|
||||||
|
new content
|
||||||
|
.
|
||||||
|
w
|
||||||
|
q
|
||||||
|
EOF
|
||||||
|
```
|
||||||
|
|
||||||
|
### Append at beginning of file (address 0)
|
||||||
|
|
||||||
|
```sh
|
||||||
|
ed -s file <<'EOF'
|
||||||
|
0a
|
||||||
|
first line
|
||||||
|
.
|
||||||
|
w
|
||||||
|
q
|
||||||
|
EOF
|
||||||
|
```
|
||||||
|
|
||||||
|
### Move lines
|
||||||
|
|
||||||
|
```sh
|
||||||
|
ed -s file <<'EOF'
|
||||||
|
5,8m12
|
||||||
|
w
|
||||||
|
q
|
||||||
|
EOF
|
||||||
|
```
|
||||||
|
|
||||||
|
### Print without modifying (verify before write)
|
||||||
|
|
||||||
|
```sh
|
||||||
|
ed -s file <<'EOF'
|
||||||
|
,p
|
||||||
|
q
|
||||||
|
EOF
|
||||||
|
```
|
||||||
|
|
||||||
|
### Multi-step script
|
||||||
|
|
||||||
|
```sh
|
||||||
|
ed -s file <<'EOF'
|
||||||
|
/^func foo/,/^}/c
|
||||||
|
func foo(x int) int {
|
||||||
|
return x * 2
|
||||||
|
}
|
||||||
|
.
|
||||||
|
,s/OldName/NewName/g
|
||||||
|
w
|
||||||
|
q
|
||||||
|
EOF
|
||||||
|
```
|
||||||
|
|
||||||
|
### Key points
|
||||||
|
|
||||||
|
- Input mode (`a`, `i`, `c`) ends with a lone `.` on its own line
|
||||||
|
- `g/re/cmd` runs cmd on every matching line; useful with `d`, `s`, `p`
|
||||||
|
- `wq` writes and quits in one command
|
||||||
|
- Substitute delimiter can be any non-space non-newline character: `s|/path/a|/path/b|`
|
||||||
|
- `\n` in replacement introduces a newline (splits the line)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## sed — Stream Substitution
|
||||||
|
|
||||||
|
Best for simple in-place substitutions. Single-pass; cannot insert multi-line
|
||||||
|
blocks as easily as ed.
|
||||||
|
|
||||||
|
### In-place substitution (GNU sed)
|
||||||
|
|
||||||
|
```sh
|
||||||
|
sed -i 's/old/new/g' file
|
||||||
|
```
|
||||||
|
|
||||||
|
### In-place on multiple files
|
||||||
|
|
||||||
|
```sh
|
||||||
|
sed -i 's/old/new/g' *.go
|
||||||
|
```
|
||||||
|
|
||||||
|
### Substitute only on lines matching a pattern
|
||||||
|
|
||||||
|
```sh
|
||||||
|
sed -i '/pattern/s/old/new/' file
|
||||||
|
```
|
||||||
|
|
||||||
|
### Delete lines matching a pattern
|
||||||
|
|
||||||
|
```sh
|
||||||
|
sed -i '/pattern/d' file
|
||||||
|
```
|
||||||
|
|
||||||
|
### Delete a line range
|
||||||
|
|
||||||
|
```sh
|
||||||
|
sed -i '5,10d' file
|
||||||
|
```
|
||||||
|
|
||||||
|
### Print a line range (no modification)
|
||||||
|
|
||||||
|
```sh
|
||||||
|
sed -n '10,20p' file
|
||||||
|
```
|
||||||
|
|
||||||
|
### Multiple expressions
|
||||||
|
|
||||||
|
```sh
|
||||||
|
sed -i -e 's/foo/bar/g' -e 's/baz/qux/g' file
|
||||||
|
```
|
||||||
|
|
||||||
|
### Address forms
|
||||||
|
|
||||||
|
```
|
||||||
|
n line n
|
||||||
|
$ last line
|
||||||
|
/regexp/ lines matching regexp
|
||||||
|
n,m range
|
||||||
|
/a/,/b/ range between patterns
|
||||||
|
n,+k line n and k lines after it
|
||||||
|
```
|
||||||
|
|
||||||
|
### Key points
|
||||||
|
|
||||||
|
- GNU sed: `-i` with no suffix. BSD sed: `-i ''`. Prefer `ed` for portability.
|
||||||
|
- sed makes one pass; edits to earlier lines do not affect later address matching
|
||||||
|
- Use `ed` when inserting or replacing blocks of multiple lines
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ssam — Structural Editing (plan9port)
|
||||||
|
|
||||||
|
`ssam` uses sam's command language. Selects structurally (by regex), not by
|
||||||
|
line number. Entire input is loaded before the script runs.
|
||||||
|
|
||||||
|
Check availability: `command -v ssam`
|
||||||
|
|
||||||
|
### Basic syntax
|
||||||
|
|
||||||
|
```
|
||||||
|
ssam 'address command' file
|
||||||
|
ssam -n 'address command' file # suppress default output
|
||||||
|
```
|
||||||
|
|
||||||
|
On invocation, the entire file is selected (dot = `,`).
|
||||||
|
|
||||||
|
### Address syntax
|
||||||
|
|
||||||
|
```
|
||||||
|
, whole file
|
||||||
|
n line n
|
||||||
|
/regexp/ next match of regexp
|
||||||
|
?regexp? previous match
|
||||||
|
#n character offset n (#0 = start of file)
|
||||||
|
$ end of file
|
||||||
|
. current selection (dot)
|
||||||
|
a1,a2 from start of a1 to end of a2
|
||||||
|
```
|
||||||
|
|
||||||
|
### Print first 10 lines
|
||||||
|
|
||||||
|
```sh
|
||||||
|
ssam -n ',10p' file
|
||||||
|
```
|
||||||
|
|
||||||
|
### Global substitution
|
||||||
|
|
||||||
|
```sh
|
||||||
|
ssam 's/old/new/g' file
|
||||||
|
```
|
||||||
|
|
||||||
|
### Substitute within lines matching a pattern
|
||||||
|
|
||||||
|
```sh
|
||||||
|
ssam 'x/.*pattern.*\n/ s/old/new/' file
|
||||||
|
```
|
||||||
|
|
||||||
|
### Delete empty lines
|
||||||
|
|
||||||
|
```sh
|
||||||
|
ssam 's/\n\n+/\n/g' file
|
||||||
|
```
|
||||||
|
|
||||||
|
### Print one word per line
|
||||||
|
|
||||||
|
```sh
|
||||||
|
ssam -n 'y/[a-zA-Z]+/ c/\n/' file
|
||||||
|
```
|
||||||
|
|
||||||
|
### Structural loop: run command on each match
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# x selects each match of the pattern, then runs the sub-command on it
|
||||||
|
ssam 'x/pattern/ c/replacement/' file
|
||||||
|
```
|
||||||
|
|
||||||
|
### Conditional: run command only if range contains match
|
||||||
|
|
||||||
|
```sh
|
||||||
|
ssam 'g/pattern/ s/old/new/' file # only lines containing pattern
|
||||||
|
ssam 'v/pattern/ s/old/new/' file # only lines NOT containing pattern
|
||||||
|
```
|
||||||
|
|
||||||
|
### Key points
|
||||||
|
|
||||||
|
- ssam consumes all of stdin before running; safe for in-place style pipelines
|
||||||
|
- `x/re/ cmd` iterates over all non-overlapping matches; nested `x` is legal
|
||||||
|
- `y/re/ cmd` iterates over the text *between* matches
|
||||||
|
- `g/re/ cmd` and `v/re/ cmd` are guards (if-contains / if-not-contains)
|
||||||
|
- To write back in place: `ssam 'script' file > tmp && mv tmp file`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Workflow
|
||||||
|
|
||||||
|
1. Locate with `grep -n` or `cat -n`
|
||||||
|
2. Preview the change with `ed -s file <<'EOF'\n,p\nq\nEOF` or `sed -n`
|
||||||
|
3. Apply the edit
|
||||||
|
4. Verify with `grep` or `cat -n` that the change landed correctly
|
||||||
Loading…
Reference in New Issue