使用SED改造C结构和类型定义(Using sed to transform a C struct

2019-09-29 11:50发布

我在我的输入代码一对夫妇的结构定义。 例如:

struct node {
   int val;
   struct node *next;
};

要么

typedef struct {
   int numer;
   int denom;
} Rational;

我用下面的线将它们转换成一行,并复制它两次。

sed '/struct[^(){]*{/{:l N;s/\n//;/}[^}]*;/!t l;s/  */ /g;p;p}'

结果是这样的:

struct node { int val; struct node *next;};
struct node { int val; struct node *next;};
struct node { int val; struct node *next;};

typedef struct { int numer; int denom;} Rational;
typedef struct { int numer; int denom;} Rational;
typedef struct { int numer; int denom;} Rational;

这就是我要的:

  1. 我想的第一行被恢复到原来的结构块
  2. 我想第二行变成一个功能标题看起来像这样...

     void init_structName( structName *var, int data1, int data2 ) 

-structName基本结构的名称。

-var是你喜欢的任何名称。

-data1,数据2 ....是是在结构中的值。

3.我想第三行变成以函数体。 我在哪里初始化的数据参数。 它是这样的。

    {
        var->data1 = data1;
        var->data2 = data2;
    }

请记住,在输入文件中的所有我的结构定义放置在一个行,抄三次。 因此,当代码发现的结构确定指标可以假定会有以下两个副本。

例如,这是我想如果输入文件具有上面所示的重复的线的输出。

struct node {
   int val;
   struct node *next;
};
void init_node(struct node *var, int val, struct node *next)
{
var->val = val;
var->next =  next;
}

typedef struct {
   int numer;
   int denom;
} Rational;
void init_Rational( Rational *var, int numer, int denom ) 
{
   var->numer = numer;
   var->denom = denom;
}

万一有人很好奇。 这些功能将在主函数中调用初始化结构变量。

有人能帮忙吗? 我意识到这是一种艰难的。 非常感谢!!

Answer 1:

眼看sed的是图灵完备 ,可以做到在一个单一的去,但是,这并不意味着该代码是非常用户友好=)

我的一个解决方案,企图将是:

#!/bin/sed -nf

/struct/b continue
p
d

: continue

# 1st step:
s/\(struct\s.*{\)\([^}]*\)\(}.*\)/\1\
\2\
\3/
s/;\(\s*[^\n}]\)/;\
\1/g
p

s/.*//
n
# 2nd step:
s/struct\s*\([A-Za-z_][A-Za-z_0-9]*\)\s*{\([^}]*\)}.*/void init_\1(struct \1 *var, \2)/
s/typedef\s*struct\s*{\([^}]*\)}\s*\([A-Za-z_][A-Za-z_0-9]*\)\s*;/void init_\2(struct \2 *var, \1)/
s/;/,/g
s/,\s*)/)/
p

s/.*//
n
# 3rd step
s/.*{\s*\([^}]*\)}.*/{\
\1}/
s/[A-Za-z \t]*[\* \t]\s*\([A-Za-z_][A-Za-z_0-9]*\)\s*;/\tvar->\1 = \1;\
/g
p

我会尽力解释一切,我做到了,但首先我必须警告说,这可能还不是很普遍。 例如,它假定三个相同线彼此跟随(即,它们之间没有其他的线)。

在开始之前,请注意,该文件是要求“-n”标志运行的脚本。 这告诉sed不打印任何东西到标准输出,除非剧本明确它告诉给(通过“p”命令,例如)。 在“-f”选项是一个“绝招”告诉sed来打开后面的文件。 当“./myscript.sed”执行脚本,bash将执行‘/ bin中/ SED -nf myscript.sed’,所以它会正确地读剧本的其余部分。

步骤零将只是一个检查,看看,如果我们有一个有效的线。 我假设每一个有效行包含字结构。 如果该行是有效的,脚本分支(跳跃,在“B”命令等同于C中的goto语句)将继续标签(不同于C,标签开始“:”,而不是与它在结束)。 如果它不是有效的,我们迫使它与所述“p”命令被印刷,然后删除从图案空间中的线与“d”的命令。 通过删除线,SED将读取下一行并从头开始执行脚本。

如果该行是有效的,行动改变行开始。 第一步骤是生成该结构体。 这是通过一系列命令来完成。

  1. 分开线分成三个部分,一切到开口支架,一切到右括号(但不包括它),以及一切从关闭托架(现在包括它)。 我要指出的sed的怪癖之一是我们搜索为“\ n”换行,但有一个“\”后跟实际换行写换行符。 这就是为什么这个命令被分成三个不同的线路。 IIRC这种行为是特定于POSIX sed的,但可能是GNU版本(目前在大多数Linux发行版),允许写为“\ n”一个换行符。
  2. 每个分号后面添加一个换行符。 该这部作品是一个有点尴尬,我们分号之后插入新行后的分号后复制的一切。 G标志告诉sed反复这样做,这就是为什么它的工作原理。 再次还要注意换行符逃逸。
  3. 强制结果将被打印

在第二步骤之前,我们手动清除从该图案空间(即缓存器)的线,因此,我们可以开始新鲜下一行。 如果我们这样做的“d”命令,sed的将开始从文件的开头再次读取命令。 “n”个命令,然后读取下一行到图案空间。 在那之后,我们开始的命令行转变为一个函数的声明:

  1. 我们首先匹配字结构,接着是零个或更多的白色空间,然后是C标识符可以与下划线或字母开始,并且可以包含下划线和字母数字字符。 所述标识符被捕获到“可变”“\ 1”。 然后,我们匹配括号内的内容,并保存在“\ 2”。 然后,这些用来生成函数声明。
  2. 然后,我们做同样的过程,但现在对于“类型定义”的情况。 注意,现在标识符是括号之后,所以“\ 1”现在包含括号内的内容和“\ 2”包含标识符。
  3. 现在,我们用逗号替换所有分号,所以它可以开始寻找更象一个函数的定义。
  4. 最后替换命令删除右括号之前额外的逗号。
  5. 最后打印出结果。

再次,最后一步之前,手动清洁图案空间和读取下一行。 然后一步将生成函数体:

  1. 匹配和捕获的括号内的一切。 请注意“ *”的开括号之前和之后的右括号。 这是用来因此只有括号中的内容被写入之后。 当写输出,我们放在单独的行打开支架。
  2. 我们匹配字母字符和空格,这样我们就可以跳过类型声明。 我们需要至少一个空白字符或星号(用于指针)来标记该标识符的开始。 然后我们继续捕捉标识。 这不仅是因为接下来的拍摄工作:我们明确要求,标识后,只有可选的空格之后分号。 这迫使表达分号之前得到的标识字符,即。 如果有两个以上的词,它只会得到最后一个字。 因此,将与“无符号整型VAR”工作,拍摄“变种”正确。 当写输出,我们把一些缺口,随后需要的格式,包括逃脱换行符。
  3. 打印最终输出。

我不知道如果我是不够清楚。 随意问任何澄清。

希望这有助于=)



Answer 2:

这应该给你如何不恰当的实际sed的是这类任务的一些技巧。 我无法弄清楚如何做到这一点的一个传球和我写完剧本的时候,我注意到你期待有所不同的结果。

你的问题是更适合的脚本语言和解析库。 考虑蟒蛇+ pyparsing( 这里有一个例子C结构解析语法,但你需要的东西比这更简单)或perl6的规则 。

不过,这也许会有些用的,如果你决定要坚持用sed:

pass-one.sh

#!/bin/sed -nf

/^struct/ {
  s|^\(struct[^(){]*{\)|\1\n|
  s|[^}];|;\n|gp
  a \\n
}

/^typedef/ {
  h
  # create signature
  s|.*{\(.*\)} \(.*\);|void init_\2( \2 *var, \1 ) {|
  # insert argument list to signature and remove trailing ;
  s|\([^;]*\); ) {|\1 ) {|g
  s|;|,|g
  p

  g
  # add constructor (further substitutions follow in pass-two)
  s|.*{\(.*\)}.*|\1|
  s|;|;\n|g
  s|\n$||p

  a }
  a \\n
}

pass-two.sh

#!/bin/sed -f

# fix struct indent
/^struct/ {
  :loop1
  n
  s|^ |    |
  t loop1
}

# unsigned int name -> var->name = name
/^void init_/{
  :loop2
  n
  s|.* \(.*\);|    var->\1 = \1;|
  t loop2
}

Usage

$ cat << EOF | ./pass-one.sh | ./pass-two.sh
struct node { int val; struct node *next;};
typedef struct { int numer; int denom;} Rational;

struct node { int val; struct node *next;};
typedef struct { int numer; unsigned int denom;} Rational;
EOF
struct node {
    int va;
    struct node *nex;
};


void init_Rational( Rational *var,  int numer, int denom ) {
    var->numer = numer;
    var->denom = denom;
}


struct node {
    int va;
    struct node *nex;
};


void init_Rational( Rational *var,  int numer, unsigned int denom ) {
    var->numer = numer;
    var->denom = denom;
}


文章来源: Using sed to transform a C struct and typedef
标签: shell unix sed