sed 通常用于过滤文本,即它接受文本输入,对其执行一些操作(或一组操作),然后输出修改后的文本。sed 通常用于使用模式匹配或替换文件中多次出现的字符串来提取文件的一部分。
Linux sed 命令是利用脚本来处理文本文件。
sed 可依照脚本的指令来处理、编辑文本文件。
Sed 主要用来自动编辑一个或多个文件、简化对文件的反复操作、编写转换程序等。
使用场景
我就讲讲我本来的使用场景,我使用最多的场景就是编写shell脚本时会用到。使用sed可以自动化的编辑一些文本文件。
官方帮助文档
https://www.gnu.org/software/sed/manual/sed.html
语法
sed [-hnV][-e<script>][-f<script文件>][文本文件]
参数说明:
-e<script>
或--expression=<script>
以选项中指定的script来处理输入的文本文件。-f<script文件>
或--file=<script文件>
以选项中指定的script文件来处理输入的文本文件。- -h或–help 显示帮助。
- -n或–quiet或–silent 仅显示script处理后的结果。
- -V或–version 显示版本信息。
动作说明:
- a :新增, a 的后面可以接字串,而这些字串会在新的一行出现(目前的下一行)~
- c :取代, c 的后面可以接字串,这些字串可以取代 n1,n2 之间的行!
- d :删除,因为是删除啊,所以 d 后面通常不接任何东东;
- i :插入, i 的后面可以接字串,而这些字串会在新的一行出现(目前的上一行);
- p :打印,亦即将某个选择的数据印出。通常 p 会与参数 sed -n 一起运行~
- s :取代,可以直接进行取代的工作哩!通常这个 s 的动作可以搭配正规表示法!例如 1,20s/old/new/g 就是啦!
示例
ubuntu切换国内的源
一般情况下,将 /etc/apt/sources.list
文件中 Ubuntu 默认的源地址 http://archive.ubuntu.com/
替换为 http://mirrors.ustc.edu.cn/
即可。
如果手动编辑,要输入很多指令,还会出错操作失误。
使用sed一行命令就可以实现了。
sudo sed -i 's/archive.ubuntu.com/mirrors.ustc.edu.cn/g' /etc/apt/sources.list
这样就非常的方便。
删除文件中的第 4 行
sed '4d' input.txt > output.txt
在第 10-20 行将所有出现的 ‘hello’ 替换为 ‘world’
sed '10,20s/hello/world/' input.txt > output.txt
仅打印输入文件的第 45 行
sed -n '45p' file.txt
多个文件拼在一起输出1到20行
sed -n '1,20p' one.txt two.txt three.txt
输入指定字符结尾的行
sed -n '/bash$/p' /etc/passwd
Id
删了指定字符结尾的行
input.txt内容如下;
boy
dog
girl
phone
age
sed '/dog$/Id' input.txt
删除命令: Id
i
指定位置插入文本
input.txt内容如下;
a
b
c
要在b上面一行加个java
sed '/b/ijava' input.txt
a
java
b
c
基中i为插入指令java是插入的值
c
将第 2 到第 9 行替换为单词 ‘hello’
seq 10 | sed '2,9c hello'
1
hello
10
命令c
替换文本
整段文本的处理
处理段落等文本块(而不是逐行)的常用技术是使用以下构造:
sed '/./{H;$!d} ; x ; s/REGEXP/REPLACEMENT/'
第一个表达式/./{H;$!d}对所有非空行进行操作,并将当前行(在模式空间中)添加到保持空间。在除最后一行之外的所有行上,模式空间都被删除并重新开始循环。
其他表达式x和s仅在空行(即段落分隔符)上执行。该x命令将累积的行从保持空间取回模式空间。然后该 s///命令对段落中的所有文本(包括嵌入的换行符)进行操作。
示例:
$ cat input.txt
a a a aa aaa
aaaa aaaa aa
aaaa aaa aaa
bbbb bbb bbb
bb bb bbb bb
bbbbbbbb bbb
ccc ccc cccc
cccc ccccc c
cc cc cc cc
$ sed '/./{H;$!d} ; x ; s/^/\nSTART-->/ ; s/$/\n<--END/' input.txt
START-->
a a a aa aaa
aaaa aaaa aa
aaaa aaa aaa
<--END
START-->
bbbb bbb bbb
bb bb bbb bb
bbbbbbbb bbb
<--END
START-->
ccc ccc cccc
cccc ccccc c
cc cc cc cc
<--END
原文:
https://www.gnu.org/software/sed/manual/sed.html#Multiline-techniques
shell配合sed实现整段文本替换实例
写本文的原始需求:
需要修改一个非常长的Java文件,以下代码块
@SuppressLint("AddJavascriptInterface")
public void setMessagingEnabled(boolean enabled) {
if (messagingEnabled == enabled) {
return;
}
messagingEnabled = enabled;
if (enabled) {
addJavascriptInterface(createRNCWebViewBridge(this), JAVASCRIPT_INTERFACE);
} else {
removeJavascriptInterface(JAVASCRIPT_INTERFACE);
}
}
替换成:
@SuppressLint("AddJavascriptInterface")
public void setMessagingEnabled(boolean enabled) {//webview removeJavascriptInterface
removeJavascriptInterface("searchBoxJavaBridge_");
removeJavascriptInterface(accessibility);
removeJavascriptInterface(accessibilityTraversal);
}
原本我想使用Java代码代码来实现替换动作,或者使用lua脚本来实现这个事情。
考虑到这两种方式都是需要安装运行环境的。所以我就想能不能使用兼容性相对比较好的shell来实现。
就想到使用sed命令来实现了。
最终功能已经成功实现了,代码写的比较笨拙,仅供大家参考:
#!/bin/bash
#自动修改React Native Webview中的代码
#editFilePath='input.txt1'
editFilePath='../node_modules/react-native-webview/android/src/main/java/com/reactnativecommunity/webview/RNCWebViewManager.java
'
#获取要修改的行号
setMessagingEnabledLine=$(sed -n '/public void setMessagingEnabled(boolean enabled) {$/=' ${editFilePath})
echo "查询关键字的位置:${setMessagingEnabledLine}"
if [[ "$setMessagingEnabledLine" == '' ]]; then
echo "没有找到关键字,程序将要退出"
echo "可能已经执行过 自动修改脚本了"
exit
fi
#要修改的开始行号
startLine="$((setMessagingEnabledLine+1))"
#要修改的结束行号
endLine="$((startLine+10))"
index1="$((startLine-1))"
index2="$((startLine))"
index3="$((startLine+1))"
#删除方法体的内容
sed -i "${startLine},${endLine}d" ${editFilePath}
#增加第一行代码___用于格式化
sed -i "${index1}a________removeJavascriptInterface(\"searchBoxJavaBridge_\");" ${editFilePath}
#增加第二行代码
sed -i "${index2}a________removeJavascriptInterface("accessibility");" ${editFilePath}
#增加第三行代码
sed -i "${index3}a________removeJavascriptInterface("accessibilityTraversal");" ${editFilePath}
#用空格格式化一下
sed -i 's/________/ /' ${editFilePath}
#标记位做个标记访问重复处理
sed -i 's/public void setMessagingEnabled(boolean enabled) {$/public void setMessagingEnabled(boolean enabled) {\/\/webview removeJavascriptInterface/' ${editFilePath}
cat ${editFilePath}
兼容macOS与linux的方案
很遗憾有些命令在linux中运行正常,而放到macOS中执行就无法正常运行了。
比如:
sed -i "1,19d" test.java
以上命令在Linux下是正常运行的,而在macOS中就不行了。报错如下:
sed -i "1,19d" test.java
sed: 1: "test.java": undefined label 'est.java'
解决办法:
sed -i.bak -e "1,19d" test.java
这样写就不报错了,但是修改文件后会自动备份文件。
如果不想生成备份文件,可以这样:
sed -i.bak -e "1,19d" test.java && rm test.java.bak
这样会显的比较繁琐,但至少可以兼容两种系统。
第二种解决方案
调整mac下sed的用法,使其与linux一致
mac上安装gnu-sed
brew install gnu-sed
alias sed=gsed
调整后两系统下sed的用法完全一致。
链接:https://www.jianshu.com/p/87a57a12d5e6
参考
https://www.runoob.com/linux/linux-comm-sed.html
https://www.gnu.org/software/sed/
https://www.gnu.org/software/sed/manual/sed.html
《Mac OSX 上的 sed 与其他“标准”sed 之间的区别?》
https://unix.stackexchange.com/questions/13711/differences-between-sed-on-mac-osx-and-other-standard-sed
《mac上遇到的错误sed command a expects followed by text》
https://www.jianshu.com/p/87a57a12d5e6