aboutsummaryrefslogtreecommitdiff
path: root/src/blog/2022-06-12-shell-ide-sed/shell-ide-sed.md
blob: bcb97d0e24f7c2ced97af3095631bc48cf1043e7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
# The UNIX shell as an IDE: look stuff up with sed

Recently I have been working on [nissy](https://nissy.tronto.net), my
Rubik's cube solver written in C. It is a faily large project for me,
consisting of multiple files for a total of ~8k lines.

Something that I need to do quite often is quickly checking a structure's
or a function's definition. Using a simple text editor without
any plugin, my workflow for this at the moment is the following:

* Open a new terminal in the project's directory (one key-binding in
  my terminal's configuration)
* Open the correct file with `vi src/file.c` (this can be tricky,
  because I don't always remember in which file the object I am looking
  for is defined)
* Search with `/`

This is not too bad, but I can do better using a short sed script!

## Coding style

I write my functions like this:

```
static int
do_thing(int var)
{
	function body...
}
```

The important part is that the function's name is at the start of the line.
In this way when I search for the function's definition I can type
`/^do_thing`. Here `^` stands for the beginning of the line, and it
is fairly standard across UNIX tools (sed, grep, ed...), so all other
uses of the funciton in the same file are ignored.
The other important thing is that the closing `}` is also at the beginning
of the line, but everyone in their right mind does that (I hope).

## The sed command

If you are like me, 99% of the time you use sed it is to replace some text
with something like `sed 's/old text/new text/g`.
But this classic program can actually do more: it parses the input line by
line, applies a series of commands to each line that matches the given
address, and then prints the result. For example

```
$ sed '5,10 p' file.c
```

prints (`p`) the lines from 5 to 10 of `file.c`. Well, kind of: it prints
the whole file, but the lines from 5 to 10 are duplicated. This is because the
default behavior, applied to every line, is to do nothing and print the
(unmodified) input. We can change this behavior with the `-n` option:

```
$ sed -n '5,10 p' file.c
```

To print our `do_thing` function, we can find its address in the file using
the `/` search. The following command:

```
$ sed -n '/^do_/,/^}/ p' file.c
```

prints all the lines between one that starts with `/^do_/` and the first
one after that that starts with `}`. If you have another function called
`do_other_thing`, it will print that one too.

## Turning it into a script

Of course typing all of this every time we want to check out a function from
our file is too complicated. So we want to turn this into a script that we
can easily call.
We will call it `cth`, for "see thing', where the `c` also reminds us
that it is based on C's syntax.

We may start with something like this:

```
#!/bin/sh

sed -n "/^$1/,/^}/ p"
```

Using double quotes instead of single quotes is necessary to have the `$1`
expand to the first argument. After saving our script to `cth` and making
it executable with `chmod +x cth`, we can call it with

```
$ ./cth do_ < file.c
```

We have to use `<` to redirect the standard input, because our script does
not read any other argument that could be interpreted as a file name.
To do this, we can do:


```
#!/bin/sh

name=$1
shift
sed -n "/^$name/,/^}/ p" $@
```

This will save the first argument to a variable called `name`, "shift" the
list of arguments and pass every remaining argument to sed with `$@`. In this
way we can pass any number of file names. For example if we know our file
is in the `src` directory, but we do not remember what its name is, we can
[glob](https://en.wikipedia.org/wiki/Glob_(programming)) it with:

```
$ cth do_ src/*
```

And it works! You can now find
[this script](https://git.tronto.net/scripts/tree/cth)
among my other [scripts](https://git.tronto.net/scripts).

*Remark: in bash one can simply do `sed -n "/^$1/,/^}/ p" ${@:2}` to match
every argument from the second to the last, but this does not work in other
shells such as ksh.*

Generated with cgit - Back to sebastiano.tronto.net