shell 三剑客(grep,sed,awk)学习
常用快捷键
常用命令
- !! 上一条命令
- !行号 执行history命令
快捷键
- ctrl + a 移动光标到开头。ctrl + e 移动光标到结尾。Alt + 鼠标点击 移动光标到鼠标点击的位置
- ctrl + w 删除光标前的一个单词 ;ctrl + k删除当前光标到行尾的所有内容 ;ctrl + u 删除当前光标到行首所有内容 ;ctrl + y粘贴删除的内容
- clear 清屏。ctrl + k 清屏。ctrl + l 清除一行
重定向
1.输出重定向(>和>>): ifconfig > 1.txt 将输出的内容写到1.txt中. >>是追加写入
2.输入重定向:1.txt > myscript.sh 将文本中的内容当作输入传入脚本中。也就是说脚本需要用户键入信息,就可以将1.txt内容当作用户输入传进去
3.错误重定向(2>): myscript.sh 2> log.txt 将脚本执行的错误写入log.txt中;
myscript.sh &> log.txt 将脚本执行的结果和错误写入log.txt中;
myscript.sh > log.txt 2>&1 将脚本执行的结果写入log.txt中;错误信息也定位到和输出相同的位置
|& 管道服重定向
4.tee将输出同时写入文件中 command | tee 1.txt 2.txt 就是输出的内容可以重定向到多个位置,同时将命令的结果输出到1.txt和2.txt
ls '*' **2>&1** | tee a.txt
输出和错误都重定向到a.txt
直接键盘输入到文本中ctrl + D结束输入
(base) xuguangyu@Mac % tee ~/1.txt > /dev/null
hello
world^D
(base) xuguangyu\@Mac ~ % cat ~/1.txt
hello
world
5.命令重定向
|可以重定向输入流,将上一条命令的输出流重定向到下一条命令的输入流
正则表达式
- grep:擅长查找和匹配文本
- awk:擅长编辑和匹配文本内容
- sed:删除格式化文本内容,对文本进行复杂处理
基本正则表达式:^\$.[]*
扩展正则表达式()[]?+|
grep -E 扩展正则表达式可以分组匹配,匹配多次。
-o 只显示匹配到的内容
()将一个或多个字符捆绑在一起进行处理
\1 第一个匹配的内容
\2 第二个匹配到的内容
grep命令
不区分大小写,且匹配行号。
-i 不区分大小
-n 显示行号
(base) xuguangyu@Mac ~ % grep -i 'root' ~/pwd.txt -n
12\:root:\*:0:0\:System Administrator:/var/root:/bin/sh
13\:daemon:\*:1:1\:System Services\:/var/root:/usr/bin/false
62:\_cvmsroot:\*:212:212\:CVMS Root:/var/empty:/usr/bin/false
文件正则表达式匹配规则
bash中-E (zsh中-e)可以查看文本中的特殊字符,可以看到每一行开头是#,结尾是\$
(base) xuguangyu\@Mac ~ % cat -en ~/pwd.txt
1 ##\$
2 # User Database\$
3 # \$
4 # Note that this file is consulted directly only when the system is running\$
5 # in single-user mode. At other times this information is provided by\$
6 # Open Directory.\$
| 或者:或着左边全部,或着右边全部
grep -E "good|dlad" test.txt
匹配到good或者glad
grep -E "g(oo|la)d" test.txt
匹配到good或者glad
sed命令
用来处理文本内容,增加,删除,修改等.;默认是把处理过的和没处理过的一起打印
sed [options] 'pattern {action}' file
option:
-n 取消默认输出
-i 直接写入文件.
-e 多次编辑.
-r 支持正则拓展.
pattern可以使用内置命令:
a追加,d删除,i插入,p打印,s/正则/替换/g
案例
1.删除有#的行
sed '/#/d' ~/pwd.txt.
2.打印第2行到第3行
(base) xuguangyu@Mac ~ % sed -n '2,3p' ~/pwd.txt
# User Database
3.从第2行开始往后打印3行
(base) xuguangyu@Mac ~ % sed -n '2,+3p' ~/pwd.txt
\# User Database
\#
\# Note that this file is consulted directly only when the system is running
\# in single-user mode. At other times this information is provided by
4.将第三行开始到结尾的内容全部删除
(base) xuguangyu@Mac ~ % sed '3,$d' ~/pwd.txt
\##
\# User Database
5.替换
-e 执行多次操作,替换两次
sed -e 's/He/I/g' -e 's/abc/123/g' 1.txt
6.添加
a在下一行加,i在上一行加
sed '41a Hello world' 1.txt 在41行后面添加一行Hello world
sed '41i Hello world' 1.txt 在41行前面添加一行Hello world
sed 'a end' 1.txt 在每一行后面加入一行end,不加数字就会对每一行进行处理
7.使用grep和sed案例:取出linux ip地址
方法一
(base) xuguangyu@Mac ~ % ifconfig | grep 'inet ' | sed -e 's/.*inet //g' -e 's/ netmask.*//g'
127.0.0.1
192.168.31.154
方法二
(base) xuguangyu@Mac ~ % ifconfig | sed -n -e '/inet /p' | sed -e 's/.*inet //g' -e 's/ net.*//g'
127.0.0.1
192.168.31.154
方法三
(base) xuguangyu@Mac ~ % ifconfig | sed -ne 's/.*inet //' -e 's/netmask.*//pg'
127.0.0.1
192.168.31.154
-n只显示匹配到的结果 -e多次匹配
如果有多个p,那么多次匹配到的结果都会打印一下,所以只需要打印最后一次匹配的结果即可,s///g替换两次 p打印结果 g全局替换
awk命令
内置变量
默认以空格为分隔符,多个空格也为分隔符
\$0 表示一整行
\$NF 表示分割后的最后一列
倒数第二列\$(NF -1)
NR 表示当前行
FS 分隔符(输入)
OFS 输出分隔符
awk '{print $1}' change.txt 输出第一列
awk '{print $1}' change.txt 输出一整行
加上,输出会多一个空格,自定义输出内容时;必须外层单引号,内层双引号,\$1这种不能加引号,否则会识别为文本。
awk参数
一个等于号修改变量,两个等于号关系运算
-F 指定分隔符
-v 定义或着修改内置的变量
-f 从脚本文件中读取awk
案例
1.给每一行添加行号
awk '{print NR,$0}' change.txt
(base) xuguangyu\@Mac \~ % ifconfig | grep 'inet ' | awk '{print NR,\$0}'
1 inet 127.0.0.1 netmask 0xff000000
2 inet 192.168.31.154 netmask 0xffffff00 broadcast 192.168.31.255
2.选择指定行
将第五行到第十行所有内容全部打印
(base) xuguangyu@Mac ~ % cat -n ~/pwd.txt | awk 'NR==5,NR==10{print $0}'
5 # in single-user mode. At other times this information is provided by
6 # Open Directory.
7 #
8 # See the opendirectoryd(8) man page for additional information about
9 # Open Directory.
10 ##
3.awk取出IP地址
默认用空格做分隔,\$2代表取第二列的元素
(base) xuguangyu@Mac ~ % ifconfig en0 | awk 'NR==5{print $2}'
192.168.31.154
(base) xuguangyu\@Mac \~ % ifconfig en0
en0: flags=8863 mtu 1500
options=6460
ether 6e:4d\:ba:45:5d:44
inet6 fe80::490\:ffe3\:b738:9dd2%en0 prefixlen 64 secured scopeid 0xb
inet 192.168.31.154 netmask 0xffffff00 broadcast 192.168.31.255
nd6 options=201
media: autoselect
status: active
(base) xuguangyu\@Mac \~ %
4.以冒号为分隔符得到第一列
awk -F ':' '{print $1}' ~/pwd.txt
NF代表总字端数;\$NF代表取最后一个字端的数值
(base) xuguangyu@Mac % awk -F ':' 'NR==20{print $1,$NF}' /pwd.txt
_scsd /usr/bin/false
等价于直接改这个数值
(base) xuguangyu@Mac % awk -v FS=':' 'NR==20{print $1,$NF}' /pwd.txt
_scsd /usr/bin/false
5.修改输出分隔符
OFS修改可以修改输出显示的分隔符
(base) xuguangyu@Mac ~ % awk -F ':' -v OFS='=' 'NR=20{print $1,$NF}' ~/pwd.txt
_scsd===/usr/bin/false
awk 格式化
printf打印:可以格式化打印。默认不打印回车
awk '{printf "第一列:%s\n第二列:%s\n第三列:%s\n",$1,$2,$3}'
一般使用BEGIN打印第一行表头,END打印结尾
awk 'BEGIN{print '在所有动作之前打印'} {print ARGV[0],$0}END{print ‘在所有动作之后’}'
ARGV[0] 代表命令行参数,可以获取命令行参数
awk模式匹配
awk可以匹配到对应行再做处理,具体有三种模式
awk 'BEGIN{xxxx}模式{xxxx}END{xxx}'
1.空模式 所有行都匹配 awk '{print \$0}'所有行都打印出来
2.关系运算符,打印匹配到的行
awk '!/^$/{print $0}'
不打印空行
awk '$5~/^[0-9]{2}$/{print $0}'
匹配正则
awk正则表达式
awk '/^games/{print $0}' pwd.txt
找文件之间的内容
awk '/^operator/,/nologin$/{print $0}'
找到以operator开头到nologin结尾的内容
sort -n排序 uniq去重 wc -l 统计行数
unique -c 统计重复的次数
sort -rn 反转排序结果
head -10 拿到前10行
grep练习
- 找出root相关的行
(base) xuguangyu\@Mac ~ % grep 'root' ~/pwd.txt
root:\*:0:0\:System Administrator:/var/root:/bin/sh
daemon:\*:1:1\:System Services\:/var/root:/usr/bin/false
\_cvmsroot:\*:212:212\:CVMS Root:/var/empty:/usr/bin/false
(base) xuguangyu\@Mac \~ %
2.找出root开头的行
(base) xuguangyu\@Mac ~ % grep '^root' ~/pwd.txt
root:\*:0:0\:System Administrator:/var/root:/bin/sh
3.匹配以root开头的行或着以yu开头的行
(base) xuguangyu\@Mac ~ % grep -E '^(root|yu)' ~/pwd.txt
root:\*:0:0\:System Administrator:/var/root:/bin/sh
注意!!!
要是找root用户和yu用户的行,匹配完整的单词不要root1,yu1,yu2这种.要匹配完整的单词,需要加>
(base) xuguangyu\@Mac ~ % grep -E '^(root|yu)\\>' ~/pwd.txt
root:\*:0:0\:System Administrator:/var/root:/bin/sh
4.过滤bin开头的行,且显示行号
(base) xuguangyu\@Mac ~ % grep -n '^bin' ~/pwd.txt
5.过滤除root开头的行
(base) xuguangyu\@Mac ~ % grep -v '^root' ~/pwd.txt
\##
\# User Database
\#
6.统计yu用户出现的次数
(base) xuguangyu\@Mac ~ % grep '^yu' ~/pwd.txt | wc -l
0
(base) xuguangyu\@Mac \~ %
使用grep -c
(base) xuguangyu\@Mac ~ % grep -c '^yu' ~/pwd.txt
0
(base) xuguangyu\@Mac \~
7.匹配yu用户,最多出现2次
(base) xuguangyu@Mac ~ % grep -m 2 '^yu' ~/pwd.txt
8.匹配多个文件,列出存在的信息文件名称
(base) xuguangyu\@Mac ~ % grep "root" ~/pwd.txt \~/pwd2.txt
/Users/xuguangyu/pwd.txt\:root:\*:0:0\:System Administrator:/var/root:/bin/sh
/Users/xuguangyu/pwd.txt\:daemon:\*:1:1\:System Services\:/var/root:/usr/bin/false
/Users/xuguangyu/pwd.txt:\_cvmsroot:\*:212:212\:CVMS Root:/var/empty:/usr/bin/false
/Users/xuguangyu/pwd2.txt\:root:\*:0:0\:System Administrator:/var/root:/bin/sh
/Users/xuguangyu/pwd2.txt\:daemon:\*:1:1\:System Services\:/var/root:/usr/bin/false
/Users/xuguangyu/pwd2.txt:\_cvmsroot:\*:212:212\:CVMS Root:/var/empty:/usr/bin/false
(base) xuguangyu\@Mac \~ %
查看哪个文件中包含这条信息
(base) xuguangyu\@Mac ~ % grep -l "root" ~/pwd.txt \~/pwd2.txt
/Users/xuguangyu/pwd.txt
/Users/xuguangyu/pwd2.txt
(base) xuguangyu\@Mac \~ %
9.显示/etc/passwd文件中不以/bin/bash结尾的行
(base) xuguangyu\@Mac \~ % grep -v '/bin/bash\$' /etc/passwd
\##
\# User Database
\#
\# Note that this file is consulted directly only when the system is running
\# in single-user mode. At other times this information is provided
10.找出/etc/passwd中的两位数或三位数
错误写法
(base) xuguangyu\@Mac \~ % grep -E '\d{2,3}' /etc/passwd
\_taskgated:\*:13:13\:Task Gate Daemon:/var/empty:/usr/bin/false
\_networkd:\*:24:24\:Network Services\:/var/networkd:/usr/bin/false
\_installassistant:\*:25:25\:Install Assistant:/var/empty:/usr/bin/false
正确写法
需要在前后加上<>做闭合排除4位数或更多数字在一起的情况,
(base) xuguangyu\@Mac \~ % grep -E '\<\d{2,3}\>' /etc/passwd
\_taskgated:\*:13:13\:Task Gate Daemon:/var/empty:/usr/bin/false
\_networkd:\*:24:24\:Network Services\:/var/networkd:/usr/bin/false
11.找出文件中至少一个空白字符开头,后面是非空白字符
(base) xuguangyu\@Mac \~ % grep -E '^\s+\S+' 1.txt
I love you
或着
(base) xuguangyu\@Mac \~ % grep '^[[:space:]][^[:space:]]' 1.txt
I love you
12.找出文件中,所有大小写i开头的行
(base) xuguangyu\@Mac \~ % grep '^[iI]' 1.txt
I love you
i love you
或着-i是忽略大小写
(base) xuguangyu\@Mac \~ % grep -i '^i' 1.txt
I love you
i love you
13.找出系统上root,yu,nobody用户的信息
(base) xuguangyu\@Mac ~ % grep -E '^(root|yu|nobody)\>' ~/pwd.txt
nobody:\*:-2:-2\:Unprivileged User:/var/empty:/usr/bin/false
root:\*:0:0\:System Administrator:/var/root:/bin/sh
14.找出/etc/init.d/functions文件中的所有函数名
(base) xuguangyu\@Mac ~ % grep -E '\w+\(\)' ~/functions
checkpid() {
\_\_kill\_pids\_term\_kill\_checkpids() {
\_\_kill\_pids\_term\_kill() {
\_\_pids\_var\_run() {
\_\_pids\_pidof() {
daemon() {
killproc() {
pidfileofproc() {
pidofproc() {
status() {
echo\_success() {
echo\_failure() {
14.找出/etc/init.d/functions文件中的所有函数名
(base) xuguangyu\@Mac ~ % grep -E '\w+\(\)' ~/functions
checkpid() {
\_\_kill\_pids\_term\_kill\_checkpids() {
\_\_kill\_pids\_term\_kill() {
\_\_pids\_var\_run() {
\_\_pids\_pidof() {
daemon() {
killproc() {
pidfileofproc() {
pidofproc() {
status() {
echo\_success() {
echo\_failure() {
15.找出用户名和shell相同的用户
base) xuguangyu\@Mac \~ % grep -E '^(\w).*\1$' passwd
(base) xuguangyu\@Mac \~ % grep -E '^([^:]+\>).*\1$' passwd
sync\:x:5:0\:sync:/sbin:/bin/sync
shutdown\:x:6:0\:shutdown:/sbin:/sbin/shutd
sed练习
a.替换⽂件的root为chaoge,只替换⼀次,与替换所有
(base) xuguangyu\@Mac \~ % sed 's/root/chaoge/' pwd2.txt
chaoge\:x:0:0\:root:/root:/bin/bash
bin\:x:1:1\:bin:/bin:/bin/false
(base) xuguangyu\@Mac \~ % sed 's/root/chaoge/g' pwd2.txt
chaoge\:x:0:0\:chaoge:/chaoge:/bin/bash
bin\:x:1:1\:bin:/bin:/bin/false
ba\:x:1002:1002::/home/zhangy:/bin/bash
b.替换⽂件所有的root为chaoge,且仅仅打印替换的结果
(base) xuguangyu\@Mac \~ % sed -n 's/root/chaoge/pg' pwd2.txt
chaoge\:x:0:0\:chaoge:/chaoge:/bin/bash
-n和p配合使用
c.替换前10⾏bin开头的⽤户,改为C,且仅仅显示替换的结果
(base) xuguangyu\@Mac \~ % sed -n '1,10s/^bin/C/pg' pwd2.txt
C\:x:1:1\:bin:/bin:/bin/false
替换前10⾏b开头的⽤户,改为C,且将m开头的⾏,改为M,且仅仅显
示替换的结果
(base) xuguangyu\@Mac \~ % sed -n -e '1,10s/^b/C/g' -e 's/m/M/pg' pwd2.txt
Ca\:x:1002:1002::/hoMe/zhangy:/bin/bash
daeMon\:x:2:2\:daeMon:/sbin:/bin/false
Mail\:x:8:12\:Mail:/var/spool/Mail:/bin/false
ftp\:x:14:11\:ftp\:/hoMe/ftp\:/bin/false
dbus\:x:81:81\:SysteM Message bus\:/:/bin/false
hal\:x:82:82\:HAL daeMon:/:/bin/false
Mysql\:x:89:89::/var/lib/Mysql:/bin/false
aaa\:x:1001:1001::/hoMe/aaa:/bin/bash
test\:x:1003:1003::/hoMe/test:/bin/bash
(base) xuguangyu\@Mac \~ %
-e多次匹配,每匹配一次就要加一个-e
删除4⾏后⾯所有
(base) xuguangyu\@Mac \~ % sed '4,$d' pwd2.txt
root\:x:0:0\:root:/root:/bin/bash
bin\:x:1:1\:bin:/bin:/bin/false
ba\:x:1002:1002::/home/zhangy:/bin/bash
删除从root开始,到ftp之间的⾏
(base) xuguangyu\@Mac \~ % sed '/^root/,/^ftp/d' pwd2.txt
\&nobody:\$:99:99\:nobody:/:/bin/false
http\:x:33:33::/srv/http\:/bin/false
dbus\:x:81:81\:System message bus\:/:/bin/false
hal\:x:82:82\:HAL daemon:/:/bin/false
mysql\:x:89:89::/var/lib/mysql:/bin/false
aaa\:x:1001:1001::/home/aaa:/bin/bash
test\:x:1003:1003::/home/test:/bin/bash
(base) xuguangyu\@Mac \~ %
跨行删除
将⽂件中空⽩字符开头的⾏,添加注释符
(base) xuguangyu\@Mac \~ % sed 's/^[[:space:]]/#/g' lovers.txt
\#I like my lover.
I love my lover.
He likes his lovers.
\#He loves his lovers.
\#she loves her cat
2.删除⽂件的空⽩和注释⾏
(base) xuguangyu\@Mac \~ % sed '/^$/d;/^#/d' lovers.txt
I like my lover.
I love my lover.
3.给⽂件前三⾏,添加#符号
(base) xuguangyu\@Mac \~ % sed '1,3s/\(^.\)/#\1/g' lovers.txt
\# I like my lover.
\#I love my lover.
\#He likes his lovers.
He loves his lovers.
\#she loves her cat
(base) xuguangyu\@Mac \~ %
注意:()
要使用\(\)
方式
4.sed取出ip地址
(base) xuguangyu\@Mac \~ % ifconfig en0 | sed -n -e 's/^.*inet //g' -e 's/netmask.*$//pg'
192.168.31.154
-- mac不备份,替换原文件
(base) xuguangyu\@Mac \~ % sed -i '' 's/\(^.\*:.\*\)0x.\*\(".\*\$\\)/\10xabc\2/g' hello.txt
awk练习
1.在当前系统中打印出所有普通⽤户的⽤户和家⽬录(/etc/passwd)
(base) xuguangyu\@Mac \~ % awk -F ':' 'BEGIN{printf "%-10s %-10s\n","用户名","家目录"}$3>1000{printf "%-10s %-10s\n",$1,$(NR-1)}' pwd2.txt
用户名 家目录
ba x
aaa
test
(base) xuguangyu\@Mac \~ %
BEGIN打印开头文字
printf 格式化打印
2.给/tmp/chaoge.txt⽂件的前五⾏,添加#号
(base) xuguangyu\@Mac \~ % awk 'NR<=5{print "#",$0}' chaoge.txt
\# 爱的魔⼒转圈圈1 爱的魔⼒转圈圈2 爱的魔⼒转圈圈3
\# 爱的魔⼒转圈圈4 爱的魔⼒转圈圈5 爱的魔⼒转圈圈6
\# 爱的魔⼒转圈圈7 爱的魔⼒转圈圈8 爱的魔⼒转圈圈9
\# 爱的魔⼒转圈圈10 爱的魔⼒转圈圈11 爱的魔⼒转圈圈12
\# 爱的魔⼒转圈圈13 爱的魔⼒转圈圈14 爱的魔⼒转圈圈15
(base) xuguangyu\@Mac \~ %
3.统计⽂本信息
姓名 区号 电话 三个⽉捐款数量
(base) xuguangyu\@Mac \~ % cat info.txt
Mike Harrington:\[510] 548-1278:250:100:175
Christian Dobbins:\[408] 538-2358:155:90:201
Susan Dalsass:\[206] 654-6279:250:60:50
Archie McNichol:\[206] 548-1348:250:100:175
Jody Savage:\[206] 548-1278:15:188:150
Guy Quigley:\[916] 343-6410:250:100:175
Dan Savage:\[406] 298-7744:450:300:275
Nancy McNeil:\[206] 548-1278:250:80:75
John Goldenrod:\[916] 348-4278:250:100:175
Chet Main:\[510] 548-5258:50:95:135
Tom Savage:\[408] 926-3456:250:168:200
Elizabeth Stachelin:\[916] 440-1763:175:75:300
(base) xuguangyu\@Mac \~ %
显示所有电话号码
(base) xuguangyu\@Mac \~ % awk -F '[ :]' '!/^$/{print $4}' info.txt
548-1278
538-2358
654-6279
548-1348
548-1278
343-6410
298-7744
548-1278
348-4278
548-5258
926-3456
440-1763
(base) xuguangyu\@Mac \~ %
-F
设置切割符号 [: ]
同时设置两个切割符号,空格和:都切
!/^\$/
不匹配空行
显示Tom的电话
(base) xuguangyu@Mac ~ % awk -F '[: ]' '/^Tom/{print $4}' info.txt
926-3456
显示Nancy的姓名、区号、电话
(base) xuguangyu@Mac ~ % awk -F '[: ]' '/^Nancy/{print $2,$3,$4}' info.txt
McNeil [206] 548-1278
显示所有D开头的姓
(base) xuguangyu@Mac % awk -F '[: ]' '$2/^D/{print $2}' info.txt
Dobbins
Dalsass
\$2\~/^D/代表第二行进行正则匹配
不能用匹配D开头的行
(base) xuguangyu@Mac ~ % awk -F '[: ]' '/^D/{print $2}' info.txt
Savage
显示所有区号是916的⼈名
(base) xuguangyu@Mac ~ % awk -F '[: ]' '$3=="[916]"{print $1}' info.txt
Guy
John
Elizabeth
(base) xuguangyu\@Mac ~ % awk -F '[: ]' '\$3~/\[916\]/{print $1}' info.txt
Guy
John
Elizabeth
(base) xuguangyu\@Mac \~ %
如果只是字面意思,就需要[916]
变为\[916\]
两种都可以
显示Mike的捐款信息,在每⼀款前加上美元符
(base) xuguangyu@Mac ~ % awk -F '[: ]' '$1=="Mike"{printf "$%s $%s $%s\n",$5,$6,$7}' info.txt
$250 $100 $175
另一种写法
(base) xuguangyu@Mac ~ % awk -F '[: ]' '$1=="Mike"{print "$"$(NF-2)" $"$(NF-1)" $"$NF}' info.txt
$250 $100 $175
显示所有⼈的`姓+逗号+名
(base) xuguangyu@Mac ~ % awk -F '[: ]' -v OFS="," '!/^$/{print $2,$1}' info.txt
Harrington,Mike
Dobbins,Christian
Dalsass,Susan
McNichol,Archie
Savage,Jody
Quigley,Guy
Savage,Dan
McNeil,Nancy
Goldenrod,John
Main,Chet
Savage,Tom
Stachelin,Elizabeth
(base) xuguangyu\@Mac \~
OFS可以指定输出分隔符
删除⽂件的空⽩⾏(awk不修改源⽂件),替换后的内容重定向写⼊新⽂
件
(base) xuguangyu@Mac ~ % awk '!/^$/{print $0}' chaoge.txt > chaoge2.txt
(base) xuguangyu\@Mac \~ % cat chaoge2.txt
爱的魔⼒转圈圈1 爱的魔⼒转圈圈2 爱的魔⼒转圈圈3
爱的魔⼒转圈圈4 爱的魔⼒转圈圈5 爱的魔⼒转圈圈6
爱的魔⼒转圈圈7 爱的魔⼒转圈圈8 爱的魔⼒转圈圈9
爱的魔⼒转圈圈10 爱的魔⼒转圈圈11 爱的魔⼒转圈圈12
爱的魔⼒转圈圈13 爱的魔⼒转圈圈14 爱的魔⼒转圈圈15
爱的魔⼒转圈圈16 爱的魔⼒转圈圈17 爱的魔⼒转圈圈18
爱的魔⼒转圈圈19 爱的魔⼒转圈圈20
(base) xuguangyu\@Mac \~ %