2

I am trying to extract fields from a structure, for example.

  typedef struct newstruct {
  long id;            
  uint32_t vtid;      
  struct HN* next;
} HashNode;

I want to use sed/awk to extract structure name, followed by the fields with a delimiter as

newstruct HashNode: long id, uint_32 vtid, struct HN* next
2

It's fairly straightforward with awk, perhaps doable with sed.

With awk, you would have a state that gets set/reset on each typedef line, and concludes on each line with a right curly brace. A suitable awk script would look like

BEGIN {
    state = 0;
    typedef="";
    fields="";
}
/typedef[ ]+struct/{
    state = 1;
    typedef=$3;
    next;
}
/}.*;/ {
    if (state != 0) {
        sub("^.*}[  ]*","",$0);
        sub(";","",$0);
        sub(",$","",fields);
        printf "%s %s: %s\n", typedef, $0, fields;
        state = 0;
        fields = "";
        typedef = "";
    }
    next;
}
(state == 1){ 
    gsub("[     ]+"," ", $0);
    gsub(";",",",$0);
    fields = fields $0;
    next;
}

where the [ and ] brackets enclose a space and tab (to make it portable). There are four parts to the script:

  1. the BEGIN action initializes variables (not strictly necessary, but some awks do slightly different things with uninitialized variables)
  2. the pattern matching the line with typedef, followed by blank(s) and the word struct. That expects at least 3 fields on the line, using the third as the name of the typedef.
  3. a pattern to match the closing curly brace. Just in case your file has other stuff in it, the action checks if state was set. The $0 is the current line. The first substitution trims away everything before the word we're interested in, and the second trims away the semicolon following it. The third substitution changes a comma after the fields variable that came from the 4th action (below), to an empty string.
  4. a pattern matching all other lines when state is set. Like the previous action, this uses substitution to trim away the parts not wanted, first by reducing multiple blanks to a single blank, and then changing the trailing semicolon to a comma.

Call that file foo.awk, and your input data foo.in, to use awk like this:

awk -f foo.awk <foo.in

If you wanted to match lines like this:

struct foo {

rather than

typedef struct foo {

then the pattern could be written

/^([  ]*typedef)?[  ]+struct[  ]+/{

(again, with a literal space and tab in the square brackets). The parentheses mark a group and the question mark ? says to repeat that zero-or-more times. (The { on the line actually denotes the beginning of the action, but I left it there to match the line in the given script).

Further reading:

  • That is a very neat and flexible solution (taking into account possible multiple white spaces is thoughtful), but would it not be simpler to set RS="typedef" and FS=";|{" ? You can then directly loop on your fields with printf and send next when $i == /^[ ]*}.*;. Not sure this is worth a separate answer though. – Valentin B. Oct 24 '16 at 11:54
  • Thanks Thomas !. Could you kindly explain the third step, I didn't fully understand it – kris Oct 24 '16 at 16:39
  • How can I modify it to be generic: typedef struct newstruct or struct newstruct ? I modified 2nd part with /[typedef ]+struct/ but it does not work – kris Oct 25 '16 at 7:28
0
sed -rn '
/typedef struct ([[:alnum:]_]+)\s+\{/!b
s//\1/; h
:X
n 
/}\s+([[:alnum:]_]+)/{
    s//\1/
    H
    g
    s/;//g
    s/(.*)\n(.*)\n(.*)\n(.*)\n(.*)/\1 \5: \2, \3, \4/
    p;b
}
s/\s*(.+);\s*/\1/
H
bX
' file

newstruct HashNode: long id, uint32_t vtid, struct HN* next

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.