asm-offset.h和sed

 

asm-offset.h 生成过程

           asm-offset.h 文件是在内核编译过程中生成的,生成过程  之前整理了一份笔记 TASK_threadsp的实现及asm-offsets.h,    其中涉及sed处理包含在内核代码 /scripts/Makefile.lib的sed-offsets中,工作是将生成的asm-offset.s转化为asm-offset.h。

/scripts/Makefile.lib
...
# ASM offsets
# ---------------------------------------------------------------------------

# Default sed regexp - multiline due to syntax constraints
#
# Use [:space:] because LLVM's integrated assembler inserts <tab> around
# the .ascii directive whereas GCC keeps the <space> as-is.
define sed-offsets
	's:^[[:space:]]*\.ascii[[:space:]]*"\(.*\)".*:\1:; \
	/^->/{s:->#\(.*\):/* \1 */:; \
	s:^->\([^ ]*\) [\$$#]*\([^ ]*\) \(.*\):#define \1 \2 /* \3 */:; \
	s:->::; p;}'
endef

 

sed用法整理:

  •  命令行参数

    • -n  : 禁止自动打印
    • -e   :支持多行匹配输入
  • 用s命令替换

s 命令(替代命令)是sed中最重要的一个命令,而且还支持很多不同选项,

    • 语法

s/regexp/replacement/flags

            将pattern space 与regexp进行匹配,匹配成功后进行替换。

    •   匹配:

             圆括号:replacement部分可以包含 \ n(n是1到9的数字,包括1和9,\1,\2,...\9),对应regexp中圆括号之间的匹配部分,pattern space 中第一个匹配的项标识为\1   。

             &符号:  replacement部分可以包含未转义的&字符。&表示正匹配的

    •  分隔符:

         s命令后的任意单个符号可以替换分隔符 '/',做为新的分隔符 , '/' 如果要出现在regexp或replacement时前面需要增加'\'符号。

    •  flags:

常见的几个flags:

  g:替换所有匹配条目 

  p:    打印 pattern space中的信息

               w filename:将结果输出到文件

  • 正则表达式

                sed手册第5章对正则表达式有详细的描述,常见如下:    

特殊字符描述
 ^      匹配输入字符串开始的位置
$     匹配输入字符串结尾的位置
 * 匹配0个或多个在*之前的表达式
\(regexp\) 1. 后缀运算符符号, 如 \(abcd\)* 将寻找0个或多个“abcd”整个字符序列
2. 反向引用
[list]匹配[]内的字符集合
[^list] 表示不匹配[]内的字符串
.*匹配所有字符串,包含空
.\+匹配所有字符串,至少包含一个字符
\$匹配单个$符号
[:alnum:] 字母数字
[:digit:]数字
[:space:]制表符(\t) 换行符(\n) 垂直制表符(\v) 换页符(\f) 回车符(\r) 空格(\s)。

           针对  Bracket Expressions , 如[:space:] , [:alnum:] 在使用时需要增加个[], 对sed新版本不增加的话会报错,对于老版本可以不加, 匹配的是[]中的集合, 非常幸运, 我的是老版本:),验证一下。 

## 1. 输入1, 不包含在“:digit:”中直接输出
[root@centosgpt generate]# echo 1|sed ' s/[:digit:]/X/'
1

## 2. 输入d, 包含在“:digit:”中替换为X
[root@centosgpt generate]# echo d|sed ' s/[:digit:]/X/'
X

##  3. 修改正则表达式数字1被替换为X
[root@centosgpt generate]# echo 1|sed ' s/[[:digit:]]/X/'
X
##  4. 改为输入d, 匹配失败,输出X
[root@centosgpt generate]# echo d |sed ' s/[[:digit:]]/X/'
d

## 下面是sed手册里关于新版本Bracket Expressions为增加[]的报错信息
# current GNU sed versions - incorrect usage rejected
$ echo 1 | sed 's/[:digit:]/X/'
sed: character class syntax is [[:space:]], not [:space:]
  • 用#命令注释

#符号用来标识注释的开始

  • 用q命令退出

退出不再处理其他指令

  • 用d命令删除

删除模式空间,进入下一个周期

  • 用p命令打印

打印模式空间一般与-n一起使用 -n将不再自动输出pattern space

  • 用n命令跳过单行

跳过单行

  • 用{command}命令组合

设置一组命令

## 1. 使用q命令退出
[root@centosgpt generate]# seq 3| sed 2q
1
2

## 2. 使用d命令删除
[root@centosgpt generate]# seq 3| sed 2d
1
3

## 3. 使用n命令跳过使用p打印
[root@centosgpt generate]# seq 6|sed -n  'n;n;p'
3
6

 

sed-offsets分析:

 下面是asm-offset.s片段 ,通过sed命令将解析成宏定义头文件

.ascii "->"
# 0 "" 2
# 16 "asm-offsets.c" 1

.ascii "->#TASK_threadsp1"
# 0 "" 2
# 17 "asm-offsets.c" 1

.ascii "->TASK_threadsp1 $0 offsetof(struct task_struct1, thread.sp)"
# 0 "" 2
# 18 "asm-offsets.c" 1

.ascii "->"
# 0 "" 2
#NO_APP

 

sed-offsets主体部分:

's:^[[:space:]]*\.ascii[[:space:]]*"\(.*\)".*:\1:; \
/^->/{s:->#\(.*\):/* \1 */:; \
s:^->\([^ ]*\) [\$#]*\([^ ]*\) \(.*\):#define \1 \2 /* \3 */:; \
s:->::; p;}'
  •  第一条指令属于替换指令:

 s:^[[:space:]]*\.ascii[[:space:]]*"\(.*\)".*:\1:;

              regexp部分分为四部分

  1. ^[[:space:]]*      匹配0个或多个以空格,制表符,回车换行开头的行;
  2.  \.ascii     匹配 ".ascii" 字符串;
  3.  [[:space:]]*    匹配0个或多个以空格,制表符,回车换行符;
  4. "\(.*\)"      上面匹配成功后,该行后续双引号""内部的字符串, 标记为引用 \1
  5. .*       匹配双引号后的任意字符串

replacement部分

  1. \1  :   引用regexp中原括号匹配的字符串

处理结果:

  1.     字符串“.ascii”对应的双引号内部的字符串替换本行如下
## 1.command  (由于使用-n 所以增加了一个p, 用于显示每步的输出)

#cat asm-offsets.s |sed -ne 's:^[[:space:]]*\.ascii[[:space:]]*"\(.*\)".*:\1:;p'

## 2.output

->
# 0 "" 2
# 16 "asm-offsets.c" 1

->#TASK_threadsp1
# 0 "" 2
# 17 "asm-offsets.c" 1

->TASK_threadsp1 $0 offsetof(struct task_struct1, thread.sp)
# 0 "" 2
# 18 "asm-offsets.c" 1

->
# 0 "" 2
#NO_APP
  •  第二条指令属于组合指令:
/^->/{s:->#\(.*\):/* \1 */:; \
s:^->\([^ ]*\) [\$#]*\([^ ]*\) \(.*\):#define \1 \2 /* \3 */:; \
s:->::; p;}'
  • 组合指令的第一条指令-替换指令:
s:->#\(.*\):/* \1 */:;

           设置"->#" 后的字符串设置为\1 ,并将\1内容放入注释段中

## 1.command  (由于使用-n 所以增加了一个p, 用于显示每步的输出)

# cat asm-offsets.s |sed -ne 's:^[[:space:]]*\.ascii[[:space:]]*"\(.*\)".*:\1:;/->/{s:->#\(.*\):/* \1 */:;p}'

## 2.output

->
/* TASK_threadsp1 */
->TASK_threadsp1 $0 offsetof(struct task_struct1, thread.sp)
->
  • 组合指令的第二条指令-替换指令:
s:^->\([^ ]*\) [\$#]*\([^ ]*\) \(.*\):#define \1 \2 /* \3 */:;

            regexp部分分为四部分:

  1.  ->\([^ ]*\)    匹配将"->"开头的行,之后非空格的字符串做为第一个引用\1 ;
  2. [\$#]* \([^ ]*\)   跳过一个空格后,过滤$ ,#字符后非空格的字符串做为第一个引用\2;
  3.    \(.*\)     跳过一个空格,后续所有字符串设置为引用\3

           replacement部分

  1. 组成C语言宏定义格式,并将\3 做为注释           #define    \1    \2   /* \3*/
## 1.command  (由于使用-n 所以增加了一个p, 用于显示每步的输出)

# cat asm-offsets.s |sed -ne 's:^[[:space:]]*\.ascii[[:space:]]*"\(.*\)".*:\1:;/->/{s:->#\(.*\):/* \1 */:;s:^->\([^ ]*\) [\$#]*\([^ ]*\) \(.*\):#define \1 \2 /* \3 */:;p}'

## 2.output

->
/* TASK_threadsp1 */
#define TASK_threadsp1 0 /* offsetof(struct task_struct1, thread.sp) */
->
  • 组合指令的第三条指令-替换指令:
s:->::

            过滤满足存在"->"行

## 1.command  (由于使用-n 所以增加了一个p, 用于显示每步的输出)

#cat asm-offsets.s |sed -ne 's:^[[:space:]]*\.ascii[[:space:]]*"\(.*\)".*:\1:;/->/{s:->#\(.*\):/* \1 */:;s:^->\([^ ]*\) [\$#]*\([^ ]*\) \(.*\):#define \1 \2 /* \3 */:;s:->::;p}'

## 2.output
/* TASK_threadsp1 */
#define TASK_threadsp1 0 /* offsetof(struct task_struct1, thread.sp) */

  • 组合指令的第四条指令-打印:
p

将处理结果输出

/* TASK_threadsp1 */
#define TASK_threadsp1 0 /* offsetof(struct task_struct1, thread.sp) */

 至此sed的任务顺利完成。

 

 

参考:

sed, a stream editor

SED 简明教程

Sed - An Introduction and Tutorial by Bruce Barnett

Be First to Comment

发表评论

电子邮件地址不会被公开。 必填项已用*标注