Shell编程基础知识


一、Shell基础

  • 工作在Linux内核与用户之间的解释程序,相当于操作系统的外壳,向Linux内核传达指令的翻译官,通常指BASH(/bin/bash)
  • Windows下的Shell解释器:C:\Windows\System32\cmd.exe
  • 登录Shell环境
    用户登录的第一个程序是最常见的“Linux命令行”环境,以交互式运行,用户每输入一个指令,立即解释并执行。
    [root@localhost ~]# echo $SHELL
    /bin/bash

1.1 交互式 VS 非交互式

交互式:人工干预,智能化程度高,逐条解释执行,效率低

非交互式:需要提前设计,智能化难度大;批量执行效率高;方便后台运行

Shell脚本:提前写好可执行代码,用来完成特定任务的文件

给Shell脚本添加可x执行权限chmod +x xxx.sh

调试Shell脚本的方法:1、直接观察执行过程,执行中的命令输出,报错信息,与用户的交互;2、开启调试模式sh -x 脚本文件

  • 免交互
    添加选项–stdin,如,
    修改密码:
    passwd --stdin 新密码,从键盘读入,
    echo 1234567 | passwd --stdin 7654321,由echo命令给出
  • 忽略无关输出
    黑洞设备/dev/null,相当于只能写入不能读出的单向文件,存放到其中的数据都会丢失
    用法:&> /dev/null
    如echo 12345678 | passwd --stdin &> /dev/null,不会有任何输出
  • 记录错误输出
    根据需要,可以将出错信息保存到指定文件,-针对后台脚本的有效排错手段,-适用于不便交互但又需要查看报错的情况
    用法:2> /路径/文件
    如:useradd lgz 2> /tem/err.log 保存报错信息

1.2 命令组合运用

分隔多条命命

1、使用分号,命令1;命令2;命令3;,依次执行,只有先后,没有逻辑关系

典型应用:开启某个服务,并将其设置为开机启动。如service vsftpd restart;chkconfig vsftpd on

2、逻辑“与”分隔,使用&&,命令1 && 命令2 && 命令3,逻辑关系为,期望所有的命令都能执行成功,一旦出现失败,后续命令不再执行。

典型应用:源代码编译安装软件包时,编译、安装过程:make %% make install

3、逻辑‘“或”分隔,使用||,命令1 || 命令2 || 命令3 ,任何一条命令成功都符合期望,只有在前面的命令失败时,后续的命令才会执行。

典型应用:针对前置命令失败的情况,设置补充任务。id lgz || useradd lgz //若用户lgz不存在,则创建。

4、组合逻辑分隔,如命令1 && 命令2 || 命令3,当命令1 成功会执行命令2忽略命令3,当命令1执行失败会忽略命令2执行命令3。

id lgz &> /dev/null && echo YES || echo NO	//判断用户是否存在
grep -q -e 'vmx|svm' /proc/cpuinfo %% echo YES || echo NO	//判断CPU是否支持虚拟化

1.3 管道

使用|,命令1 | 命令2 | 命令3将命令的屏幕输出信息交给下一个命令处理,后续命令能够正确处理传来的文本,否则无意义。

应用实例1:

计算配置目录/etc下包括多少普通文件

命令1:使用find在/etc目录下递归查找,列出所有的普通文件

命令2:将查找结果交给wc统计行数

find /etc -type f | wc -l

应用实例2:

统计正处于监听状态的TCP端口数

命令1:使用netstat命令列出所有的TCP连接信息

命令2: 将查找结果交给grep过滤,计算状态为LISTEN的连接数

netstat -anpt | grep -c "LISTEN"

1.4 标准输入和输出

Linux的基本思想:一切皆文件

I/O交互设备:

标准输入:从此设备接收用户输入的数据

标准输出:通过此设备向用户报告正常的命令输出结果

标准错误:通过此设备报告执行中的错误信息

类型 设备文件 文件描述号 默认设备
标准输入 /dev/stdin 0 键盘
标准输出 /dev/stdout 1 显示器
标准错误 /dev/stderr 2 显示器

1.5 重定向

从新指定命令执行时I/O设备的方向,-不使用默认的键盘、显示器,-该用指定的文本文件。

类型 操作符 用途
重定向输入 < 将文本输入来源由键盘改为制定的文件
重定向输出 > 或>> 将命令行的正常执行输出保存到文件,而不是直接显示在屏幕上,>是覆盖文件内容,>>是追加文件内容
重定向错误 2>或2>> 将命令行执行的错误输出信息保存到文件,而不是直接显示在屏幕上,2>是覆盖文件内容,2>>是追加文件内容
混合重定向 &> 相当于>和2>覆盖到同一个文件

重定向输入,比如使用mail命令发送电子邮件时

vim /root/mail.txt //提前写好邮件内容

mail -s ‘A test Mail’ root@localhost < /root/mail.txt //读取文件内容并发送邮件

重定向输出,比如保存某个命令的正常执行结果到文件

echo 'hhhh' > /etc/resolv.conf //覆盖目标文件内容
echo 'hhhh' >> /etc/resolv.conf //追加目标文件内容

重定向错误,比如保存某个命令的错误执行结果到文件

ls -ld /errroot /root 2> /root/err.log //只记录错误信息

混合重定向

ls -ld /root /rootx > /root/ls.log 2> /root/err.log //分别保存到不同的文件
ls -ld /root /rootx &> /dev/null //忽略所有的输出,等同于: > /dev/null 2>&1

1.6 变量

定义一个变量:var1=CentOS //定义一个变量,并赋值为CentOS

引用一个变量的值: 变 量 名 或 者 变量名 或者 {变量名}

取消变量:unset var1

变量的分类

存储类型:Shell不作为高级编程语言,对存储类型的要求比较松散

使用类型:

类型 说明
环境变量 变量名通常都大写,由系统维护,用来设置工作环境,其中只有个别变量用户可以直接修改
位置变量 由bash内置,用来存储在执行脚本时提供的命令行参数
预定义变量 由bash内置,一类由特殊用途的变量,可以直接调用,但不能直接赋值和修改
自定义变量 由用户自行设置,修改和使用

环境变量:/etc/profile、~/.bash_profile

相关操作:

env:  列出所有的环境变量

set:  列出所有变量

常见的环境变量:

PWD、PATH、USER、LOGNAME、UID、SHELL、HOME、PS1、PS2、...

位置变量:$n,n为序号,$1、 2 、 . . 2、.. 2..{10}、…

查看第1和第10个位置的参数:echo $1 ${10}

vim a.sh

#!/bin/bash
echo $1 ${10}

./a.sh 1 2 3 4 5 6 7 8 9 10
1 10

预定义变量:用来保存脚本程序的执行信息,直接使用,不能赋值

常见的预定义变量

变量名 含义
$0 当前所在进程或脚本名
$$ 当前运行进程的PID号
$? 命令执行后的返回状态值,0表示正常,1或其他表示异常
$# 已加载的位置变量个数
$* 所有位置变量的值

1.6.1 变量值及其范围控制

1.6.1 变量值及其范围控制

引号在赋值中的作用

单引号和双引号:

“字符串”:在双引号内允许$扩展,可调用其他变量的值,出现特殊字符时用\符合转义,当变量值不包括空格,制表符,双引号通常被省略。

‘字符串’:所有字符串均视为字符本身,不允许转义

read取值的用法:read 变量名,read -p “提示信息” 变量名

$ read -p "login:" USER_NAME
login: lgz
$ echo $USER_NAME
lgz

read静默取值:read -s -p “提示信息” 变量名

$ read -s -p "password:" PASSWORD
password: 
$ echo $PASSWORD
12345678

变量的作用范围:局部变量,全局变量

发布全局变量export 变量名=变量值

取消全局变量export -n 变量名

1.6.2 整数运算,自增,随机数,seq、小数运算

使用$[表达式]

[root@localhost src]# X=23;y=45
[root@localhost src]# echo $[X+y*2]		//混合运算
113
[root@localhost src]# echo $[y%4]		//求模
1
[root@localhost src]# echo $[y**3]		//y的3次方
91125

使用let命令操作变量

[root@localhost src]# X=3;Y=5
[root@localhost src]# let X++,Y/=2
[root@localhost src]# echo $X,$Y
4,2

随机数$RANDOM

$RANDOM会随机返回0-32767之间某个整数

[root@localhost src]# echo $RANDOM
14357
[root@localhost src]# echo $RANDOM
10710

整数序列命令seq会随机给出数组

[root@localhost src]# seq 5
1
2
3
4
5
[root@localhost src]# seq 2 5
2
3
4
5
[root@localhost src]# seq 2 5 20
2
7
12
17
[root@localhost src]# seq -s' ' 2 5 20
2 7 12 17
[root@localhost src]# seq -w 2 5 20
02
07
12
17
[root@localhost src]# seq -w -s' '  2 5 20
02 07 12 17

利用bc进行小数运算,和对比大小

[root@localhost src]# echo "$X*3.6" | bc
12.2
[root@localhost src]# echo "scale=4;$X*3.6;5/3" | bc		//保留4位小数
12.24
1.6666
[root@localhost src]# echo "$X<=$Y" | bc		//比较大小,成立返回1,不成立返回0
1
[root@localhost src]# echo "$X>$Y" | bc
0

二、字符串处理

使用expr substr

[root@localhost src]# var1="abcdefg"
[root@localhost src]# expr substr $var1 1 2		//从第一个截取,截取两个字符
ab
[root@localhost src]# expr substr $var1 3 2
cd

cut命令使用:命令输出 | cut -c 起始位置-结束位置;命令输出 | cut -d ‘分隔符’ -f 字段编号

[root@localhost src]# X=qwerrttryty
[root@localhost src]# echo $X | cut -c 4-5
rr
[root@localhost src]# echo $X | cut -d 't' -f1
qwerr

使用${}表达式截取

${var1:起始位置:截取长度}

${var1:截取长度}

[root@localhost src]# var1=helloworld
[root@localhost src]# echo ${var1:1:4}
ello
[root@localhost src]# echo ${var1::4}
hell
[root@localhost src]# echo ${var1:2:4}
llow
[root@localhost src]# echo ${var1:4:2}
ow

注意:起始位置为0

使用${}表达式替换

${var1/old/new}

${var1//old/new} //替换所有

[root@localhost src]# var1=helloworld
[root@localhost src]# echo ${var1/h/H}
Helloworld
[root@localhost src]# echo ${var1//o/O}
hellOwOrld

tr单字替换

命令输出 | tr ‘abc’ ‘ABC’

命令输出 | tr -d ‘ABC’

[root@localhost src]# head -1 /etc/passwd | tr 'or' 'OR'
ROOt:x:0:0:ROOt:/ROOt:/bin/bash
[root@localhost src]# head -1 /etc/passwd | tr -d ':'		//删除所有:
rootx00root/root/bin/bash

dirname和basename

[root@localhost src]# dirname "/etc/passwd"
/etc
[root@localhost src]# var1="/usr/local/"
[root@localhost src]# dirname $var1
/usr
[root@localhost src]# basename "/etc/passwd"
passwd
[root@localhost src]# basename $var1
local

随机字符串

[root@localhost src]# uuidgen
e8b5008a-8f41-47a2-bb06-fd21da5142ec
[root@localhost src]# head -1 /dev/urandom
醨ùd`õ^ŽÒgÕú¥³P#§	‡P_
äÜÀ]©ܪ¨¡%¢÷݉2ǰºïùÕph¤^z-†#ÍÀÜ#z‰onÙOÓà Ž¨™!¤€ž¦ñkÞÊ$$³¶,”@•zóJ>ñü|ý*³âscb}=+¸šA¼n.9E#E÷IͧjZc’!˜�í
[root@localhost src]# head -1 /dev/urandom | md5sum
8ab2dd5dbae5b198a4a4eced571ac134  -
[root@localhost src]# echo $RANDOM | md5sum | cut -c -8
0b6952b3
[root@localhost src]# uuidgen | md5sum | cut -c -8
53fd9d89
[root@localhost src]# uuidgen | md5sum | cut -c -16
ea1fcd6d03aa18b1
[root@localhost src]# uuidgen | cut -c -16
bf148425-b257-41
[root@localhost src]# uuidgen | tr -d '-' | cut -c -16
30ef108b4f9340b2

提取命令输出

使用反撇号:var1=可执行命令

[root@localhost src]# var1=`which tr`
[root@localhost src]# echo $var1
/usr/bin/tr

使用$(可执行命令),对比反撇号,有个好处,可以嵌套

[root@localhost src]# var1=$(which tr)
[root@localhost src]# echo $var1
/usr/bin/tr

使用实例

1、列出/boot/下超过3M的文件的详细属性

[root@localhost src]# ls -lh $(find /boot/ -size +3M)
-rw-------. 1 root root  53M 9月   9 20:11 /boot/initramfs-0-rescue-bb2914001a4c43e39d3328c755f39261.img
-rw-------. 1 root root  21M 9月   9 20:13 /boot/initramfs-3.10.0-862.el7.x86_64.img
-rw-------. 1 root root  13M 9月   9 20:14 /boot/initramfs-3.10.0-862.el7.x86_64kdump.img
-rw-------. 1 root root 3.3M 4月  21 00:57 /boot/System.map-3.10.0-862.el7.x86_64
-rwxr-xr-x. 1 root root 6.0M 9月   9 20:11 /boot/vmlinuz-0-rescue-bb2914001a4c43e39d3328c755f39261
-rwxr-xr-x. 1 root root 6.0M 4月  21 00:57 /boot/vmlinuz-3.10.0-862.el7.x86_64

三、流程控制

3.1 if

if

if [ 条件 ]
then
    ...
fi

if-else

例子1:判断目录存在

dir="/usr/local/src/"
if [ ! -d $dir ]
then
    echo "${dir}目录已经存在"
else
    echo "${dir}目录不存在"
fi

例子2:主机ip是否ping通

dir="/usr/local/src/"
ping -c 3 -i 0.2 -W 3 $1 &> /dev/null
if [ $? -eq 0 ]; then
    echo "$1 is up"
else
    echo "$1 is down"
fi

[root@localhost src]# ./t.sh g.cn
g.cn is up

If-elif-else

if [ 条件 ];then
	...
elif [ 条件 ];then
	...
else
	...
fi

3.2 for

for 变量名 in 列表
do
	...
done

例子,读取文件u.txt每行的内容

u.txt

张三
李四
王五

ULIST=$(cat ./u.txt)
for u in $ULIST
do
    echo $u
done

执行

[root@localhost src]# ./t.sh
张三
李四
王五

3.3 while

while [ 条件 ]
do
   ...
done

3.4 中断,退出控制

语句 含义
break 跳出循环流程,不再循环
continue 本次循环终止,开启新的循环
exit 10 退出脚本,并返回状态10

3.5 case

case 变量值 in
模式1)
	... ;;
模式2)
	... ;;
*)
	默认 ;;
esac

四、函数

funaction 函数名{
    ...
}

或者

函数名(){
    ...
}

函数调用:函数名 参数 参数

五、文本的排序和统计

ls -S 大小降序

ls -t 时间降序

-r 反序排序

ls -lh -S 目录

ls -lh -t 目录

ls -lh -rS 目录

在一个目录下找出最大文件:ls -S |head -1

最旧的文件:ls -t | tail -1

uniq去重,只能去掉紧挨着的重复行,间隔的重复行去不了

uniq 文件

命令 | uniq

排序

sort 文件

命令 | sort

常用选项:

-u 去除重复行

-n 按数字顺序升序排序

-r 反向排序

-k 优先对第几行的内容排序

例子1:对文件内内容排序且去重处理

sort -u 文件

例子2:查看进程,根据%MEM按数字降序排序

[root@localhost src]# ps -aux | head -1;ps -aux | sort -nr -k4
USER        PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root        912  0.0  0.9 573816 17028 ?        Ssl  20:27   0:01 /usr/bin/python -Es /usr/sbin/tuned -l -P
polkitd     657  0.0  0.6 538436 12088 ?        Ssl  20:27   0:00 /usr/lib/polkit-1/polkitd --no-debug
root        654  0.0  0.5 704184  9584 ?        Ssl  20:27   0:00 /usr/sbin/NetworkManager --no-daemon
root        497  0.0  0.3 127256  6100 ?        Ss   20:27   0:00 /usr/sbin/lvmetad -f
root       1417  0.0  0.3 158800  5668 ?        Ss   20:44   0:01 sshd: root@pts/0
root          1  0.0  0.3 193484  6488 ?        Ss   20:27   0:03 /usr/lib/systemd/systemd --switched-root --system --deserialize 22
root        914  0.0  0.2 216376  4260 ?        Ssl  20:27   0:00 /usr/sbin/rsyslogd -n
root        910  0.0  0.2 112796  4296 ?        Ss   20:27   0:00 /usr/sbin/sshd -D

先排序,在去重sort 文件 | uniq

统计重复的次数-c: sort 文件 | uniq -c

反序输出文本行:

[root@localhost src]# head -3 /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
[root@localhost src]# head -3 /etc/passwd | tac
daemon:x:2:2:daemon:/sbin:/sbin/nologin
bin:x:1:1:bin:/bin:/sbin/nologin
root:x:0:0:root:/root:/bin/bash
[root@localhost src]# tac /etc/passwd
daemon:x:2:2:daemon:/sbin:/sbin/nologin
bin:x:1:1:bin:/bin:/sbin/nologin
root:x:0:0:root:/root:/bin/bash

删除末尾5行tac 文件 | sed ‘1,5d’ | tac

反序输出字符串rev

[root@localhost src]# head -3 /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
[root@localhost src]# head -3 /etc/passwd | rev
hsab/nib/:toor/:toor:0:0:x:toor
nigolon/nibs/:nib/:nib:1:1:x:nib
nigolon/nibs/:nibs/:nomead:2:2:x:nomead

例子:输出文件中每一行末尾的8个字符

[root@localhost src]# cat /etc/hosts
127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6
^C[root@localhost src]# rev  /etc/hosts | cut -b -8 | rev
ldomain4
ldomain6

tee整合重定向

作用:输出结果且保存到文件

[root@localhost src]# uname -r | tee un.txt
3.10.0-862.el7.x86_64
[root@localhost src]# cat un.txt
3.10.0-862.el7.x86_64
[root@localhost src]#

unix2dos和dos2unix

Windows 和Unix有些字符格式不兼容,需要转换

安装yum -y install dos2unix unix2dos

用法:dos2unix 文件

六、xargs多参数处理

很多命令给出的参数长度最大大概2.5M

用法1:提供参数的命令 | xargs 目标命令

用法2:xargs --arg-file=提供参数的文件 目标命令

如:find / | xargs ls -lh

文件改名,复制等操作

[root@localhost src]# ls *.txt
un.txt  u.txt
[root@localhost src]# ls *.txt | xargs -I{} cp {} {}.bak	//{}表示一个对象
[root@localhost src]# ls *.txt *.bak
un.txt  un.txt.bak  u.txt  u.txt.bak

-d指定分隔符

[root@localhost src]# head -1 /etc/passwd
root:x:0:0:root:/root:/bin/bash
[root@localhost src]# head -1 /etc/passwd | xargs -d: -I{} echo {}
root
x
0
0
root
/root
/bin/bash

七、expect语气交互控制

安装:yum -y install expect

例子,自动ssh登录脚本

#!/usr/local/bin/expect
# set 设置变量
set host 192.168.230.128
set user root
set password rootlgz
# spawn 发起交互进程
spawn ssh $user@$host
# 预计会出现"(yes/no)?",出现就输入yes \r时回车键,以此类推
expect "(yes/no)?" { send "yes\r" }
expect "*password:" { send "$password\r" }
expect "\[$user\@" { }
# interact保留进程,expect eof 结束指令
interact

八、正则表达式

egrep测试工具

用法

egrep [选项] ‘正则表达式’ 文件

前置命令 | egrep [选项] ‘正则表达式’

常见选项:

-i 忽略大小写

-q 静默,无任何输出,一般用于检测(根据$?值判断有无匹配)

-n 显示出匹配的结果所在的行号

^abc 以abc开头

abc$ 以abc结尾

^$ 空行

. 单个字符

例如:

[root@localhost src]# egrep '^root' /etc/passwd	//以root开头的行
root:x:0:0:root:/root:/bin/bash
[root@localhost src]# egrep 'ash$' /etc/passwd	//以ash结尾的行
root:x:0:0:root:/root:/bin/bash
[root@localhost src]# egrep 'oot:' /etc/passwd	//包含oot:的行
root:x:0:0:root:/root:/bin/bash
operator:x:11:0:operator:/root:/sbin/nologin

类型 含义 示例 说明

  • 最少匹配一次	a+,(abc)+   	一个或者多个连续的a,一个或多个连续的abc         
    

? 最多匹配一次 a?,(abc)? 一个或者多个a,一个或者多个abc

  • 匹配任意次数	a*,(abc)* .*	零个或多个连续的a,零个或多个连续的abc,任意长度任意字符串
    

元字符{},限定匹配次数

类型 含义 示例 说明
{n} 匹配n次 (ab){3} 匹配ababab
{n,m} 匹配n-m次 (ab){1,3} 匹配 ab、abab、ababab
{n,} 匹配至少n次 (ab){2,} 匹配2个及以上的ab, abab、ababab 、…

元字符[],限定匹配次数

匹配范围内的单个字符,[]内有^表示取反

其他元字符

类型 含义 示例 说明
() 组合为一个整体 ab{1,3}匹配ab abb abbb,(ab){1,3}匹配ab abab ababab
| 或者 root|bin匹配root bin
\b 单词的边界 \broot\b匹配单词root,不匹配keroot等其他
< 单词的开头 <th以th开头的单词
> 单词的结尾 <root>匹配单词root,不匹配keroot等其他

九、awk文本过滤

基本用法:

格式1:`前置命令 | awk [选项] '[条件]{编辑指令}'`
格式2:`awk [选项] ‘[条件]{编辑指令}’ 文件`

例子:

例子1,以:分割,取出第1和第2列的值

[root@localhost src]# 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
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
operator:x:11:0:operator:/root:/sbin/nologin
games:x:12:100:games:/usr/games:/sbin/nologin
ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin
nobody:x:99:99:Nobody:/:/sbin/nologin

[root@localhost src]# awk -F ":" '{print$1,$2}' /etc/passwd
root x
bin x
daemon x
adm x
lp x
...

awk的内置变量

变量 用途
FS 保存或者设置字段分隔符,例如 FS=“:”
$n 指定分隔的第几个字段,如$1 , $3分别表示第一第三列
$0 当前读入的整行文本内容
NF 记录当前处理行的字段个数(列数)
NR 记录当前已读入行的个数(行数)
FNR 保存当前处理行在原文本内的序号(行号)
FILENAME 当前处理的文件名
ENVIRON 调用Shell环境变量,格式:ENVIRON[“变量名”]

查看有几行几列

[root@localhost src]# cat u.txt
name 张三
name 李四
name 王五
[root@localhost src]# awk '{print"第"FNR"行","有"NF"列"}' u.txt
第1行 有2列
第2行 有2列
第3行 有2列

输出每行最后一个字段的内容

[root@localhost src]# awk '{print"Last:"$NF}' u.txt
Last:张三
Last:李四
Last:王五

输出当前用户的UID信息

[root@localhost src]# awk -F:  '$1==ENVIRON["USER"]{print$3}' /etc/passwd
0

基本用法操作示例

[root@localhost src]# head -5 /etc/passwd  >> pass.txt
[root@localhost src]# cat pass.txt
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
[root@localhost src]# awk -F: '{print}' pass.txt
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
[root@localhost src]# awk -F: '{print $1}' pass.txt
root
bin
daemon
adm
lp
[root@localhost src]# awk -F: '{print $1,$3}' pass.txt
root 0
bin 1
daemon 2
adm 3
lp 4
[root@localhost src]# awk -F: '{print $1","$3}' pass.txt
root,0
bin,1
daemon,2
adm,3
lp,4

9.1 awk的处理时机

1、行前处理BEGIN{},读入第一行文本之前执行,一般用来初始化操作

2、逐行处理{},逐行读入文本执行相应的处理,是最常见的编辑指令块

3、行后处理END{},处理完最后一行文本之后执行,一般用来输出处理结果

均可以单独使用,也可以一起使用

[root@localhost src]# awk 'BEGIN{a=34.67;print a+32}'
66.67
[root@localhost src]# awk 'BEGIN{x=0}/\<bash$/{x++}END{print x}' /etc/passwd
1								//统计使用bash的用户个数
[root@localhost src]# awk 'BEGIN{print NR}END{print NR}' u.txt
0								//预处理时行数为0
3								//处理完后,行数为已读入文本的行数

9.2 awk处理条件

格式:awk [选项] ’[条件]{编辑指令}‘ 文件

条件的表现形式

正则表达式、数值/字符串比较、逻辑比较、运算符

9.3 正则表达式

/正则表达式/

匹配、!~不匹配

// 列出以ro开头的用户记录
[root@localhost src]# awk -F: ‘/^ro/{print}’ /etc/passwd
root❌0:0:root:/root:/bin/bash
// 列出第七个字段不以bash结尾的用户名
[root@localhost src]# awk -F: ‘ 7 !   / b a s h 7!~/bash 7! /bash/{print$1,$7}’ /etc/passwd
bin /sbin/nologin
daemon /sbin/nologin
adm /sbin/nologin
lp /sbin/nologin
sync /bin/sync
shutdown /sbin/shutdown
halt /sbin/halt
mail /sbin/nologin
operator /sbin/nologin
games /sbin/nologin
ftp /sbin/nologin
nobody /sbin/nologin
systemd-network /sbin/nologin
dbus /sbin/nologin
polkitd /sbin/nologin
sshd /sbin/nologin
postfix /sbin/nologin
chrony /sbin/nologin

9.4 数值比较

[root@localhost src]# awk 'NR==2{print}' u.txt
name 李四							//输出第2行文本
[root@localhost src]# awk '$2!="李四"{print}' u.txt
name 张三							//输出第2列不是李四的行
name 王五
[root@localhost src]# awk 'NF>=2{print}' u.txt
name 张三							//输出含有2列及以上的行
name 李四
name 王五

9.5 多个条件组合

&&与,||或

9.6 变量的运算

+,-,*,/,%

++,–,+=,-+,*=,/=

// 输出奇数行的文本
[root@localhost src]# awk 'NR%2==1{print}' u.txt
name 张三							
name 王五
// 计算总的字段的个数
[root@localhost src]# cat u.txt
name 张三
name 李四
name 王五
[root@localhost src]# awk 'BEGIN{i=0}{i+=NF}END{print i}' u.txt
6
// 200以内能同时被3和13整除的整数个数
[root@localhost src]# seq 200 | awk 'BEGIN{i=0}($0%3==0)&&($0%13==0){i++}END{print i}'
5
// 200以内能同时被3和13整除的整数
[root@localhost src]# seq 200 | awk '($0%3==0)&&($0%13==0){print}'
39
78
117
156
195

9.7 流程控制

9.7.1 if

if(条件){编辑指令}

if(){}else{}

if(){}else if(){}…else{}

例子1,统计UID小鱼或者等于500的用户个数,和UID大于500的用户的个数

awk -F: 'BEGIN{i=0;j=0}{if($3<=500){i++}else{j++}}END{print i,j}' /etc/passwd

9.7.2 while

while(条件){编辑指令}

do{编辑指令}while(条件)

例子,统计/etc/passwd文件内“root”出现的次数,利用-F[?]表示分隔符为:或者/

awk -F[:/] '{i=1}{while(i<NF){if($i~/root/){j++};i++}}END{print j}' /etc/passwd

等效于

str=$(cat /etc/passwd)
echo $str | awk -F "root" '{print NF-1}'

9.7.3 for

for(初始值;条件;步长){编辑指令}

[root@localhost ~]# awk 'BEGIN{for(i=1;i<=5;i++){print i}}'
1
2
3
4
5

9.7.4 控制语句

关键字 含义
break 结束当前的循环体
continue 中止本次循环,转入下一次循环
next 跳过当前行,读入下一行文本开始操作
exit 结束文本读入,转入END{}执行,如果没有END{}则直接退出awk处理操作

[root@localhost src]# awk 'NR<2{next}{print}' u.txt
name 李四
name 王五

十、sed文本流处理

10.1 主要用法

格式1:前置命令 | sed [选项] ‘编辑指令’

格式2:sed [选项] ‘编辑指令’ 文件…

例子1,输出第4到底7行。

[root@localhost ~]# cat -n /etc/inittab
     1	# inittab is no longer used when using systemd.
     2	#
     3	# ADDING CONFIGURATION HERE WILL HAVE NO EFFECT ON YOUR SYSTEM.
     4	#
     5	# Ctrl-Alt-Delete is handled by /usr/lib/systemd/system/ctrl-alt-del.target
     6	#
     7	# systemd uses 'targets' instead of runlevels. By default, there are two main targets:
     8	#
     9	# multi-user.target: analogous to runlevel 3
    10	# graphical.target: analogous to runlevel 5
    11	#
    12	# To view current default target, run:
    13	# systemctl get-default
    14	#
    15	# To set a default target, run:
    16	# systemctl set-default TARGET.target
    17	#
[root@localhost ~]# cat -n /etc/inittab | sed -n '4,7p'
     4	#
     5	# Ctrl-Alt-Delete is handled by /usr/lib/systemd/system/ctrl-alt-del.target
     6	#
     7	# systemd uses 'targets' instead of runlevels. By default, there are two main targets:

10.2 常见的命令选项

-n 屏蔽默认输出(全部文本)

-i 直接修改文件内容

-f 使用sed脚本

-e 可指定多个处理动作

-r 启动扩展的正则表达式,若与其他选项一起使用,应作为首个选项

{} 可组合多个命令,以逗号分隔

10.3 定址符,即[地址1[,地址2]]

用来指定处理的起止行数;

省略地址符时,默认逐行处理全部文本;

地址可表示为文本的“行号”,或者用来匹配的“/正则表达式/”

例子,输出第2-4行

[root@localhost ~]# sed -n '2,4p' /etc/inittab
#
# ADDING CONFIGURATION HERE WILL HAVE NO EFFECT ON YOUR SYSTEM.
#

10.4 基本的处理动作

操作符 用途 指令示例
p 打印行 2,4p 输出第2 3 4 行,2p;4p 输出第2 4行
d 删除行 2,4d删除第2 3 4行
s 字符串替换 s/old/new/将每行的第1个old替换为new; s/old/new/3将每行的第3个old替换为new;s/old/new/g将所有的old替换为new

注:替换操作的分隔“/”可改用其他字符,如#、&等,便于修改文件路径

10.5 操作示例

10.5.1 输出文本

示例 含义
sed -n ‘p’ a.txt 输出所有的行,没等同于cat a.txt
sed -n ‘4p’ a.txt 输出第4行
sed -n ‘4,7p’ a.txt 输出第4-7行
sed -n ‘4,+10p’ a.txt 输出第4行,及其后面的10行
sed -n ‘/^bin/p’ a.txt 输出所有已bin开头的行
sed -n ‘p;n’ a.txt 输出奇数行,n表示读入下一行文本(隔一行)
sed -n ‘n;p’ a.txt 输出偶数行,n表示读入下一行文本(隔一行)
sed -n ‘10, n ; p ′ a . t x t 从 第 10 行 开 始 , 输 出 偶 数 行 s e d − n ′ {n;p}&#x27; a.txt 从第10行开始,输出偶数行 sed -n &#x27; n;pa.txt10sedn=’ a.txt 输出文件的行数

10.5.2 删除文本

示例 含义
sed ‘3,5d’ a.txt 删除3-5行
sed ‘/xml/d’ a.txt 删除所有包含xml的行
sed ‘/xml/!d’ a.txt 删除所有不包含xml的行,!取反
sed ‘/^xml/d’ a.txt 删除所有xml开头的行
sed ‘KaTeX parse error: Expected group after '^' at position 49: … sed '/^̲/d’ a.txt 删除所有的空行
sed ‘/^KaTeX parse error: Expected group after '^' at position 6: /{n;/^̲/d}’ a.txt 删除重复的空行,连续两个空行只保留一行

注意:此例子只作输出,不更改原文件,若要更改,应该添加选项-i,如:删除第4-7行sed -i ‘4,7d’ a.txt

10.5.3 替换文本

示例 含义
sed ‘s/xml/XML/’ a.txt 将每一行中的第一个小写xml替换成大写的XML
sed ‘s/xml/XML/3’ a.txt 将每一行中的第三个小写xml替换成大写的XML
sed ‘s/xml/XML/g’ a.txt 将所有行中的小写xml替换成大写的XML
sed ‘s/xml//g’ a.txt 将所有的xml都删除(替换为空串)
sed 's/doc/ s / g ′ a . t x t 将 所 有 的 d o c 替 换 成 d o c s , s/g&#x27; a.txt 将所有的doc替换成docs, s/ga.txtdocdocs代表查找串
sed ‘4,7s/^/#/’ a.txt 将第4-7行注释掉(行首加#号)
sed ‘s/^#an/an/’ a.txt 解除以#an开头的行的注释(行首去掉#号)

注意:此例子只作输出,不更改原文件,若要更改,应该添加选项-i,如:sed -i ‘s/xml/XML/’ a.txt

10.6 文本块处理动作

操作符 用途 指令示例
i 行前插入文本 2iYY在第2行之前添加文本行“YY”;4,7iYY在第4-7行的每一行前添加文本行
a 行后插入文本 2aYY在第2行之后添加文本;/^XX/aYY以XX开头的行之后添加文本
c 替换当前行 2cYY 将第2行的内容替换为"YY"

注意:此例子只作输出,不更改原文件,若要更改,应该添加选项-i,如:sed -i ‘s/xml/XML/’ a.txt

例子,插入到行前

[root@localhost src]# sed '2iXX' u.txt
name 张三
XX
name 李四
name 王五
[root@localhost src]# cat u.txt
name 张三
name 李四
name 王五

// 添加-i选项,修改文本
[root@localhost src]# sed -i '2iXX' u.txt
[root@localhost src]# cat u.txt
name 张三
XX
name 李四
name 王五

// 插入两行, \n换行符
[root@localhost src]# sed '2iYYYYYYYYYY\nYYYYYYYYYY ' u.txt
name 张三
YYYYYYYYYY
YYYYYYYYYY
XX
name 李四
name 王五

// 命令行文本追加在末尾添加'\'符号
[root@localhost src]# sed '2iZZZZZZZ\
> zzzzzzzz\
> ZZZZZZZZ' u.txt
name 张三
ZZZZZZZ
zzzzzzzz
ZZZZZZZZ
XX
name 李四
name 王五

10.7 导入导出操作

r 动作应结合-i选项才会存入,否则只会输出

w 动作以覆盖的方式另存为新文件

操作符 用途 指令示例
r 读取文件 3r b.txt在第3行下方插入b.txt的文件内容;4,7r b.txt在第4-7行的每一行后面插入文件b.txt的内容
w 保存到文件 3w b.txt将第3行另存为文件c.txt;4,7w c.txt将第4-7行另存为文件c.txt

例子1,在reg.txt第2行的下方插入u.txt的内容

[root@localhost src]# sed '2r u.txt' reg.txt
ababab
ab dd abc
name 张三
name 李四
name 王五
a xx
abcabc ,,

例子2,reg.txt以ab开头的行下面天机u.txt的内容

[root@localhost src]# sed '/^ab/r u.txt' reg.txt
ababab
name 张三
name 李四
name 王五
ab dd abc
name 张三
name 李四
name 王五
a xx
abcabc ,,
name 张三
name 李四
name 王五

例子3,吧reg.txt第1 2行的内容保存到c.txt文件

[root@localhost src]# sed '1,2w c.txt' reg.txt
ababab
ab dd abc
a xx
abcabc ,,
[root@localhost src]# cat c.txt
ababab
ab dd abc

10.8 复制剪切

模式空间:存放当前处理的行,将处理结果输出;若当前行不符合处理条件,则原样输出;处理完当前行再读入下一行来处理。

保持空间:作用类似“剪贴板”;默认存放一个空行(换行符\n)

基本动作:

复制:

-H 模式空间----[追加]---->保持空间

-h 模式空间----[覆盖]---->保持空间

粘贴:

-G 保持空间----[追加]---->模式空间

-g 保持空间----[覆盖]---->模式空间

例子,把第1-3行复制到文件末尾

[root@localhost src]# sed '1,3H;$G' reg.txt
1
2
3
4
5
6

1
2
3
[root@localhost src]# sed -i '1,3H;$G' reg.txt
[root@localhost src]# cat reg.txt
1
2
3
4
5
6

1
2
3

十一、awk&sed应用实战

  • 例子1,利用awk数组去重

11.1 数组定义

数组的下表不仅可以时数字还可以是字符

# awk 'BEGIN{ip[1]="192.168.255.128";ip[2]="192.168.255.129";print ip[2]}'
192.168.255.129

# awk 'BEGIN{ip[mail]="192.168.255.128";ip[web]="192.168.255.129";print ip[web]}'
192.168.255.129

# awk 'BEGIN{ip[1]="192.168.255.128";ip[2]="192.168.255.129";for(x in ip){print ip[x]}}'
192.168.255.128
192.168.255.129

//如果是字符,需要用引号括起来
# awk 'BEGIN{ip["mail"]="192.168.255.128";ip["web"]="192.168.255.129";for(x in ip){print ip[x]}}'
192.168.255.129
192.168.255.128

11.2 去除重复行实例

[root@localhost src]# cut -d: -f7 /etc/passwd > loginshell.txt
[root@localhost src]# cat loginshell.txt
/bin/bash
/sbin/nologin
/sbin/nologin
/sbin/nologin
/sbin/nologin
/bin/sync
/sbin/shutdown
/sbin/halt
/sbin/nologin
/sbin/nologin
/sbin/nologin
/sbin/nologin
/sbin/nologin
/sbin/nologin
/sbin/nologin
/sbin/nologin
/sbin/nologin
/sbin/nologin
/sbin/nologin

//列出shell的种类
[root@localhost src]# awk '!a[$0]++' loginshell.txt
/bin/bash
/sbin/nologin
/bin/sync
/sbin/shutdown
/sbin/halt

11.3 去除重复字段实例

[root@localhost src]# 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
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
operator:x:11:0:operator:/root:/sbin/nologin
games:x:12:100:games:/usr/games:/sbin/nologin
ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin
nobody:x:99:99:Nobody:/:/sbin/nologin
systemd-network:x:192:192:systemd Network Management:/:/sbin/nologin
dbus:x:81:81:System message bus:/:/sbin/nologin
polkitd:x:999:998:User for polkitd:/:/sbin/nologin
sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin
postfix:x:89:89::/var/spool/postfix:/sbin/nologin
chrony:x:998:996::/var/lib/chrony:/sbin/nologin

// 以:号分隔 去第七个列所有的值 然后去重
[root@localhost src]# awk -F: '!a[$7]++{print $7}' /etc/passwd
/bin/bash
/sbin/nologin
/bin/sync
/sbin/shutdown
/sbin/halt
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值