您的位置:首页 > 运维架构 > Shell

Shell编程之字符截取命令——cut、printf、awk、sed

2019-07-12 16:49 851 查看
  • cut、printf和awk三个字段提取命令与grep命令的区别是:grep命令在文件当中提取符合条件的行,cut命令和awk命令提取列。
  • 一般cut不能独立使用,必须管道符和grep命令联合使用;
  • 只有普通用户的权限才是/bin/bash文件,所有系统用户的权限都是/sbin/nilogin文件

1、cut字段提取命令

[root@localhost ~]# cut [选项] 文件名

选项:

  • -f 列号:提出第几列
  • -d 分隔符:按照指定分隔符分割列。默认时候的分隔符是制表符,即“Tab”键

联系测试文件如下:

[root@root ~]# vi student.txt

ID      Name    gender  Mark
1       Liming  M       86
2       SC      M       90
3       Gao     M       83
  • 示例如下:
[root@root ~]# cut -f 2 student.txt
Name
Liming
SC
Gao
[root@root ~]# cut -f 2,3 student.txt
Name	gender
Liming	M
SC	M
Gao	M

注意:/etc/passwd——>是用户信息文件

[root@root ~]# cut -d ":" -f 1,3 /etc/passwd
root:0
bin:1
daemon:2
adm:3
lp:4
sync:5
shutdown:6
... ...

在管道符中使用grep命令和cut命令的示例:

  • cat /etc/passwd ——> 会列出所有的passwd文件;
  • grep /bin/bash ——> 提取所有包含有/bin/bash的行(只有普通用户的权限才是/bin/bash,所有系统用户的权限都是/sbin/nilogin);
  • grep -v root ——> 取反,在这个命令里面提取不包含有root的行;
  • cut -d “:” -f 1 ——>一冒号[ : ]为分割符提取第一列;
[root@root ~]# useradd user1
[root@root ~]# useradd user2
[root@root ~]# useradd user3
#添加普通用户
[root@root ~]# cat /etc/passwd
#通过cat命令读取/etc下的passwd文档
#在/etc/passwd文档内容如下:
... ...
sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin
tcpdump:x:72:72::/:/sbin/nologin
user1:x:500:500::/home/user1:/bin/bash
user2:x:501:501::/home/user2:/bin/bash
user3:x:502:502::/home/user3:/bin/bash
[root@root ~]# cat /etc/passwd | grep /bin/bash
root:x:0:0:root:/root:/bin/bash
user1:x:500:500::/home/user1:/bin/bash
user2:x:501:501::/home/user2:/bin/bash
user3:x:502:502::/home/user3:/bin/bash
#grep   /bin/bash ——> 提取所有包含有/bin/bash的行(只有普通用户的权限才是/bin/bash,所有系统用户的权限都是/sbin/nilogin);
[root@root ~]# cat /etc/passwd | grep /bin/bash | grep -v root
user1:x:500:500::/home/user1:/bin/bash
user2:x:501:501::/home/user2:/bin/bash
user3:x:502:502::/home/user3:/bin/bash
#grep   -v   root ——> 取反,在这个命令里面提取不包含有root的行;
[root@root ~]# cat /etc/passwd | grep /bin/bash | grep -v root | cut -d ":" -f 1
user1
user2
user3
#cut   -d   ":"   -f   1 ——>一冒号[ : ]为分割符提取第一列;
#接下来把这个值赋给变量,就可以通过循环删除这些用户了的;
  • cut命令的局限:
  • 如果以空格作为分隔符,cut命令是不能识别的,这是cut命令最大的问题。这时候只能用awk命令。能用cut命令尽量用cut命令,,这个比较简单。
  • cut命令只能识别制表符(Tab键),要么就是有分隔符的具体符号,比如,[: , .]都是可以的。

[root@localhost ~]# df -h | cut -d " " -f 1,3

注意:df命令是查看我们分区的使用状况;

示例如下:

[root@root ~]# df -h
Filesystem      Size  Used Avail Use% Mounted on
/dev/sda2        20G  3.6G   15G  20% /
tmpfs           932M  224K  932M   1% /dev/shm
/dev/sda1       985M   46M  889M   5% /boot
/dev/sda3       9.7G  151M  9.0G   2% /home
/dev/sda5       4.9G  138M  4.5G   3% /opt
/dev/sr0        3.6G  3.6G     0 100% /media/RHEL_6.5 x86_64 Disc 1

写一个脚本,用于判断根分区的使用率,如果这个使用率超过80%,让程序报警,告诉管理员硬盘快占满了,需要管理员手工清除。

2、printf命令(格式化打印命令)

严格来讲printf命令并不是字符提取命令,而是应用到awk列提取命令当中的标准输出格式。因此我们学习awk命令之前先学习一下printf命令。

格式:printf ‘输出类型输出格式’ 输出内容

只有加了单元号或双引号,程序才知道不是输出的字符串,而是需要格式化调整的格式符。

输出类型:

  • %ns : 输出字符串。n是数字指代输出几个字符;
  • %ni : 输出整数。n是数字指代输出几个数字;
  • %m.nf : 输出浮点数。m和n是数字,指代输出整数,位数和小数位数。如%8.2f代表共输出8位数,其中2位是小数,6位是整数;

printf基本使用格式符:

基本格式命令:[root@root ~]# printf ‘%s %s %s\n’ 1 2 3 4 5 6

  • ‘%s %s %s\n’ ——> 格式符应该用引号括起来,然后指定我要输出的无内容,如果我们要输出6个字符作为一行,我们就应该写6个%s,如果只写了3个%s系统就认为每3个为一组的形式输出,最后通过\n、\r、\t 来调整输出格式。
    其中,\n、\r、\t 这三个格式是最常用的;

示例如下:

  • [root@localhost ~]# printf %s 1 2 3 4 5 6

输出选项和输出类型并没有用引号括起来的

[root@root ~]# printf %s 1 2 3 4 5  6
123456[root@root ~]#

#输出选项和输出类型并没有用引号括起来的。它会将123456当成一个完整的字符串输出,
#而且没有任何的格式调整,全部都握在了一起。这就是printf最基本的用法。
  • [root@localhost ~]# printf %s %s %s 1 2 3 4 5 6
[root@root ~]# printf %s %s %s 1 2 3 4 5  6
%s%s123456[root@root ~]#

#目的:我想把123当成单独的字符串,456也当成单独的字符串输出。
#实际结果:只有把第一个%s当成了输出选项,后面的所有的内容,包括两个%s被当成了字符串,然后原封不动的输出这不符合我们的需求。
#只有加了单元号或双引号,程序才知道输出的%s不是字符串,而是需要格式化调整的格式符。
  • [root@localhost ~]# printf ‘%s%s%s’ 1 2 3 4 5 6
[root@root ~]# printf '%s %s %s' 1 2 3 4 5  6
1 2 34 5 6[root@root ~]#

#尝试着将123和456分成两组输出,只是没有换行,因为没有换行符。%s之间有空格,程序就知道输出的时候有空格了。怎么让程序知道123是一组,456是一组呢?这时候就要加入换行符来区分。
#只有加了单元号或双引号,程序才知道输出的%s不是字符串,而是需要格式化调整的格式符。
  • [root@localhost ~]# printf ‘%s%s%s\n’ 1 2 3 4 5 6
[root@root ~]# printf '%s %s %s\n' 1 2 3 4 5  6
1 2 3
4 5 6
  • printf命令输出文件内容:
[root@root ~]# cat student.txt
ID	Name	gender	Mark
1	Liming	M	86
2	SC	M	90
3	Gao	M	83
[root@root ~]# printf '%s' student.txt
student.txt[root@root ~]#
#printf命令这时候输出的并不是文件的内容而是文件名,因为printf命令认为student.txt是输出的字符串。

student.txt[root@root ~]# cat student.txt | printf '%s'
[root@root ~]#
#printf命令后面不能直接加文件名,也不能通过管道符来接收第一条命令的执行结果。

[root@root ~]# printf '%s' $(cat student.txt)
IDNamegenderMark1LimingM862SCM903GaoM83[root@root ~]#
# $(cat student.txt)代表我后面执行的内容是一条系统命令 ,先让这一条命令执行,然后通过printf格式化打印这条命令。当然没有调整格式,所以输出是一连串的字符串。

[root@root ~]# printf '%s\t %s\t %s\t %s\n' $(cat student.txt)
ID	 Name	 gender	 Mark
1	 Liming	 M	 86
2	 SC	 M	 90
3	 Gao	 M	 83

3、awk命令

在awk命令的输出中支持print和printf命令:

  • 在awk命令中,我们不能直接调用系统命令cat,也不能直接调用系统命令,我们使用的命令就是printf。
  • print命令:print会在每个输出之后自动加入一个换行符(Linux默认没有print命令);
  • printf命令:printf是标准格式输出命令,并不会自动加入换行符,如果需要换行,必须手动加入换行符;
  • 如果以空格作为分隔符,awk命令可以识别空格作为分隔符的。但是cut命令是不能识别的,这是cut命令最大的问题。这是cut命令的局限性。
  • awk命令也可以使用制表符(Tab键)或者[: , .]这样的分隔符,它的功能比cut命令功能强大。
  • awk命令是一个非常复杂的命令,它不但可以实现字符段的截取。还可以在awk命令中编程调用,如定义函数,进行判断,调用流程控制。也可以成为awk编程。
3.1> awk命令的格式:

# awk ‘条件1{动作1} 条件2{动作2} …’ 文件名

条件(Pattern):

  • 一般使用关系表达式作为条件
  • x > 10 : 判断变量x是否大于10
  • x > 10 : 大于等于
  • x > 10 : 小于等于

动作(Action):

  • 格式化输出。输出某一个内容,
  • 流程控制语句。if,for

关于awk命令说明:

  • 如果符合条件1,就执行动作1;同理,如果符合条件2,就执行动作2。如果没有定义条件,表示不论什么样的数据都执行这个动作,相当于执行文件当中的所有内容。
  • awk命令虽然是列命令,但是它处理数据的时候先读入一行数据,然后把这一行所有的数据都赋给$1($1代表第一列)、$2($2代表第二列)、$3($3代表第三列)…,$0($0代表整个行本身,这一行的所有数据)。然后在判断条件是否符合(这里没有输入任何条件,表示只要数据全部执行这个动作)。
  • awk命令的基本使用,示例说明:
#student.txt文件内容如下:
[root@root ~]# cat student.txt
ID	Name	gender	PHP	Linux	MySQL	Average
1	Liming	M		82	95	    86	    87.66
2	SC	    M		74	96	    87	    85.66
3	Gao	    M		99	83	    93	    91.66

[root@root ~]# awk '{printf $2 $6}' student.txt
NameMySQLLiming86SC87Gao93[root@root ~]# awk '{printf $2 "\t" $6"\n"}' student.txt
Name	MySQL
Liming	86
SC	    87
Gao	    93
#如果没有定义条件,表示不论什么样的数据都执行这个动作,相当于执行{ }里面文件中的所有内容。
#$2代表文件的第二列,$6代表文件的第六列。这里面printf命令再也不是系统命令,而是awk命令里面的动作命令。

#awk命令虽然是列命令,但是它处理数据的时候先读入一行数据,然后把这一行所有的数据都赋
#给$1($1代表第一列)、$2($2代表第二列)、$3($3代表第二列)...,$0($1代表整个行本身,
#这一行的所有数据)。然后在判断条件是否符合(这里没有输入任何条件,表示只要数据全部执行这
#个动作),我们看到了这个时候先输出$2和$6,这个时候回打印第一行的Name和Average。接着把
#第二行数据读入到awk当中。后面的一次类推。
  • awk命令可以识别空格作为分隔符的,示例如下:
[root@root ~]# df -h
Filesystem      Size  Used Avail Use% Mounted on
/dev/sda2        20G  3.6G   15G  20% /
tmpfs           932M   76K  932M   1% /dev/shm
/dev/sda1       985M   46M  889M   5% /boot
/dev/sda3       9.7G  151M  9.0G   2% /home
/dev/sda5       4.9G  138M  4.5G   3% /opt
/dev/sr0        3.6G  3.6G     0 100% /media/RHEL_6.5 x86_64 Disc 1

[root@root ~]# df -h | awk '{print $1 "\t" $5 "\t" $6}'
Filesystem	Use%	Mounted
/dev/sda2	20%	/
tmpfs	1%	/dev/shm
/dev/sda1	5%	/boot
/dev/sda3	2%	/home
/dev/sda5	3%	/opt
/dev/sr0	100%	/media/RHEL_6.5

[root@root ~]# df -h | awk '{printf $1 "\t" $5 "\t" $6 "\n"}'
Filesystem	Use%	Mounted
/dev/sda2	20%	/
tmpfs	1%	/dev/shm
/dev/sda1	5%	/boot
/dev/sda3	2%	/home
/dev/sda5	3%	/opt
/dev/sr0	100%	/media/RHEL_6.5

print和printf的区别:

  • 在系统Linux命令当中,是没有print命令的,只有printf命令。但是在awk命令当中print命令和printf命令都可以使用;
  • 区别就是:printf命令在不会在每行末尾自动加入换行符,需要手工来加入换行符。而print命令会在每行末尾自动加入换行符
  • 提取CPU的使用率,示例如下
[root@root ~]# df -h
Filesystem      Size  Used Avail Use% Mounted on
/dev/sda2        20G  3.6G   15G  20% /
tmpfs           932M  224K  932M   1% /dev/shm
/dev/sda1       985M   46M  889M   5% /boot
/dev/sda3       9.7G  151M  9.0G   2% /home
/dev/sda5       4.9G  138M  4.5G   3% /opt
/dev/sr0        3.6G  3.6G     0 100% /media/RHEL_6.5 x86_64 Disc 1[root@root ~]# df -h | grep sda2
/dev/sda2        20G  3.6G   15G  20% /
[root@root ~]# df -h | grep sda2 | awk '{print $5}'
20%
[root@root ~]# df -h | grep sda2 | awk '{print $5}' | cut -d "%" -f 1
20
3.2> BEGIN命令——作用是开始:

# awk ‘BEGIN {printf “This is a transcript \n” } {printf $2 “\t” $6 “\n”}’ student.txt

关于BEGIN命令的相关解释:

  • BEGIN是指,在所有的数据读取之前,执行{printf “This is a transcript \n” } 的内容,并且{printf “This is a transcript \n” }只执行一次。
  • BEGIN是写在了大括号前的,所以BEGIN是一个条件,只有满足了BEGIN条件,才会执行 {printf “This is a transcript \n” } 里面的动作,接着处理后面的数据。

示例如下:

[root@root ~]# awk 'BEGIN{print "test !"} {print $2 "\t" $5}' student.txt
test !
Name	Linux
Liming	95
SC	96
Gao	83
3.3> FS内置变量

# cat /etc/passwd | grep “/bin/bash” | awk ‘BEGIN {FS=":"} {printf $1 “\t” $3 “\n”}’

FS内置变量的作用是:指定分隔符,默认的分隔符是空格和制表符(Tab键)。

FS内置变量,示例如下:

[root@root ~]# awk '{FS=":"} {print $1 "\t" $3}' /etc/passwd
root:x:0:0:root:/root:/bin/bash
bin	1
daemon	2
adm	3
... ...
#但是第一行没有处理的,从第二行开始处理。
#awk命令先执行的时候,先读入“root:x:0:0:root:/root:/bin/bash	”数据,然后在执行后面的操作。
#在执行{FS=":"} 分隔符之前,第一行的数据已经读入,因此这个数据的处理已经来不及了。因此,在FS执行之前加入BEGIN变量。

[root@root ~]# awk 'BEGIN{FS=":"} {print $1 "\t" $3}' /etc/passwd
root	0
bin	1
daemon	2
adm	3
lp	4
#在FS执行之前加入BEGIN变量。在读取第一条数据之前,先先把分隔符写进去,接着处理后面的数据。这样所有的数据都按照我们的要求执行了。
3.4> END命令

# awk ‘END{printf “The End \n” } {printf $2 “\t” $6 “\n”}’ student.txt

END命令是指当所有的命令都执行完之后,在执行END后面的动作。

示例如下:

[root@root ~]# awk 'BEGIN{FS=":"} END{print"AAAAAAAAAAAAAAAAA"} {print $1 "\t" $3}' /etc/passwd
... ...
tcpdump	72
user1	500
user2	501
user3	502
AAAAAAAAAAAAAAAAA
#所有的命令处理完之后,会执行一条END命令。
3.5> 关系运算符

# cat student.txt | grep -v Name | \awk ‘$6>=87{printf “\n”}’

示例如下:

[root@root ~]# cat student.txt
ID	Name	gender	PHP	Linux	MySQL	Average
1	Liming	M	82	95	86	87.66
2	SC	M	74	96	87	85.66
3	Gao	M	99	83	93	91.66
[root@root ~]# cat student.txt | grep -v Name | awk '$6 >= 87{printf $2 "\n"}'
SC
Gao
#[ grep -v Name]反选取消“ID	Name	gender	PHP	Linux	MySQL	Average”行。
#在执行{printf $2 "\n"}内容之前,先判断$6 >= 87知否成立。

4、sed命令

前面三个命令(cut、printf、awk)都是字段截取的。grep命令是进行行截取的,而cut命令和awk命令是进行列截取。

sed命令是一种几乎包括所有UNIX(包括Linux)的轻量级流编辑器。sed主要是对文本或者是文件当中的数据进行选取、替换、删除、新增命令。相当于一个编译器

  • vi命令只能修改文件。但是vi不能够直接修改结果里面的内容,如果要修改命令的输出,先把命令保存到文件当中,然后用vi去修改这个文件。这是vi的局限。
  • sed不但可以修改文件,而且可以通过管道符来接收数据修改 命令的结果。话句话说,不需要将命令的结果先写到文件当中,而是用管道符直接接收直接用sed命令进行编辑。也就是说,sed命令支持管道符操作。

1、sed命令格式:

[root@localhost ~]# sed [选项] ‘[动作]’ 文件名

  • 选项:
  • -n : 一般sed命令会把所有数据都输出到屏幕。如果加入此选择,则只会把经过sed 命令处理的行输出到屏幕。
  • -e : 允许输入数据应用多条sed命令编辑。
  • -i : 用sed的修改结果直接修改读取数据文件(不但进行修改,而且把修改的结果保存到文件当中),而不是由屏幕输出。

关于以上选项的解释:

  • 选项-i,不但进行修改而且把修改的结果保存到文件当中。也就说不加-i,你的数据只是输出的时候进行了改变,但是不会影响原文件里面的数据。如果加了-i,不但输出数据进行了更改,你的源文件数据也会进行更改。
  • 一般情况下,要用p动作(即,打印输出动作)都要跟-n联合使用,否则,会全部打印。
  • 动作:
    (1)示例说明:查看、删除

[root@localhost ~]# sed ‘2p’ student.txt
#查看文件第二行

[root@localhost ~]# sed -n ‘2p’ student.txt

[root@localhost ~]# sed ‘2,4d’ student.txt
#删除第二行到第四行的数据,但不修改文件本身

[root@root ~]# sed '2p' student.txt
ID	Name	gender	PHP	Linux	MySQL	Average
1	Liming	M	82	95	86	87.66
1	Liming	M	82	95	86	87.66
2	SC	M	74	96	87	85.66
3	Gao	M	99	83	93	91.66
[root@root ~]# sed -n '2p' student.txt
1	Liming	M	82	95	86	87.66
#注意以上命令的输出都不会影响文件的本身,sed命令只要不加-i选项,所有的操作都不影响文件本身。只是影响了屏幕输出。

[root@root ~]# df -h |sed -n '2p'
/dev/sda2        20G  3.6G   15G  20% /
#sed命令可以放在管道符之后处理,接收前一个命令的执行结果。vi就做不到这一点。

[root@root ~]# sed '2,4d' student.txt
ID	Name	gender	PHP	Linux	MySQL	Average
[root@root ~]# sed '2,3d' student.txt
ID	Name	gender	PHP	Linux	MySQL	Average
3	Gao	M	99	83	93	91.66
[root@root ~]# cat student.txt
ID	Name	gender	PHP	Linux	MySQL	Average
1	Liming	M	82	95	86	87.66
2	SC	M	74	96	87	85.66
3	Gao	M	99	83	93	91.66
#但是文件的内容并没有更改。没有加i选项,仅仅影响的是屏幕输出。

(2)示例说明:追加、插入

[root@localhost ~]# sed ‘2a hello’ student.txt
#在第二行后追加hello

[root@localhost ~]# sed ‘2i hello
world’ student.txt
#在第二行前插入两行数据。其中\表示数据内有输完,还有第二行

[root@root ~]# sed '2a zuobi' student.txt
ID	Name	gender	PHP	Linux	MySQL	Average
1	Liming	M	82	95	86	87.66
zuobi
2	SC	M	74	96	87	85.66
3	Gao	M	99	83	93	91.66
[root@root ~]# sed '2i canglaoshi \
> bolaoshi' student.txt
ID	Name	gender	PHP	Linux	MySQL	Average
canglaoshi
bolaoshi
1	Liming	M	82	95	86	87.66
2	SC	M	74	96	87	85.66
3	Gao	M	99	83	93	91.66
#和上面一样。修改只是命令的输出,没有修改原文件。

(3)示例说明:行替换

[root@localhost ~]# sed ‘2c No such person’ student.txt
#数据替换。动作c是替换整个行。

[root@root ~]# cat student.txt
ID	Name	gender	PHP	Linux	MySQL	Average
1	Liming	M	82	95	86	87.66
2	SC	M	74	96	87	85.66
3	Gao	M	99	83	93	91.66
[root@root ~]# sed '4c No such person' student.txt
ID	Name	gender	PHP	Linux	MySQL	Average
1	Liming	M	82	95	86	87.66
2	SC	M	74	96	87	85.66
No such person

(4)示例说明:字符串替换

[root@root ~]# sed ‘s/旧字串/新字串/g’ 文件名

例如:
[root@root ~]# sed ‘3s/74/99/g’ student.txt
#在第三行中,把74 换成99
[root@root ~]# [root@root ~]# sed -i ‘3s/74/99/g’ student.txt
#sed操作的数据直接写入文件

[root@root ~]# sed -e ‘s/Liming//g ; s/Gao//g’ student.txt
#同时把“Liming”和“Gao”替换为空

[root@root ~]# cat student.txt
ID	Name	gender	PHP	Linux	MySQL	Average
1	Liming	M	82	95	86	87.66
2	SC	M	74	96	87	85.66
3	Gao	M	99	83	93	91.66
[root@root ~]# sed '3s/74/99/g' student.txt
ID	Name	gender	PHP	Linux	MySQL	Average
1	Liming	M	82	95	86	87.66
2	SC	M	99	96	87	85.66
3	Gao	M	99	83	93	91.66
[root@root ~]# cat student.txt
ID	Name	gender	PHP	Linux	MySQL	Average
1	Liming	M	82	95	86	87.66
2	SC	M	74	96	87	85.66
3	Gao	M	99	83	93	91.66
[root@root ~]# sed -i  '3s/74/99/g' student.txt
[root@root ~]# cat student.txt
ID	Name	gender	PHP	Linux	MySQL	Average
1	Liming	M	82	95	86	87.66
2	SC	M	99	96	87	85.66
3	Gao	M	99	83	93	91.66
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: