sebastiano.tronto.net

Source files and build scripts for my personal website
git clone https://git.tronto.net/sebastiano.tronto.net
Download | Log | Files | Refs | README

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.*