sed

[TOC]

特殊字符反斜杠的问题

#sed -i '4 s/^/good baby\n/' test.txt 

执行成功,符合预期,但是

#sed -i '4 s/^/<center>查询结果:6个  <a href="./">继续</a>/' test.txt

却运行失败,经多次尝试排查,终于发现原因。
因为正则表达式中有'/'这个特殊字符,应该进行转义。
改成'\/'即可,但是实际在C代码中,采用system(cmd)的方式,并未成功

由于时间紧张,未能深入排查。
但是在排查的时候,意外发现把's'换成‘a’,即进行追加而不是替换的话,可以完美工作。
不需要考虑特殊字符的转义问题,在'a'后面,反斜杠'/'成了普通字符。

#sed -i '8 a<center>查询结果:6个  <a href="./">继续</a>' test.txt

一个常用技巧,查询数据类型在哪个头文件中定义

grep -rn keyWords ../../ | sed -n "/\.h:\w\+/"p

find ../ -name '*.c' | xargs grep -rni keyWords
--->>输出中带有路径

find ../ -type f | grep '\.c' | xargs grep -rni keyWords
-->>这种情况下,输出带有路径,行数,内容

find ../ -name '*.c' | xargs -i grep -rni keyWords {}
==>>这种情况下,输出结果中,没有文件名

grep 执行条目过多时,会报错

# grep -rni keyWords `find ../../ -name *.c`

找到关键字所在的行,将其替换为别的内容

如:void function(int a, int b) 修改为:
static void function(int a, int b)

fileName=/home/user/a.c
lineNum=`sed -n '/void function/=' a.c` ; \
echo ${lineNum}; \
sed -i "${lineNum}d" a.c; \
sed -i "${lineNum}iStatic void function(int a, int b)" a.c

说明: sed -i "${lineNum}d" 删除指定行
      sed -i "${lineNum}iXXX" 在指定行插入XXX
      sed -n "/key/=" 查找关键字所在行,如果是多个,
则返回 3 5 7 这样的字符串,可以用数组接收

找到关键字,并在其后追加一行

第一步,制作准备原始文件
# cat pps
aaaa
bbbb
cccc
dddd

第二步,命令行实验,尝试先查找"cccc"所在的行,并在其后追加"zzzz"
# sed -e "/cccc azzzz"
aaaa
bbbb
cccc
zzzz
dddd

第三步,因为第二步实验成功,将其写入文件
# sed -i "/cccc azzzz" pps
# cat pps
aaaa
bbbb
cccc
zzzz
dddd

一个高难度应用

1、问题描述

源文本如下:
<test> blah blah   blah blah blah   widget   blah blah blah
</test>
<formula>   blah   <details>      widget   </details>
</formula>

希望从文本中,提取出如下内容:
<test>widget</test> 
<formula>widget</formula>

2、实现脚本

sed -ne '/[Ww][Ii][Dd][Gg][Ee][Tt]/,/^<\// {//p}' file.txt | awk 'NR%2==1 { sub(/^[ \t]+/, ""); search = $0 }

效果:
<test>widget</test>
<formula>widget</formula>

3、脚本讲解

## The sed pipe: 
sed -ne '/[Ww][Ii][Dd][Gg][Ee][Tt]/,/^<\// {//p}'
## This finds the widget pattern, ignoring case, then finds the last, 
## highest level markup tag (these must match the start of the line)
## Ultimately, this prints two lines for each pattern match 
## Now the awk pipe: 
NR%2==1 { sub(/^[ \t]+/, ""); search = $0 }
## This takes the first line (the widget pattern) and removes leading
## whitespace, saving the pattern in 'search' 
NR%2==0 { end = $0; sub(/^<\//, "<"); printf "%s%s%s\n", $0, search, end }
## This finds the next line (which is even), and stores the markup tag in 'end'
## We then remove the slash from this tag and print it, the widget pattern, and
## the saved markup tag

对包含特殊关键字的行进行操作

1、在包含关键字的行中,删除另外一个关键字

# cat ass.txt
good boy
good man
good girl
# 将包含 man 的行中,对应的关键字 good 删除
# sed -i /man/{s/good//} ass.txt
# cat ass.txt
good boy
 man
good girl

2、将同时包含关键字A和关键字B的行删除

# cat ass.txt
good boy
good man
good girl
# 将包含 man 的行中,对应的关键字 good 删除
# sed -i /man/{/good/d} ass.txt
# cat ass.txt
good boy
good girl

sed 的一个大 bug

1、诡异现象

写入文件,出现故障。
一段脚本之中,前面写入表现都很正常。
但是执行到某一段代码时,出现莫名其妙的故障,代码如下:

#==>>这是前面一段代码,表现正常,能成功写入,符合预期
for fileName in ${file_1} ${file_2}
do
    lineNum=`sed -n /keyword_1/= ${fileName}`
    startLine=$((lineNum - 5))
    endLine=$((lineNum + 2))
    # 删除这一段内容
    sed -i "${startLine},${endLine} d"
    # 下面用 sed 重新插入代码
    sed -i "${startLine}a #ifndef OK" ${fileName} ; startLine=$((${startLine} + 1))
    sleep 0.01 # 为了防止写文件太耗时,加一个延时10ms
    sed -i "${startLine}a #define OK 0" ${fileName} ; startLine=$((${startLine} + 1))
    sleep 0.01 # 为了防止写文件太耗时,加一个延时10ms
    sed -i "${startLine}a #define ERRPR 1" ${fileName} ; startLine=$((${startLine} + 1))
    sleep 0.01 # 为了防止写文件太耗时,加一个延时10ms
    ...
    sed -i "${startLine}a #endif" ${fileName} ; startLine=$((${startLine} + 1))
done

#==>>第二段代码,与第一段雷同,只是行数略多,20行而已,但是后来发现跟行数无关
for fileName in ${file_3} ${file_4}
do
    lineNum=`sed -n /keyword_2/= ${fileName}`
    startLine=$((lineNum - 19))
    endLine=$((lineNum + 2))
    # 删除这一段内容
    sed -i "${startLine},${endLine} d"
    # 下面用 sed 重新插入代码
    sed -i "${startLine}a #ifndef OK" ${fileName} ; startLine=$((${startLine} + 1))
    sleep 0.01 # 为了防止写文件太耗时,加一个延时10ms
    sed -i "${startLine}a #define OK 0" ${fileName} ; startLine=$((${startLine} + 1))
    sleep 0.01 # 为了防止写文件太耗时,加一个延时10ms
    sed -i "${startLine}a #define ERRPR 1" ${fileName} ; startLine=$((${startLine} + 1))
    sleep 0.01 # 为了防止写文件太耗时,加一个延时10ms
    ...
    sed -i "${startLine}a #endif" ${fileName} ; startLine=$((${startLine} + 1))
done
# 此时,执行到第二段代码时,实际上成了死循环。如果只有一两行写入语句
# 还能跳出死循环,但目标文件已被写乱掉
# 如果行数多,直接就是死循环,及时中止,txt 文件已经达到37M
# 此问题百思不得其解,实在诡异

2、发现一点不同

在出故障的代码段中,发现最后一行行为正常:
    sed -i "${startLine}a #endif" ${fileName} ; startLine=$((${startLine} + 1))
它能正常写入,然后用它替换其它的行:
sed -i "${startLine}a #ifndef OK" ${fileName} ; startLine=$((${startLine} + 1))
改为 `#endif`之后,就能正常写入,其它字符串就不行,将中间空格去掉也不行:
sed -i "${startLine}a #ifndefOK" ${fileName} ; startLine=$((${startLine} + 1))

到了这份上,其实不就是写一个普通字符串吗,`#ifndefOK` 和 `#endif`能有啥区别?

发表评论