20210211-1 Linux基础与应用(中)
一、文本操作命令
文本操作命令
• cat
• more
• less
• head
• head -2 /etc/profile
• tail
• tail -2 /etc/profile
• tail -f 实时监控日志,日志不断增长时,可以看到最新的当前的日志
如果只想看第 4 行日志,应该怎么办?可以借助管道的概念
[root@localhost ~]# head -4 install.log.syslog2 | tail -1
4d
类似于取交集??
• 管道
• cat b.txt | head -3
打开文本b 输入到管道 管道后的进程读取文本前三行;管道前后相当于开两个子进程
[root@localhost ~]# echo $$
4743
$$ 代表当前进程的PID
[root@minicent6~]# echo $$
1169 现在是在父进程中
[root@minicent6~]# /bin/bash 打开一个bash,开了一个子进程
[root@minicent6~]# echo$$ 所以进程号变了
1375
[root@minicent6~]# /bin/bash 又打开一个bash,用pstree查看进程结构
[root@minicent6~]# exit 退出到父进程
exit
[root@minicent6~]# echo $$ 退回到 1375
1375
[root@minicent6~]# exit 退出后,pstree查看进程,只有一个了
exit
[root@minicent6~]# echo $$ 打印PID和最初的是一样的,退回到最上面的进程
1169
所以管道符会自动打开两个进程,而且都是当前进程的子进程,通过bash打开交互
把 cat b.txt的输出作为 head -3 子进程的输入,同时 cat b.txt进程关闭,head -3进程执行完成后也会关闭子进程,输出结果后,回到当前的父进程
• | tail -1
• xargs
• shell读取用户输入的字符串
• 发现 |,代表有管道
• | 左右被理解为简单命令
• 加工:前一个(左边)简单命令的标准输出
• 指向后一个(右边)简单命令的标准输入
• 注意:后一个简单命令一定能够接受标准输入
一个命令的出现一定是为了解决某个问题
如果在root路径下,但是想看etc目录下的内容 可以
[root@localhost ~]# ls -l /etc
• xargs:
• 命令
• 1,在标准输入中读取到的内容!
• 2,自己的参数理解为一个字符串
• 模仿shell,做blank切分,第一个子字符串为命令
• 3,将步骤1的内容做为步骤2的命令的选项参数拼接起来
• 4,执行得到的结果
• echo "/" | xargs ls -l
现在在脚本里,想把目录作为变量输入管道后的命令
[root@localhost ~]# echo "/" | ls -l
总用量 12
-rw-------. 1 root root 1523 1月 25 15:15 anaconda-ks.cfg
……
这种方式失败了,并没有接成输入,显示的还是当前目录root下的内容,这时可以用xargs
[root@localhost ~]# echo "/" | xargs ls -l
总用量 28
lrwxrwxrwx. 1 root root 7 1月 25 15:06 bin -> usr/bin
dr-xr-xr-x. 5 root root 4096 1月 25 15:21 boot
drwxr-xr-x. 20 root root 3300 2月 8 21:45 dev
……
现在显示的就是根目录下的内容,xargs把前面的命令作为一个参数传到后面
• 打开文件
• vim /path/to/somefile
• vim +# :打开文件,并定位于第#行
• vim +:打开文件,定位至最后一行
• vim +/PATTERN : 打开文件,定位至第一次被PATTERN匹配到的行的行首
vi 是 vim的简化版本,一般vim是需要自己去安装的
[root@localhost ~]# yum -i install vim
如果打开的文本很长,现在需要定位到某一行,不想上下滚动
[root@localhost em]# vi +5 profile
光标就会在第5行的位置,shift+冒号进入末行模式,在末行模式输入:set nu,可以打印行数;输入:set nonu,取消打印行数
如果想直接进入最后一行
[root@localhost em]# vi + profile
如果已经 vi profile 进入命令模式了,想要定位在最后一行,可以按 shift+g,进入末行
如果配置文件中的某个字符有误,可以直接查找定位到某个字的位置
/是查找的意思,+可以表示增加的功能 +/after,增加查找after的功能
[root@localhost em]# vi +/after profile
• 关闭文件
• 末行模式:
• :q 退出 没有动过文件
• :wq 保存并退出 动过了,不后悔
• :q! 不保存并退出 动过了,后悔了
• :w 保存
• :w! 强行保存
• :wq --> :x
• ZZ: 保存并退出 不需要冒号,编辑模式
全屏编辑器
模式:
编辑模式:按键具有编辑文本功能:默认打开进入编辑模式
输入模式:按键本身意义
末行模式:接受用户命令输入文本操作命令
在输入模式按ESC退出insert插入模式的界面上,可以按 hjkl上下左右控制光标
• 编辑-->输入:
• i: 在当前光标所在字符的前面,转为输入模式;
• a: 在当前光标所在字符的后面,转为输入模式;
• o: 在当前光标所在行的下方,新建一行,并转为输入模式;
• O:在当前光标所在行的上方,新建一行,并转为输入模式;
• I:在当前光标所在行的行首,转换为输入模式
• A:在当前光标所在行的行尾,转换为输入模式
• 输入-->编辑:
• ESC
• 编辑-->末行:
• :
• 末行-->编辑:
• ESC, ESC (按两次ESC)
末行模式 shift+冒号,末行模式也可以进行查找,比如 :/after,查找词after
末行模式允许我们对文档输入一些命令,并不会改变文档本身的内容
编辑模式
• 移动光标
• 删除&替换单个字符
• 删除命令
• 复制粘贴
• 撤销&重做
• 移动光标
• 字符
• h: 左;j: 下;k: 上;l: 右
• 单词
• w: 移至下一个单词的词首
• e: 跳至当前或下一个单词的词尾
• b: 跳至当前或前一个单词的词首
• 行内
• 0: 绝对行首
• ^: 行首的第一个非空白字符
• $: 绝对行尾
• 行间
• G:文章末尾
• 3G:第3行
数字+g,组合键,要快速按,就像打游戏的组合键一样
• gg:文章开头
• 翻屏
• ctrl:f,b
• 删除&替换单个字符
• x:删除光标位置字符
• 3x:删除光标开始3个字符
注意是下方没有 insert的编辑模式
• r:替换光标位置字符
• 删除命令 : d ,2d
• dw,dd
d 删除一行,2d删除两行
dw 删除一个单词,dd和d是一样的效果
• 复制粘贴&剪切
• yw,yy
• p
• P
yw就是复制一个单词,和dw相似,单行一个d或者y就可以,加上行数时一定要dd或者yy
• 撤销&重做
• u 撤销
• ctrl+r 重做 撤销的操作
• . 重复上一步的操作
末行模式
• set:设置
• set nu number
• set nonu nonumber
• set readonly (设置只读)
• /:查找
• /after
• n,N (N向上查找,n向下查找)
• ?向上查找
• !:执行命令
• :!ls -l /
• s查找并替换
• s/str1/str2/gi
• /:临近s命令的第一个字符为边界字符:/,@,#
• g:一行内全部替换
• i:忽略大小写
• 范围
• n:行号
• .:当前光标行
• +n:偏移n行
• $:末尾行,$-3
• %:全文文本处理文本处理的几种方法
二、文本处理:cut、sort和wc
文本处理的几种方法
• cut
• sort
• wc
• awk
• cut:显示切割的行数据
• f:选择显示的列
• s:不显示没有分隔符的行
• d:自定义分隔符
[root@localhost ~]# cat grep.txt
aazz12121212aazz
aazz 12121212
aaz 12121212
1212 aazz 1212
aa3zz
aa4zz
aaEzz
aamzz
$aazz
aa1234zz
aaopqzz
现在只想显示第一列,应该怎么做?可以用空格分割
[root@localhost ~]# cut -d ' ' -f1 grep.txt
aazz12121212aazz
aazz
aaz
1212
aa3zz
aa4zz
aaEzz
aamzz
$aazz
aa1234zz
aaopqzz
如果显示第一至第三列
[root@localhost ~]# cut -d ' ' -f1-3 grep.txt
如果显示第一 和第三列
[root@localhost ~]# cut -d ' ' -f1,3 grep.txt
aazz12121212aazz
aazz
aaz
1212 1212
aa3zz
aa4zz
aaEzz
aamzz
$aazz
aa1234zz
aaopqzz
但是,只有一列的也在里面,这样的数据叫做“脏数据”,可以用 -s 排掉,就可以找到有三列的数据
[root@localhost ~]# cut -s -d ' ' -f1,3 grep.txt
aazz
aaz
1212 1212
• sort:排序文件的行
• n:按数值排序
• r:倒序
• t:自定义分隔符
• k:选择排序列
• u:合并相同行
• f:忽略大小写
[root@localhost ~]# cat sort.txt
banana 12
apple 1
orange 8
如果这是一个产品价目表,按产品价格对其进行排序,k2 表示按第 2 列排序
[root@localhost ~]# sort -t ' ' -k2 -nr sort.txt
banana 12
orange 8
apple 1
如果把n去掉,默认按字典排序
[root@localhost ~]# sort -t ' ' -k2 -r sort.txt
orange 8
banana 12
apple 1
wc word count 单词计数 -l 按行计数
[root@localhost ~]# cat sort.txt
banana 12
apple 1
orange 8
[root@localhost ~]# wc -l sort.txt
3 sort.txt
如果在写脚本文件,想把计算的行数作为一个变量,想去掉 sort.txt
[root@localhost ~]# cat sort.txt | wc -l
3
这样就只显示 3 了,利用管道,先读出文本,把文本传给wc,这样就不会因为命令把名字显示出来了
三、文本处理:awk
awk
• awk是一个强大的文本分析工具。
• 相对于grep的查找,awk在其对数据分析并生成报告时,显得尤为强大。
• 简单来说awk就是把文件逐行的读入,(空格,制表符)为默认分隔符将每行切片,切开的部分再进行各种分析处理。
如何在程序里使用定义变量?如果不加引号,执行到空格后,会认为是下一个命令
所以需要把整个的命令用反引号引入
[root@localhost ~]# lines=`cat sort.txt | wc -l`
[root@localhost ~]# echo $lines
3
[root@localhost ~]# cat sort.txt | grep banana
banana 12
[root@localhost ~]# cat /etc/passwd
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
……
显示所有用户,bin等用户不是登录用户;
root(用户):x:0:0:root(所在组):/root(登录进来的家目录):/bin/bash(/bin/bash指用户一登录进来能够执行的程序 )
• awk -F '{pattern + action}' {filenames}
• 支持自定义分隔符
• 支持正则表达式匹配
• 支持自定义变量,数组 a[1] a[tom] map(key)
• 支持内置变量
• ARGC 命令行参数个数
• ARGV 命令行参数排列
• ENVIRON 支持队列中系统环境变量的使用
• FILENAME awk 浏览的文件名
• FNR 浏览文件的记录数
• FS 设置输入域分隔符,等价于命令行 -F 选项
• NF 浏览记录的域的个数
• NR 已读的记录数
• OFS 输出域分隔符
• ORS 输出记录分隔符
• RS 控制记录分隔符
• 支持函数
• print、split、substr、sub、gsub
• 支持流程控制语句,类C语言
• if、while、do/while、for、break、continue
• 只是显示/etc/passwd的账户:CUT
• awk -F':' '{print $1}' passwd
[root@localhost etc]# awk -F':' '{print $1}' passwd
root
bin
daemon
adm
……
只显示了第一列用户名,awk 可以理解为一段程序,是一个 命令,-F是定义分隔符,以冒号定义分隔符;'{print $1}'可以理解为一个程序,方法名大括号中包含程序内容,没有方法名,可以理解为匿名方法;print是它的语法; $1表示 第 1 列
• 只是显示/etc/passwd的账户和账户对应的shell,而账户与shell之间以逗号分割,而且在所有行开始前添加列名name,shell,在最后一行添加"blue,/bin/nosh"(cut,sed)
• awk -F':' 'BEGIN{print "name,shell"} {print $1 "," $7} END{print"blue,/bin/nosh"}' passwd
对应的shell是指最末尾那一行
[root@localhost etc]# awk -F':' 'BEGIN{print "name,shell"} {print $1 "," $7} END{print"blue,/bin/nosh"}' passwd
name,shell
root,/bin/bash
bin,/sbin/nologin
daemon,/sbin/nologin
adm,/sbin/nologin
lp,/sbin/nologin
……
tcpdump,/sbin/nologin
malakh,/bin/bash
blue,/bin/nosh
相当于制作了一个表头,表头第1列是name,第2列是执行的shell,最后表尾,blue,/bin/nosh
{print $1 "," $7} 读取每一行的时候,以逗号分隔每一行的值,表头是awk内置的一个函数
BEGIN{print "name,shell"} 只在第一行操作
• 搜索/etc/passwd有root关键字的所有行
• awk '/root/ { print $0}' passwd
比如查找所有 root 的关键行,并显示出来
[root@localhost etc]# awk '/root/ { print $0}' passwd
root:x:0:0:root:/root:/bin/bash
operator:x:11:0:operator:/root:/sbin/nologin
• 统计/etc/passwd文件中,每行的行号,每行的列数,对应的完整行内容
• 统计报表:合计每人1月工资,0:manager,1:worker
• Tom 0 2012-12-11 car 3000
• John 1 2013-01-13 bike 1000
• Vivi 1 2013-01-18 car 2800
• Tom 0 2013-01-20 car 2500
• John 1 2013-01-28 bike 3500
• E
• awk '{gsub("0","manager",$2);gsub("1","worker",$2);;split($3,month,"-");if(month[2]=="01"){name[$1]+=$5}} ND{for( i in name){print i "\t"name[i]}}' awk.txt
现在想统计一月份的,角色是经理的打出m,角色是工人的打出w
[root@localhost ~]# awk '{split($3,date,"-");if(date[2]=="01"){name[$1]+=$5};if($2==0){role[$1]="M"}else{role[$1]="W"}} END{for(i in name){print i"\t"name[i]"\t"role[i]}}' awk.txt
Tom 2500 M
Vivi 2800 W
John 4500 W
'{split($3,date,"-")}‘ 日期是第三列,所有$3,拆开split函数,保存到date变量中,切割符是-切割,所以切割成三列,date现在就是一个数组
类似于C的语法 '{split($3,date,"-");if(date[2]=="01"){name[$1]+=$5}}’ name[$1]+=$5 定义的数组变量,键是第一列Tom人的名字,值是薪水,第5列 ,+=是因为里面有一些重复数据,把重复数据加到一起了
END{for(i in name){print i"\t"name[i]"\t"role[i]}}' awk.txt 最后制作报表,循环打印,\t 制表符
• awk '{
• split($3,date,"-");
• if(date[2]=="01"){
• name[$1]+=$5
• }
• }
• END{
• for(i in name){
• print i "\t" name[i]}
• }' awk.txt
• awk '{split($3,date,"-
");if(date[2]=="01"){name[$1]+=$5;if($2=="0"){role[$1]="M"}else{role[$1]=
"W"}}} END{for(i in name){print i "\t" name[i]"\t" role[i]}}' awk.txt