shell-ide-sed.md (3998B)
1 # The UNIX shell as an IDE: look stuff up with sed 2 3 Recently I have been working on [nissy](https://nissy.tronto.net), my 4 Rubik's cube solver written in C. It is a faily large project for me, 5 consisting of multiple files for a total of ~8k lines. 6 7 Something that I need to do quite often is quickly checking a structure's 8 or a function's definition. Using a simple text editor without 9 any plugin, my workflow for this at the moment is the following: 10 11 * Open a new terminal in the project's directory (one key-binding in 12 my terminal's configuration) 13 * Open the correct file with `vi src/file.c` (this can be tricky, 14 because I don't always remember in which file the object I am looking 15 for is defined) 16 * Search with `/` 17 18 This is not too bad, but I can do better using a short sed script! 19 20 ## Coding style 21 22 I write my functions like this: 23 24 ``` 25 static int 26 do_thing(int var) 27 { 28 function body... 29 } 30 ``` 31 32 The important part is that the function's name is at the start of the line. 33 In this way when I search for the function's definition I can type 34 `/^do_thing`. Here `^` stands for the beginning of the line, and it 35 is fairly standard across UNIX tools (sed, grep, ed...), so all other 36 uses of the funciton in the same file are ignored. 37 The other important thing is that the closing `}` is also at the beginning 38 of the line, but everyone in their right mind does that (I hope). 39 40 ## The sed command 41 42 If you are like me, 99% of the time you use sed it is to replace some text 43 with something like `sed 's/old text/new text/g`. 44 But this classic program can actually do more: it parses the input line by 45 line, applies a series of commands to each line that matches the given 46 address, and then prints the result. For example 47 48 ``` 49 $ sed '5,10 p' file.c 50 ``` 51 52 prints (`p`) the lines from 5 to 10 of `file.c`. Well, kind of: it prints 53 the whole file, but the lines from 5 to 10 are duplicated. This is because the 54 default behavior, applied to every line, is to do nothing and print the 55 (unmodified) input. We can change this behavior with the `-n` option: 56 57 ``` 58 $ sed -n '5,10 p' file.c 59 ``` 60 61 To print our `do_thing` function, we can find its address in the file using 62 the `/` search. The following command: 63 64 ``` 65 $ sed -n '/^do_/,/^}/ p' file.c 66 ``` 67 68 prints all the lines between one that starts with `/^do_/` and the first 69 one after that that starts with `}`. If you have another function called 70 `do_other_thing`, it will print that one too. 71 72 ## Turning it into a script 73 74 Of course typing all of this every time we want to check out a function from 75 our file is too complicated. So we want to turn this into a script that we 76 can easily call. 77 We will call it `cth`, for "see thing', where the `c` also reminds us 78 that it is based on C's syntax. 79 80 We may start with something like this: 81 82 ``` 83 #!/bin/sh 84 85 sed -n "/^$1/,/^}/ p" 86 ``` 87 88 Using double quotes instead of single quotes is necessary to have the `$1` 89 expand to the first argument. After saving our script to `cth` and making 90 it executable with `chmod +x cth`, we can call it with 91 92 ``` 93 $ ./cth do_ < file.c 94 ``` 95 96 We have to use `<` to redirect the standard input, because our script does 97 not read any other argument that could be interpreted as a file name. 98 To do this, we can do: 99 100 101 ``` 102 #!/bin/sh 103 104 name=$1 105 shift 106 sed -n "/^$name/,/^}/ p" $@ 107 ``` 108 109 This will save the first argument to a variable called `name`, "shift" the 110 list of arguments and pass every remaining argument to sed with `$@`. In this 111 way we can pass any number of file names. For example if we know our file 112 is in the `src` directory, but we do not remember what its name is, we can 113 [glob](https://en.wikipedia.org/wiki/Glob_(programming)) it with: 114 115 ``` 116 $ cth do_ src/* 117 ``` 118 119 And it works! You can now find 120 [this script](https://git.tronto.net/scripts/file/cth%2Ehtml) 121 among my other [scripts](https://git.tronto.net/scripts). 122 123 *Remark: in bash one can simply do `sed -n "/^$1/,/^}/ p" ${@:2}` to match 124 every argument from the second to the last, but this does not work in other 125 shells such as ksh.*