Linux-文本操作
wc
print newline, word, and byte counts for each file
- wc -l [文件名] // 计算文件的行数
- wc -c [文件名] // 计算文件字节数
- wc -w [文件名] // 计算文件字符数
stat
display file or file system status
- LANG=c stat [filename] // 英文化查看文件的信息
grep
print lines matching a pattern
- grep [正则][文件名] 一行一行查找正则匹配的内容
find
search for files in a directory hierarchy
- find *txt -exec rm -v {} ; // 查找文件并执行命令操作
time
计算指令的执行时间
- time ls
cut
remove sections from each line of files
- cut -d " " -f 1 // 以空格分隔文本并显示第一个(相当于 split()[0])
sed
默认只替换每行的第一个
sed 的工作模式
- 将文件以行为单位读取到内存(模式空间)
- 使用 sed 的每个脚本对该行进行操作 sed "script" filename
- 处理完成后输出该行(默认输出)
sed 'a/lod/new/' filename
# 如果要替换/,可以使用别的分隔符
sed 's!/!abc!' filename
# 执行多个命令
sed -e 's/a/aa/' -e 's/aa/bb/' afile
sed 's/a/aa/;s/aa/bb/' afile
# 将替换后的内容写入到原始文件
sed -i 's/a/aa/;s/aa/bb/' afile
# 支持元字符
head -5 /etc/passwd | sed 's/s*bin//'
# 支持回调,源文件内容:axyzb
# 扩展元字符使用 -r 参数
root@4ebe31ff7a18:/tmp# sed -r 's/(a.*b)/\1:\1/' cfile
axyzb:axyzb
sed 替换指令加强版
-
标志位:
- s/old/new/ 标志位
# 全局替换
sed "s/old/new/g" afile
# 替换前两次
sed "s/old/new/2" afile
# /p 打印模式空间的内容,匹配的行在下面再进行输出,应用:只输出替换成功的行, -n阻止默认输出
sed -n 's/root/123/p'
# /w 将替换成功的行写入别的文件
sed -n 's/root/123/w a.txt'
-
寻址:默认对每行进行操作,增加寻址后对匹配的行进行操作
- /正则表达式/s/old/new/g
- 行号/s/old/new/g
- 可以使用两个寻址符号,也可以混合使用行号和正则地址
# 从1-6行进行替换
head -6 /etc/passwd | sed '1,6s/games/!!!!!!!!/'
# 匹配root行,进行替换
head -6 /etc/passwd | sed '/root/s/bash/!!!!!!!!/'
# 多条命令
sed /正则/{s/old/new/;s/old/new/}
- 脚本文件:文件里写正常的 sed 命令
sed -f sedscript filename // 加载脚本文件
sed 的其他常用命令
# 删除行,删除操作会改变模式空间,就和管道的作用一样,后面的命令会以删除后的文件内容为准
sed '/ab/d' bfile
sed '/ab/d;=' bfile
# /i 在上一行插入
sed '/ab/i hello' bfile
# /a 在下一行插入
sed '/ab/a hello' bfile
# /c 替换行
sed '/ab/c hello' bfile
# /r 合并两个文件
sed '/ab/r cfile' bfile
# 打印行号 =
sed "=" bfile
# /n 操作匹配的下一行
# 打印匹配行,这个替换中的p不同,通常和-n参数配合只打印匹配到的行
sed -n '/ab/p' bfile
# 退出命令
sed 10q filename # 不会把文件全部读入
sed -n 1,10p filename
sed 多行模式
- N:将下一行加入到模式空间中
- D:删除模式空间中的第一个字符到第一个换行符
- P:打印模式空间中第一个字符到第一个换行符
root@4ebe31ff7a18:/tmp# cat dfile
hel
lo
root@4ebe31ff7a18:/tmp# sed 'N;s/hel\nlo/!!!/' dfile
!!!
root@4ebe31ff7a18:/tmp# sed 'N;s/hel.lo/!!!/' dfile
!!!
# 多行操作
root@4ebe31ff7a18:/tmp# cat b.txt
hell
o bash hel
lo bash
# 两行连起来操作
root@4ebe31ff7a18:/tmp# sed 'N;s/\n//;s/hello bash/hello sed\n/;P;D' b.txt
hello sed
hello sed
# 三行连起来操作
sed 'N;N;s/\n//;s/hello bash/hello sed\n/;P;D' b.txt
AWK
认识 awk
- awk 更像是脚本语言
- awk 用于比较规范的文本处理,用于统计数量并输出指定字段
- sed 将不规范的文本,处理为比较规范的文本
AWK 的字段
- 每行称作 AWK 的记录
- 使用空格、制表符分隔开的单词成为字段
- 可以自己制定分隔的字段
# -F 指定分隔符
# x++:打印行号
# ^menu:匹配行
awk -F "''" '/^menu/{ print x++, $2 }' /boot/grub2/grub.cfg
AWK 表达式
-
系统变量
- FS 表示输入的内容用什么分隔,OFS 表示输出用什么符号分隔
- RS 行分隔符
- NR 打印行号,如果文件变化了 NR 是不会重新排号,FNR 则会重新排号
- NF 字段的数量
# 下面两行意义相同,BEGIN表示在输入内容之前的操作
head -n 6 /etc/passwd | awk 'BEGIN{FS=":"}{print $1}'
head -n 6 /etc/passwd | awk -F ':' '{print $1} '
# 设置输出分隔符
head -n 6 /etc/passwd | awk 'BEGIN{FS=":";OFS="-"}{print $1,$2}
# 改变内容行的分隔符,改为通过:来换行,$0是输出一行
head -n 6 /etc/passwd | awk 'BEGIN{RS=":"}{print $0}'
# 打印行号
root@709230c0b366:~# awk '{print NR, $0}' /etc/hosts /etc/hosts
1 127.0.0.1 localhost
2 ::1 localhost ip6-localhost ip6-loopback
3 fe00::0 ip6-localnet
4 ff00::0 ip6-mcastprefix
5 ff02::1 ip6-allnodes
6 ff02::2 ip6-allrouters
7 172.17.0.4 709230c0b366
8 127.0.0.1 localhost
9 ::1 localhost ip6-localhost ip6-loopback
10 fe00::0 ip6-localnet
11 ff00::0 ip6-mcastprefix
12 ff02::1 ip6-allnodes
13 ff02::2 ip6-allrouters
14 172.17.0.4 709230c0b366
root@709230c0b366:~# awk '{print FNR, $0}' /etc/hosts /etc/hosts
1 127.0.0.1 localhost
2 ::1 localhost ip6-localhost ip6-loopback
3 fe00::0 ip6-localnet
4 ff00::0 ip6-mcastprefix
5 ff02::1 ip6-allnodes
6 ff02::2 ip6-allrouters
7 172.17.0.4 709230c0b366
1 127.0.0.1 localhost
2 ::1 localhost ip6-localhost ip6-loopback
3 fe00::0 ip6-localnet
4 ff00::0 ip6-mcastprefix
5 ff02::1 ip6-allnodes
6 ff02::2 ip6-allrouters
7 172.17.0.4 709230c0b366
# 打印有多少个字段
head -n 6 /etc/passwd | awk 'BEGIN{FS=":"}{print NF}'
# 打印最后一个字段的值
head -n 6 /etc/passwd | awk 'BEGIN{FS=":"}{print $NF}'
AWK 的判断和循环
root@7dc8bb7d5702:/tmp# cat a.txt
user1 34 54 23 12
user2 94 34 73 22
user3 99 45 12 49
root@7dc8bb7d5702:/tmp# awk '{ if($2>80) {print $1,$2} }' a.txt
user2 94
user3 99
root@7dc8bb7d5702:/tmp# awk '{ for(c=2;c<=NF;c++) print $c }' a.txt
34
54
23
12
94
34
73
22
99
45
12
49
root@7dc8bb7d5702:/tmp# awk '{ for(c=2;c<=NF;c++) sum+=$c; print sum}' a.txt
123
346
551
AWK 数组
# END表示数据读入完毕
root@7dc8bb7d5702:/tmp# awk '{ sum=0; for(c=2;c<=NF;c++) sum+=$c; average[$1]=sum/(NF-1) }END{ for( user in average ) print user,average[user] }' a.txt
user1 30.75
user2 55.75
user3 51.25
root@7dc8bb7d5702:/tmp# cat a.txt
user1 34 54 23 12
user2 94 34 73 22
user3 99 45 12 49
root@7dc8bb7d5702:/tmp# awk '{ sum=0; for(c=2;c<=NF;c++) sum+=$c; average[$1]=sum/(NF-1) }END{ for( user in average ) print user,average[user] }' a.txt
user1 30.75
user2 55.75
user3 51.25
root@7dc8bb7d5702:/tmp# awk '{ sum=0; for(c=2;c<=NF;c++) sum+=$c; average[$1]=sum/(NF-1) }END{ for( user in average ) sum2+=average[user]; print sum2/NR }' a.txt
45.9167
AWK 数组的应用
root@7dc8bb7d5702:/tmp# cat a.txt
user1 34 54 23 12
user2 94 34 73 22
user3 99 45 12 49
root@7dc8bb7d5702:/tmp# cat result.awk
{
sum = 0
for(c = 2; c <= NF; c++)
sum += $c
average[$1] = sum / (NF - 1)
print $1, average[$1]
}
END{
for(user in average)
sum_all += average[user]
avg_all = sum_all / NR
for(user in average)
if (average[user] > avg_all)
above++
else
below++
print "above", above
print "below", below
}
root@7dc8bb7d5702:/tmp# awk -f result.awk a.txt
user1 30.75
user2 55.75
user3 51.25
above 2
below 1
AWK 函数
# 整数
root@7dc8bb7d5702:/tmp# awk 'BEGIN{pi=3.114; print int(pi)}'
3
# 随机数
root@7dc8bb7d5702:/tmp# awk 'BEGIN{srand(); print rand()}'
0.739691
root@7dc8bb7d5702:/tmp# awk 'BEGIN{srand(); print rand()}'
0.00276686
root@7dc8bb7d5702:/tmp# awk 'BEGIN{srand(); print rand()}'
0.134305
# 自定义函数
root@7dc8bb7d5702:/tmp# awk 'function double(str) {return str str} BEGIN{print double("hello")}'
hellohello