# Shell 脚本

image-20210611164433831

Shell 脚本就是将完成一个任务的所有的命令按照执行的先后顺序,自上而下的写到一个文本文件中,然后给予执行的权限。

  • 如何书写一个 shell 脚本
  • shell 脚本运行
  • shell 中的特殊符号
  • 管道
  • 重定向
  • shell 中数学运算
  • 脚本退出

参考:https://www.zutuanxue.com/

# 1. Shell 规范

1)#!/bin/bash
//脚本第一行, #!魔法字符,指定脚本代码执行的程序。即它告诉系统这个脚本需要什么解释器来执行,也就是使用哪一种 Shell。

2)#代表注释,#!特例 。

# 3)对脚本的基本信息的描述,大家可以根据实际情况尽可能的写详细一些,方便后续使用者。
# Name: 脚本名字
# Desc:描述describe
# Path:存放路径
# Usage:用法
# Update:更新时间
# Author:作者
# Release: 分发版本

4)脚本的具体内容
commands
...

# 2. Shell 变量

# 2.1 变量定义

# 定义一个变量

变量格式:变量名=值

# 变量名命名规则:
    1. 命名只能使用英文字母,数字和下划线,首个字符不能以数字开头。
    2. 中间不能有空格,可以使用下划线(_)。
    3. 不能使用标点符号。
    4. 不能使用 bash 里的关键字(可用 help 命令查看保留关键字)。
    5. 建议变量名为大写,和命令区分。
    
# 举例
	VAR1=1
	age=18
	name="名称"
	score=88.8
注意:字符串要用单引号或双引号引起来
# 取消一个变量

取消当前环境中的变量,如果是变量设置是保存在文件中,下次重启又会恢复。

可以使用 unset 变量名 来取消一个变量。

# 取消变量
unset name

# 再次尝试输出变量,发现没有
echo $name
# 有类型的变量

-i 将变量看成整数。 -r 使变量只读 readonly,该变量的值无法改变,并且不能为 unset。 -x 标记变量通过环境导出 export。 -a 指定为索引数组(普通数组)。 -A 指定为关联数组。

[root@zutuanxue ~]# declare -i num='asa'
[root@zutuanxue ~]# echo $num
0
[root@zutuanxue ~]# num=100
[root@zutuanxue ~]# echo $num
100
[root@zutuanxue ~]# declare -r num
[root@zutuanxue ~]# echo $num
100
[root@zutuanxue~]# num=200
-bash: num: 只读变量

[root@zutuanxue ~]# declare -x
declare -x HISTCONTROL="ignoredups"
declare -x HISTSIZE="1000"
declare -x HOME="/root"
declare -x HOSTNAME="Bai_Shuming"
declare -x LANG="zh_CN.UTF-8"
declare -x LESSOPEN="||/usr/bin/lesspipe.sh %s"

# 2.2 变量分类

系统中的变量根据作用域及生命周期可以分为四类:本地变量、环境变量、全局变量、内置变量。

# 本地变量

用户自定义的变量,定义在脚本或者当前终端中,脚本执行完毕或终端结束变量失效。

# 环境变量

定义在用户家目录下的 .bashrc.bash_profile 文件中,用户私有变量,只能本用户使用。

查看当前用户的环境变量 env

查询当前用户的所有变量(临时变量与环境变量) set

# 可以用 export 来将一个本地变量临时设置为环境变量
[root@VM-12-10-centos shell]# export A=hello
[root@VM-12-10-centos shell]# env | grep A
A=hello

# 设置永久环境变量
需要将变量写入 .bash_profile 或者 ~/.bashrc,然后 source 一下。

# 关于 export
`用户登录时:`
1) 用户登录到 Linux 系统后,系统将启动一个用户 shell。在这个 shell 中,可以使用 shell 命令或声明变量,也可以创建并运行 shell 脚本程序。

`运行脚本时:`
2) 运行 shell 脚本程序时,系统将创建一个子 shell。此时,系统中将有两个 shell,一个是登录时系统启动的 shell,另一个是系统为运行脚本程序创建的 shell。当一个脚本程序运行完毕,它的脚本 shell 将终止,可以返回到执行该脚本之前的 shell。

从这种意义上来说,用户可以有许多 shell,每个 shell 都是由某个 shell(称为父 shell )派生的。

在子 shell 中定义的变量只在该子 shell 内有效。如果在一个 shell 脚本程序中定义了一个变量,当该脚本程序运行时,这个定义的变量只是该脚本程序内的一个局部变量,其他的 shell 不能引用它,要使某个变量的值可以在其他 shell 中被改变,可以使用 export 命令对已定义的变量进行输出。 

export 命令将使系统在创建每一个新的 shell 时定义这个变量的一个拷贝。这个过程称之为变量输出。

父 shell 与子 shell:

shell父子关系.png
# 全局变量

使用 export 命令将本地变量输出为当前 shell 中的环境变量。

想要所有用户及 shell 都可以使用,可以在 /etc/profile /etc/bashrc 下永久定义。

# 内置变量

系统变量(内置 bash 中变量): shell 本身已经固定好了它的名字和作用。

$?:上一条命令执行后返回的状态,当返回状态值为 0 时表示执行正常,非 0 值表示执行异常或出错
 若退出状态值为 0,表示命令运行成功
 若退出状态值为 127,表示 command not found
 若退出状态值为 126,表示找到了该命令但无法执行(权限不够)
 若退出状态值为 1&2,表示没有那个文件或目录
 
$$:当前所在进程的进程号     echo $$   eg:kill -9 `echo $$`  = exit   退出当前会话

$!:后台运行的最后一个进程号  (当前终端)  # gedit &
!$ 调用最后一条命令历史中的参数
!! 调用最后一条命令历史


$#:脚本后面接的参数的个数
$*:脚本后面所有参数,参数当成一个整体输出,每一个变量参数之间以空格隔开
$@: 脚本后面所有参数,参数是独立的,也是全部输出

$0:当前执行的进程/程序名  echo $0     
$1~$9 位置参数变量
${10}~${n} 扩展位置参数变量  第10个位置变量必须用{}大括号括起来
 

了解$*$@的区别:
$* :表示将变量看成一个整体
$@ :表示变量是独立的

# 2.3 变量取值

变量内容读出
[root@zutuanxue ~]# echo $name
baism
[root@zutuanxue ~]# echo $school
ayitula
[root@zutuanxue ~]# echo $age
30
[root@zutuanxue ~]# echo $score
88.8

注意:

变量读取过程中,默认单引号是不解释变量的。比如
[root@zutuanxue ~]# echo '$name'
$name

如果必须使用单引号还要读取变量的值可以使用 eval 命令[重新运算求出参数的内容] 
[root@zutuanxue ~]# eval  echo '$name'
baism

# 3. Shell 格式化输出

# 3.1 echo

echo 命令的功能是在显示器上显示一段文字,一般起到一个提示的作用。

功能说明:显示文字。

# 语法: 
echo [-ne] [字符串]

# 补充说明:
1、echo会将输入的字符串送往标准输出。
2、输出的字符串间以空白字符隔开,并在最后加上换行号。

# OPTIONS:
-n	不要在最后自动换行
-e	若字符串中出现以下字符,则特别加以处理,而不会将它当成一般文字输出:

# 转义字符
\a	发出警告声;
\b	删除前一个字符;
\t	插入tab;
\n	换行且光标移至行首;
\c	最后不加上换行符号;
\f	换行但光标仍旧停留在原来的位置;
\r	光标移至行首,但不换行;
\v	与\f相同;
\		插入\字符;
\0nnn	打印nnn(八进制)所代表的ASCII字符;  备注:数字0  不要理解成字母o
\xNN  打印NN(十六进制)所代表的ASCII字符;

-–help	显示帮助
-–version显示版本信息

示例:

[root@VM-12-10-centos shell]# echo -e "\0123"
S
[root@VM-12-10-centos shell]# echo -e "\x61"
a

# 3.2 输出有颜色的字体

脚本中 echo 显示内容带颜色显示。

echo 显示带颜色,需要使用参数 -e

# 格式如下:
echo -e "\033[字背景颜色;文字颜色m字符串\033[0m"

例如: echo -e “\033[41;36m something here \033[0m”

其中41的位置代表底色, 36m 的位置是代表字的颜色

1、字背景颜色和文字颜色之间是英文的

2、文字颜色后面有个 m

3、字符串前后可以没有空格,如果有的话,输出也是同样有空格

下面是相应的字和背景颜色,可以自己来尝试找出不同颜色搭配

  例
  echo -e “\033[31m 红色字 \033[0m”
  echo -e “\033[34m 黄色字 \033[0m”
  echo -e “\033[41;33m 红底黄字 \033[0m”
  echo -e “\033[41;37m 红底白字 \033[0m”
  
字颜色:30—–37
  echo -e “\033[30m 黑色字 \033[0m”
  echo -e “\033[31m 红色字 \033[0m”
  echo -e “\033[32m 绿色字 \033[0m”
  echo -e “\033[33m 黄色字 \033[0m”
  echo -e “\033[34m 蓝色字 \033[0m”
  echo -e “\033[35m 紫色字 \033[0m”
  echo -e “\033[36m 天蓝字 \033[0m”
  echo -e “\033[37m 白色字 \033[0m”

  
字背景颜色范围:40—–47
  echo -e “\033[40;37m 黑底白字 \033[0m”
  echo -e “\033[41;37m 红底白字 \033[0m”
  echo -e “\033[42;37m 绿底白字 \033[0m”
  echo -e “\033[43;37m 黄底白字 \033[0m”
  echo -e “\033[44;37m 蓝底白字 \033[0m”
  echo -e “\033[45;37m 紫底白字 \033[0m”
  echo -e “\033[46;37m 天蓝底白字 \033[0m”
  echo -e “\033[47;30m 白底黑字 \033[0m”
  
最后面控制选项说明
  \033[0m 关闭所有属性
  \033[1m 设置高亮度
  \033[4m 下划线
  \033[5m 闪烁
  \033[7m 反显
  \033[8m 消隐

  \033[30m — \33[37m 

设置前景色
  \033[40m — \33[47m 设置背景色
  
  
  \033[nA 光标上移n行
  \033[nB 光标下移n行
  \033[nC 光标右移n行
  \033[nD 光标左移n行
  \033[y;xH设置光标位置
  \033[2J 清屏
  \033[K 清除从光标到行尾的内容
  \33[s 保存光标位置
  \033[u 恢复光标位置
  \033[?25l 隐藏光标
  \033[?25h 显示光标

# 4. Shell 交互

# 4.1 read

# 功能
默认接受键盘的输入,回车符代表输入结束。

# 应用场景
人机交互。

# 命令选项
-p 打印信息
-t 限定时间
-s 不回显
-n 输入字符个

案例:写一个系统用户交互登录界面脚本,仿linux文本界面登录。

login.gif

步骤:

  1. 根据linux文本界面登陆窗口输出信息,打印登陆提示信息
  2. 交互输入登陆账号
  3. 交互输入登陆密码

实现:

#!/bin/bash

#Author: Hedon
#Release: 1.0
#Description: imitate the linux login process

IP=`ifconfig ens33 | egrep -w "inet" | awk '{print $2}'`

#1. clear the window
clear

#2. print the prompt info
echo "CentOS Linux 8 (Core)"
echo -e "Kernel `uname -r` on an `uname -m`\n"
echo -e "Web console: https://localhost:9090/ or https://$IP:9090/ \n"

#3. let use input login username
echo -n "$HOSTNAME login: "
read account

#4. let use input login password
read -s -t30 -p "Password: " pw
echo

结果:

login2.gif

# 5. Shell 运算

# 5.1 赋值运算

赋值运算符 =

 	a=10   
 	name='baism'
 
 重点:字符串必须用引号引起来

# 5.2 算法运算

运算命令:

  • 整形运算 – expr – let – $(()) – bc
  • 浮点运算 – bc
# 整型运算

expr 命令:只能做整数运算,格式比较古板,注意空格。

[root@zutuanxue ~]# expr 1 + 1
2
[root@zutuanxue ~]# expr 5 - 2
3
[root@zutuanxue ~]# expr 5 \* 2  #注意*出现应该转义,否则认为是通配符
10
[root@zutuanxue ~]# expr 5 / 2
2
[root@zutuanxue ~]# expr 5 % 2
1

let 命令:只能做整数运算,且运算元素必须是变量,无法直接对整数做运算。

[root@zutuanxue ~]# let a=100+3;echo $a
103
root@zutuanxue ~]# let a=100-3;echo $a
97
[root@zutuanxue ~]# let a=100/3;echo $a
33
[root@zutuanxue ~]# let a=100*3;echo $a
300
[root@zutuanxue ~]# let a=100%3;echo $a
1
[root@zutuanxue ~]# let a=100**3;echo $a
1000000
[root@zutuanxue ~]# a=100
[root@zutuanxue ~]# let a++;echo $a
101
[root@zutuanxue ~]# let a--;echo $a
100
[root@zutuanxue ~]# let a-=3;echo $a
97
[root@zutuanxue ~]# let a+=5;echo $a
102

双小圆括号运算,在 shell 中 (( )) 也可以用来做数学运算。

[root@zutuanxue ~]# echo $(( 100+3))
103
[root@zutuanxue ~]# echo $(( 100-3)) 
97
[root@zutuanxue ~]# echo $(( 100%3))
1
[root@zutuanxue ~]# echo $(( 100*3))
300
[root@zutuanxue ~]# echo $(( 100/3))
33
[root@zutuanxue ~]# echo $(( 100**3))     #开方运算
1000000
# 浮点运算

浮点运算是采用的命令组合的方式来实现的 echo “scale=N;表达式”|bc

[root@zutuanxue ~]# echo "scale=2;3+100"|bc
103
[root@zutuanxue ~]# echo "scale=2;100-3"|bc
97
[root@zutuanxue ~]# echo "scale=2;100/3"|bc
33.33
[root@zutuanxue ~]# echo "scale=2;100*3"|bc
300

# 5.3 比较运算

																						运算符解释:

 精确比较
        -eq         等于 equal

        -gt         大于

        -lt         小于

 模糊比较
        -ge         大于或等于

        -le         小于或等于

        -ne         不等于

通过 test 命令比较两个整数关系:

[root@zutuanxue ~]# test 100 -gt 300;echo $?
1
[root@zutuanxue ~]# test 100 -ge 300;echo $?
1
[root@zutuanxue ~]# test 100 -eq 300;echo $?
1
[root@zutuanxue ~]# test 100 -le 300;echo $?
0
[root@zutuanxue ~]# test 100 -lt 300;echo $?
0
[root@zutuanxue ~]# test 100 -ne 300;echo $?
0

备注:linux命令test只能比较两个整数的关系,不会返回结果,需要通过$?才能看到结果
		$? 返回 0 表示正确,非 0 表示错误。

# 5.4 字符串比较运算

# 运算符解释,注意字符串一定别忘了使用引号引起来
  ==          等于   
  !=          不等于
  -n          检查字符串的长度是否大于 0  
  -z          检查字符串的长度是否为 0

示例:

[root@zutuanxue ~]# test 'root' == 'root';echo $?
0
[root@zutuanxue ~]# test 'root' != 'root1';echo $?
0
[root@zutuanxue ~]# name=
[root@zutuanxue ~]# test -n "$name";echo $?
1
[root@zutuanxue ~]# test -z "$name";echo $?
0

# 5.4 逻辑运算

  • 逻辑与运算 &&
  • 逻辑或运算 ||
  • 逻辑非运算 !

# 5.5 文件判断

Linux 的设计思路:一切皆文件,对文件系统的操作其实可以狭隘的理解为对文件的操作。

如果希望对文件类型和权限或者两个文件做新旧或者是否同一个文件进行判断,可以用 test 命令。

test 命令功能: 检测文件类型和比较运算

test 命令用法

																	test [命令选项] 表达式

test 命令选项

-d  检查文件是否存在且为目录
-e  检查文件是否存在
-f  检查文件是否存在且为文件
-r  检查文件是否存在且可读
-s  检查文件是否存在且不为空
-w  检查文件是否存在且可写
-x  检查文件是否存在且可执行
-O  检查文件是否存在并且被当前用户拥有
-G  检查文件是否存在并且默认组为当前用户组
-nt file1 -nt file2  检查file1是否比file2新
-ot file1 -ot file2  检查file1是否比file2旧     
-ef file1 -ef file2  检查file1是否与file2是同一个文件,判定依据的是i节点

以上只列出部分命令选项,详细的可以通过:man test获得。

# 案例

# 1. 实现一个简单的四则运算
  1. 要求用户传输三个参数,num1 算术运算符 num2
  2. 运算输出结果
#!/bin/bash

#Author:Hedon
#Release:v1.0
#Description:imitate a basic calculator which supports +,-,*,\ operator.

echo "$1 $2 $3" | bc
# 2. 内存使用率统计,要求打印内存使用率

案例思考:

物料1. 内存总量 获得方式是什么 free top /proc/meminfo

物料2. 内存使用量

物料3. 内存使用率公式 使用量/总量*100%

实现步骤:

  1. 获取内存总量
  2. 获取内存使用量
  3. 运算输出结果

代码实现:

#!/bin/bash

#Author:Hedon
#Release:v1.0
#Description:内存使用率计算脚本

#total memoy
total_memory=`free | grep -i "Mem" | tr -s " " | cut -d " " -f2`

#used memory
used_memory=`free | grep -i "Mem" | tr -s " " | cut -d " " -f3`

#calculate
echo "内存使用率: `echo "scale=2;$used_memory*100/$total_memory"|bc`%"
# 3. 模拟一个 linux 文本界面登陆程序,要求账号密码验证成功进入系统,账号密码验证失败退回登陆界面

案例步骤:

  1. 预设正确账号、密码
  2. 输出提示登录信息并实现交互登录
  3. 输出密码输入信息并实现交互
  4. 判断输入是否正确
    1. 正确,进入系统
    2. 不正确 继续运行该脚本

实现代码:

#!/bin/bash

#Author:Hedon
#Release:v1.0
#Description:imiatete linux login process.

default_account='root'
default_pw='123456'


######main
#1、清屏
clear
#2、输出提示信息
echo "CentOS Linux 7 (Core)"
echo -e "Kernel `uname -r` on an `uname -m`\n"

#3、交互输入登陆名
echo -n "$HOSTNAME login: "
read account

#4、交互输入密码
read -s -t30 -p "Password: " pw

#5、判断用户输入是否正确
if [ "$default_account" == "$account" ] && [ "$default_pw" == "$pw" ];then
   clear
   echo -e "\nwelcome to root"
else
   echo  "用户名或密码错误..."
   #输入错误,再次调用本脚本
   sh $0
fi

# 6. Shell 数组

# 6.1 数组介绍

数组可以让用户一次赋予多个值,需要读取数据时只需通过索引调用就可以方便读出了。

  • 普通数组:只能使用整数作为数组索引(元素的索引)
  • 关联数组:可以使用字符串作为数组索引(元素的索引)

# 6.2 数组定义

数组名称=(元素1 元素2 元素3 ...)

# 6.3 数组赋值

  • 一次赋一个值

    变量名=变量值
    array[0]=v1
    array[1]=v2
    array[3]=v3
    
  • 一次性赋多个值

    array=(var1 var2 var3 var4)
    array1=(`cat /etc/passwd`)			//将文件中每一行赋值给array1数组
    array2=(`ls /root`)
    array3=(harry amy jack "Miss zhang")
    array4=(1 2 3 4 "hello world" [10]=linux)
    

# 6.4 数组取值

取值方式: ${数组名称[索引]}

**索引:**默认情况下索引是指数组中的元素[存的值]在数组中的顺序,从 0 始计数,关联数组除外。比如:

array=(var1 var2 var3 var4)

array 数组中存有 4 个元素,分别是:var1 var2 var3 var4

那么我想取出 var2 这个元素,那么就得先看看他在数组中的位置,数组中的元素索引如下:

		元素	var1 var2 var3 var4
		索引	0     1     2    3

所以正确表示 array 数组中元素 var2 的方式是:${array[1]}

# 6.5 关联数组

定义关联数组

关联数组使用首先需要申明该数组为关联数组,申明方式: declare -A 数组名称

# 首先声明关联数组
declare -A asso_array1
declare -A asso_array2
declare -A asso_array3

关联数组赋值

  • 一次赋一个值

    数组名[索引]=变量值
    [root@zutuanxue ~]# asso_array1[linux]=one
    [root@zutuanxue ~]# asso_array1[java]=two
    [root@zutuanxue ~]# asso_array1[php]=three
    
  • 一次附多个值

    [root@zutuanxue ~]# asso_array2=([name1]=harry [name2]=jack [name3]=amy [name4]="Miss zhang")
    

查看关联数组

[root@zutuanxue ~]# declare -A
declare -A asso_array1='([php]="three" [java]="two" [linux]="one" )'
declare -A asso_array2='([name3]="amy" [name2]="jack" [name1]="harry" [name4]="Miss zhang" )'

关联数组取值

  • !:拿 key
  • #:统计个数
[root@zutuanxue ~]# echo ${asso_array1[linux]}
one
[root@zutuanxue ~]# echo ${asso_array1[php]}
three
[root@zutuanxue ~]# echo ${asso_array1[*]}
three two one

# 加 !表示取出 key
[root@zutuanxue ~]# echo ${!asso_array1[*]}
php java linux

# 加 # 表示取出个数
[root@zutuanxue ~]# echo ${#asso_array1[*]}
3

[root@zutuanxue ~]# echo ${#asso_array2[*]}
4

[root@zutuanxue ~]# echo ${!asso_array2[*]}
name3 name2 name1 name4

# 7. Shell 函数

# 7.1 函数定义

# 语法一:

函数名 () {
    代码块
    return N
}


# 语法二:
function 函数名 {
      代码块
      return N
}
      
      
# 函数中 return 说明:
1. return 可以结束一个函数,类似于循环控制语句的 break (结束当前循环,执行循环体后面的代码)。
2. return 默认返回函数中最后一个命令的退出状态,也可以给定参数值,该参数值的范围是 0-256 之间。
3. 如果没有 return 命令,函数将返回最后一个 Shell 的退出值。

# 7.2 函数调用

当前命令调用

[root@zutuanxue shell04]# cat fun1.sh 
#!/bin/bash
hello(){
echo "hello zutuanxue $1"
hostname
}
menu(){
cat <<-EOF
1. mysql
2. web
3. app
4. exit
EOF
}

[root@zutuanxue shell04]# source fun1.sh 
[root@zutuanxue shell04]# . fun1.sh 

[root@zutuanxue shell04]# hello 888
hello zutuanxue 888
MissHou.zutuanxue.cc
[root@zutuanxue shell04]# menu
1. mysql
2. web
3. app
4. exit

定义到用户的环境变量中调用

  • 就是直接把函数写到 /etc/profile /etc/bashrc ~/.bash_profile ~/.bashrc 等文件当中。

脚本中调用

#!/bin/bash
#打印菜单
source ./fun1.sh
menu(){
cat <<-END
	h	显示命令帮助
	f	显示磁盘分区
	d	显示磁盘挂载
	m	查看内存使用
	u	查看系统负载
	q	退出程序
	END
}
menu		//调用函数

# 8. Shell 控制流程

# 8.1 if

基本用法:

# 开始 if
if [ condition1 ]
then          
            command1    #满足 condition1 执行 command1代 码块
            
elif [ condition2 ]   
then          
            commands2    # 满足 condition2 执行 command2 代码块
  .......
else     
            commandsX      #  如果条件都不满足执行 commandX 代码块
fi    
# fi 结束

高级用法:

# 高级用法 1:条件符号使用双圆括号,可以在条件中植入数学表达式 if (())
if (( (5+5-5)*5/5 > 10 ))
    then
        echo "yes"
else
        echo "no"
fi

# 高级用法 2:使用双方括号,可以在条件中使用通配符
for var in  ab ac rx bx rvv vt
   do
       if [[ "$var" == r* ]]
	  then
		echo "$var"
       fi
done

案例需求:

判断内存的使用率:

  • 60 以下 ok
  • 60 以上 黄色警告
  • 70 以上 橙色严重警告
  • 80 以上 红色警告

案例思考:

  • 内存使用率获得方式
    1. 手动给出
    2. 通过内存查看命令运算得出

代码实现:

#!/bin/bash

#统计内存使用率
total_memory=`free | grep -i "Mem" | tr -s " " | cut -d " " -f2`
used_memory=`free | grep -i "Mem" | tr -s " " | cut -d " " -f3`
used_percent=`echo "$used_memory*100/$total_memory"|bc`


#判断使用情况
if [ $used_percent -gt 80 ]
then
	echo -e "\033[31m 警告 \033[0m"
elif [ $used_percent -gt 70 ]
then
	echo -e "\033[35m 警告 \033[0m"
elif [ $used_percent -gt 60 ]
then
	echo -e "\033[33m 警告 \033[0m"
else
	echo -e "\033[32m OK \033[0m"
fi

# 8.2 for

几种赋值方式:

  1. 直接赋值
  2. 赋值来自一个范围
  3. 赋值来自命令产生的结果
  4. 不带列表,由用户指定
#!/bin/bash

echo "直接赋值"
for var in 1 2 3 4 5
do
	echo "$var \n"
done

echo "列表赋值"
for var in {1..10}
do
	echo "$var \n"
done

echo "列表赋值带步长"
for var in {1..10..2}
do 
	echo "$var \n"
done

echo "列表赋值带步长,反向"
for var in {10..1..-2}
do
	echo "$var \n"
done

echo "命令赋值"
for var in `seq 10 -2 1`
do
	echo "$var \n"
done

echo "不带列表,由用户指定"
for var
do
	echo "$var \n"
done
echo "用户输入了$#个参数"

C 风格的 for:

for(( expr1;expr2;expr3 ))
	do
		command
		commanddone
for (( i=1;i<=5;i++))  
	do
		echo $i
	done


expr1:定义变量并赋初值   变量初始值
expr2:决定是否进行循环(条件)  变量的条件
expr3:决定循环变量如何改变,决定循环什么时候退出 自增或自减运算

# 8.3 while

while [ 表达式 ]
	do
		command...
	done
	
while  [ 1 -eq 1 ]    或者 (( 1 > 2 ))
  do
     command
     command
     ...
 done

无限循环:

# 方法 1
while :
	do
		command...
	done
	
	
# 方法 2
while true
	do
		command
	done

# 8.4 until

特点:条件为假就进入循环;条件为真就退出循环。

until expression  # [ 1 -eq 1 ] 或者 (( 1 >= 1 ))
	do
		command
		command
		...
	done

# 8.5 case

case $var in             # 定义变量, var代表是变量名
pattern1)               # 模式1:用 | 分割多个模式,相当于or
    command1             # 需要执行的语句
    ;;                   # 两个分号代表命令结束
pattern2)
    command2
    ;;
pattern3)
    command3
    ;;
		  *)               #default,不满足以上模式,默认执行 *)下面的语句
    command4
    ;;
esac							esac表示case语句结束

# 8.6 循环控制语句

break:跳出循环

continue:跳过本次循环,进入下一轮循环

sleep 秒数:睡眠

exit:退出程序

shift:外部传参到循环时,参数管理命令。使位置参数向左移动,默认移动 1 位,可以使用shift 2。

# 9. Shell 操作文件

  • sed 命令

# 9.1 sed 命令介绍

sed 是 Linux 中提供的一个外部命令,它是一个行(流)编辑器,非交互式的对文件内容进行增删改查的操作,使用者只能在命令行输入编辑命令、指定文件名,然后在屏幕上查看输出。它和文本编辑器有本质的区别。

区别是:

文本编辑器: 编辑对象是文件

行编辑器:编辑对象是文件中的行

sed 数据处理原理:

image-20210610165824987

# 9.2 sed 语法

[命令选项]

-e script 将脚本中指定的命令添加到处理输入时执行的命令中  多条件,一行中要有多个操作
-f script 将文件中指定的命令添加到处理输入时执行的命令中
-n        抑制自动输出
-i        编辑文件内容
-i.bak    修改时同时创建 .bak 备份文件。
-r        使用扩展的正则表达式
!         取反 (跟在模式条件后与shell有所区别)

[内部命令]

a   在匹配后面添加
i   在匹配前面添加
d   删除
s   查找替换  字符串
c   更改
y   转换   N D P 
p   打印

[Flag]

数字             表示新文本替换的模式
g:              表示用新文本替换现有文本的全部实例
p:              表示打印原始的内容
w filename:     将替换的结果写入文件

# 9.3 内部命令

示例文档如下:

[root@zutuanxue ~]# cat data1
1 the quick brown fox jumps over the lazy dog.
2 the quick brown fox jumps over the lazy dog.
3 the quick brown fox jumps over the lazy dog.
4 the quick brown fox jumps over the lazy dog.
5 the quick brown fox jumps over the lazy dog.

文件内容「增加」操作,将数据追加到某个位置之后,使用命令 「a」:

# 在data1的每行后追加一行新数据内容: append data "haha"
[root@zutuanxue ~]# sed 'a\append data "haha"' data1
1 the quick brown fox jumps over the lazy dog.
append data "haha"
2 the quick brown fox jumps over the lazy dog.
append data "haha"
3 the quick brown fox jumps over the lazy dog.
append data "haha"
4 the quick brown fox jumps over the lazy dog.
append data "haha"
5 the quick brown fox jumps over the lazy dog.
append data "haha"


# 在第二行后新开一行追加数据: append data "haha"
[root@zutuanxue ~]# sed '2a\append data "haha"' data1
1 the quick brown fox jumps over the lazy dog.
2 the quick brown fox jumps over the lazy dog.
append data "haha"
3 the quick brown fox jumps over the lazy dog.
4 the quick brown fox jumps over the lazy dog.
5 the quick brown fox jumps over the lazy dog.


# 在第二到四行每行后新开一行追加数据: append data "haha"
[root@zutuanxue ~]# sed '2,4a\append data "haha"' data1
1 the quick brown fox jumps over the lazy dog.
2 the quick brown fox jumps over the lazy dog.
append data "haha"
3 the quick brown fox jumps over the lazy dog.
append data "haha"
4 the quick brown fox jumps over the lazy dog.
append data "haha"
5 the quick brown fox jumps over the lazy dog.


# 匹配字符串追加: 找到包含"3 the"的行,在其后新开一行追加内容: append data "haha"
[root@zutuanxue ~]# sed '/3 the/a\append data "haha"' data1
1 the quick brown fox jumps over the lazy dog.
2 the quick brown fox jumps over the lazy dog.
3 the quick brown fox jumps over the lazy dog.
append data "haha"
4 the quick brown fox jumps over the lazy dog.
5 the quick brown fox jumps over the lazy dog.

# 注意://开启匹配模式  /要匹配的字符串/

文件内容「增加」操作,将数据插入到某个位置之前,使用命令 「i」:

# 在data1的每行前插入一行新数据内容: insert data "haha"
[root@zutuanxue ~]# sed 'i\insert data "haha"' data1
insert data "haha"
1 the quick brown fox jumps over the lazy dog.
insert data "haha"
2 the quick brown fox jumps over the lazy dog.
insert data "haha"
3 the quick brown fox jumps over the lazy dog.
insert data "haha"
4 the quick brown fox jumps over the lazy dog.
insert data "haha"
5 the quick brown fox jumps over the lazy dog.


# 在第二行前新开一行插入数据: insert data "haha"
[root@zutuanxue ~]# sed '2i\insert data "haha"' data1
1 the quick brown fox jumps over the lazy dog.
insert data "haha"
2 the quick brown fox jumps over the lazy dog.
3 the quick brown fox jumps over the lazy dog.
4 the quick brown fox jumps over the lazy dog.
5 the quick brown fox jumps over the lazy dog.


# 在第二到四行每行前新开一行插入数据: insert data "haha"
[root@zutuanxue ~]# sed '2,4i\insert data "haha"' data1
1 the quick brown fox jumps over the lazy dog.
insert data "haha"
2 the quick brown fox jumps over the lazy dog.
insert data "haha"
3 the quick brown fox jumps over the lazy dog.
insert data "haha"
4 the quick brown fox jumps over the lazy dog.
5 the quick brown fox jumps over the lazy dog.


# 匹配字符串插入: 找到包含"3 the"的行,在其前新开一行插入内容: insert data "haha"
[root@zutuanxue ~]# sed '/3 the/i\insert data "haha"' data1
1 the quick brown fox jumps over the lazy dog.
2 the quick brown fox jumps over the lazy dog.
insert data "haha"
3 the quick brown fox jumps over the lazy dog.
4 the quick brown fox jumps over the lazy dog.
5 the quick brown fox jumps over the lazy dog.

文件内容修改操作「替换」,将一行中匹配的内容替换为新的数据,使用命令「s」。

# 从标准输出流中做替换,将test替换为text
[root@zutuanxue ~]# echo "this is a test" |sed 's/test/text/'
this is a text

# 将data1中每行的dog替换为cat
[root@zutuanxue ~]# sed 's/dog/cat/' data1
1 the quick brown fox jumps over the lazy cat.
2 the quick brown fox jumps over the lazy cat.
3 the quick brown fox jumps over the lazy cat.
4 the quick brown fox jumps over the lazy cat.
5 the quick brown fox jumps over the lazy cat.

# 将data1中第二行的dog替换为cat
[root@zutuanxue ~]# sed '2s/dog/cat/' data1
1 the quick brown fox jumps over the lazy dog.
2 the quick brown fox jumps over the lazy cat.
3 the quick brown fox jumps over the lazy dog.
4 the quick brown fox jumps over the lazy dog.
5 the quick brown fox jumps over the lazy dog.

# 将data1中第二到第四行的dog替换为cat
[root@zutuanxue ~]# sed '2,4s/dog/cat/' data1
1 the quick brown fox jumps over the lazy dog.
2 the quick brown fox jumps over the lazy cat.
3 the quick brown fox jumps over the lazy cat.
4 the quick brown fox jumps over the lazy cat.
5 the quick brown fox jumps over the lazy dog.

# 匹配字符串替换:将包含字符串"3 the"的行中的dog替换为cat
[root@zutuanxue ~]# sed '/3 the/s/dog/cat/' data1
1 the quick brown fox jumps over the lazy dog.
2 the quick brown fox jumps over the lazy dog.
3 the quick brown fox jumps over the lazy cat.
4 the quick brown fox jumps over the lazy dog.
5 the quick brown fox jumps over the lazy dog.

文件内容修改操作「更改」,将一行中匹配的内容替换为新的数据,使用命令 「c」:

# 将data1文件中的所有行的内容更改为: change data "data"
[root@zutuanxue ~]# sed 'c\change data "haha"' data1
change data "haha"
change data "haha"
change data "haha"
change data "haha"
change data "haha"

# 将data1文件第二行的内容更改为: change data "haha"
[root@zutuanxue ~]# sed '2c\change data "haha"' data1
1 the quick brown fox jumps over the lazy dog.
change data "haha"
3 the quick brown fox jumps over the lazy dog.
4 the quick brown fox jumps over the lazy dog.
5 the quick brown fox jumps over the lazy dog.

# 将data1文件中的第二、三、四行的内容更改为:change data "haha"
[root@zutuanxue ~]# sed '2,4c\change data "haha"' data1
1 the quick brown fox jumps over the lazy dog.
change data "haha"
5 the quick brown fox jumps over the lazy dog.

# 将data1文件中包含"3 the"的行内容更改为: change data "haha"
[root@zutuanxue ~]# sed '/3 the/c\change data "data"' data1
1 the quick brown fox jumps over the lazy dog.
2 the quick brown fox jumps over the lazy dog.
change data "data"
4 the quick brown fox jumps over the lazy dog.
5 the quick brown fox jumps over the lazy dog.

文件内容修改操作「字符转换」,将一行中匹配的内容替换为新的数据,使用命令「y」:

# 将data1中的a b c字符转换为对应的 A  B  C字符
[root@zutuanxue ~]# sed 'y/abc/ABC/' data1
1 the quiCk Brown fox jumps over the lAzy dog.
2 the quiCk Brown fox jumps over the lAzy dog.
3 the quiCk Brown fox jumps over the lAzy dog.
4 the quiCk Brown fox jumps over the lAzy dog.
5 the quiCk Brown fox jumps over the lAzy dog.

文件内容「删除」,将文件中的指定数据删除,使用命令「d」:

# 删除文件data1中的所有数据
[root@zutuanxue ~]# sed 'd' data1

# 删除文件data1中的第三行数据
[root@zutuanxue ~]# sed '3d' data1
1 the quick brown fox jumps over the lazy dog.
2 the quick brown fox jumps over the lazy dog.
4 the quick brown fox jumps over the lazy dog.
5 the quick brown fox jumps over the lazy dog.

# 删除文件data1第三到第四行的数据
[root@zutuanxue ~]# sed '3,4d' data1
1 the quick brown fox jumps over the lazy dog.
2 the quick brown fox jumps over the lazy dog.
5 the quick brown fox jumps over the lazy dog.

# 删除文件data1中包含字符串"3 the"的行
[root@zutuanxue ~]# sed '/3 the/d' data1
1 the quick brown fox jumps over the lazy dog.
2 the quick brown fox jumps over the lazy dog.
4 the quick brown fox jumps over the lazy dog.
5 the quick brown fox jumps over the lazy dog.

文件内容查看,将文件内容输出到屏幕,使用命令「p」:

# 打印data1文件内容
[root@zutuanxue ~]# sed 'p' data1
1 the quick brown fox jumps over the lazy dog.
1 the quick brown fox jumps over the lazy dog.
2 the quick brown fox jumps over the lazy dog.
2 the quick brown fox jumps over the lazy dog.
3 the quick brown fox jumps over the lazy dog.
3 the quick brown fox jumps over the lazy dog.
4 the quick brown fox jumps over the lazy dog.
4 the quick brown fox jumps over the lazy dog.
5 the quick brown fox jumps over the lazy dog.
5 the quick brown fox jumps over the lazy dog.

# 打印data1文件第三行的内容
[root@zutuanxue ~]# sed '3p' data1
1 the quick brown fox jumps over the lazy dog.
2 the quick brown fox jumps over the lazy dog.
3 the quick brown fox jumps over the lazy dog.
3 the quick brown fox jumps over the lazy dog.
4 the quick brown fox jumps over the lazy dog.
5 the quick brown fox jumps over the lazy dog.

# 打印data1文件第二、三、四行内容
[root@zutuanxue ~]# sed '2,4p' data1
1 the quick brown fox jumps over the lazy dog.
2 the quick brown fox jumps over the lazy dog.
2 the quick brown fox jumps over the lazy dog.
3 the quick brown fox jumps over the lazy dog.
3 the quick brown fox jumps over the lazy dog.
4 the quick brown fox jumps over the lazy dog.
4 the quick brown fox jumps over the lazy dog.
5 the quick brown fox jumps over the lazy dog.

# 打印data1文件包含字符串"3 the"的行
[root@zutuanxue ~]# sed '/3 the/p' data1
1 the quick brown fox jumps over the lazy dog.
2 the quick brown fox jumps over the lazy dog.
3 the quick brown fox jumps over the lazy dog.
3 the quick brown fox jumps over the lazy dog.
4 the quick brown fox jumps over the lazy dog.
5 the quick brown fox jumps over the lazy dog. 


# 可以看得出,打印内容是重复的行,原因是打印了指定文件内容一次,又将读入缓存的所有数据打印了一次,所以会看到这样的效果,
# 如果不想看到这样的结果,可以加命令选项「-n」抑制内存输出即可。

# 9.4 内部选项

在 sed 命令中,命令选项是对 sed 中的命令的增强。

在命令行中使用多个命令 「-e」:

# 将brown替换为green dog替换为cat
[root@zutuanxue ~]# sed -e 's/brown/green/;s/dog/cat/' data1
1 the quick green fox jumps over the lazy cat.
2 the quick green fox jumps over the lazy cat.
3 the quick green fox jumps over the lazy cat.
4 the quick green fox jumps over the lazy cat.
5 the quick green fox jumps over the lazy cat.

从文件读取编辑器命令 「-f」 适用于日常重复执行的场景:

# 1)将命令写入文件
[root@zutuanxue ~]# vim abc
s/brown/green/
s/dog/cat/
s/fox/elephant/

# 2)使用 -f 命令选项调用命令文件
[root@zutuanxue ~]# sed -f abc data1 
1 the quick green elephant jumps over the lazy cat.
2 the quick green elephant jumps over the lazy cat.
3 the quick green elephant jumps over the lazy cat.
4 the quick green elephant jumps over the lazy cat.
5 the quick green elephant jumps over the lazy cat.

抑制内存输出 「-n」:

# 打印data1文件的第二行到最后一行内容.  $ 是最后的意思
[root@zutuanxue ~]# sed -n '2,$p' data1 
2 the quick brown fox jumps over the lazy dog.
3 the quick brown fox jumps over the lazy dog.
4 the quick brown fox jumps over the lazy dog.
5 the quick brown fox jumps over the lazy dog.

使用正则表达式 「-r」:

# 打印data1中以字符串"3 the"开头的行内容
[root@zutuanxue ~]# sed -n  -r '/^(3 the)/p' data1
3 the quick brown fox jumps over the lazy dog.

永久修改「-i」:

从上述的演示中,大家可以看出,数据处理只是在缓存中完成的,并没有实际修改文件内容,如果需要修改文件内容可以直接使用**「-i」** 命令选项。

在这里我需要说明的是 -i 是一个不可逆的操作,一旦修改,如果想复原就很困难,几乎不可能,所以建议大家在操作的时候可以备份一下源文件。

-i 命令选项提供了备份功能,比如参数使用 「-i.bak」,那么在修改源文件的同时会先备份一个以.bak结尾的源文件,然后再进行修改操作。

# 1)查看文件列表,没有发现data1.bak
[root@zutuanxue ~]# ls
abc  apache  data1  Dobby  file  node-v10.14.1  Python-3.7.1  soft1  vimset

# 2)执行替换命令并修改文件
[root@zutuanxue ~]# sed -i.bak 's/brown/green/' data1

# 3)发现文件夹中多了一个data1.bak文件
[root@zutuanxue ~]# ls
abc     data1      Dobby  node-v10.14.1  soft1
apache  data1.bak  file   Python-3.7.1   vimset

# 4)打印比较一下,发现data1已经被修改,data1.bak是源文件的备份。
[root@zutuanxue ~]# cat data1
1 the quick green fox jumps over the lazy dog.
2 the quick green fox jumps over the lazy dog.
3 the quick green fox jumps over the lazy dog.
4 the quick green fox jumps over the lazy dog.
5 the quick green fox jumps over the lazy dog.
[root@zutuanxue ~]# cat data1.bak 
1 the quick brown fox jumps over the lazy dog.
2 the quick brown fox jumps over the lazy dog.
3 the quick brown fox jumps over the lazy dog.
4 the quick brown fox jumps over the lazy dog.
5 the quick brown fox jumps over the lazy dog.

# 9.5 标志

在sed命令中,标志是对sed中的内部命令做补充说明。

# 演示文档
[root@zutuanxue ~]# cat data2
1 the quick brown fox jumps over the lazy dog . dog
2 the quick brown fox jumps over the lazy dog . dog
3 the quick brown fox jumps over the lazy dog . dog
4 the quick brown fox jumps over the lazy dog . dog
5 the quick brown fox jumps over the lazy dog . dog

数字标志

此标志是一个非零正数,默认情况下,执行替换的时候,如果一行中有多个符合的字符串,如果没有标志位定义,那么只会替换第一个字符串,其他的就被忽略掉了,为了能精确替换,可以使用数字位做定义。

# 替换一行中的第二处dog为cat
[root@zutuanxue ~]# sed 's/dog/cat/2' data2
1 the quick brown fox jumps over the lazy dog . cat
2 the quick brown fox jumps over the lazy dog . cat
3 the quick brown fox jumps over the lazy dog . cat
4 the quick brown fox jumps over the lazy dog . cat
5 the quick brown fox jumps over the lazy dog . cat
# g 标志

将一行中的所有符合的字符串全部执行替换。

# 将data1文件中的所有dog替换为cat
[root@zutuanxue ~]# sed 's/dog/cat/g' data2
1 the quick brown fox jumps over the lazy cat . cat
2 the quick brown fox jumps over the lazy cat . cat
3 the quick brown fox jumps over the lazy cat . cat
4 the quick brown fox jumps over the lazy cat . cat
5 the quick brown fox jumps over the lazy cat . cat
# p 标志

打印文本内容,类似于 -p 命令选项。

# [root@zutuanxue ~]# sed  '3s/dog/cat/p' data2
1 the quick brown fox jumps over the lazy dog . dog
2 the quick brown fox jumps over the lazy dog . dog
3 the quick brown fox jumps over the lazy cat . dog
3 the quick brown fox jumps over the lazy cat . dog
4 the quick brown fox jumps over the lazy dog . dog
5 the quick brown fox jumps over the lazy dog . dog
# w filename 标志

将修改的内容存入 filename 文件中。

[root@zutuanxue ~]# sed  '3s/dog/cat/w text' data2
1 the quick brown fox jumps over the lazy dog . dog
2 the quick brown fox jumps over the lazy dog . dog
3 the quick brown fox jumps over the lazy cat . dog
4 the quick brown fox jumps over the lazy dog . dog
5 the quick brown fox jumps over the lazy dog . dog

# 可以看出,将修改的第三行内容存在了text文件中
[root@zutuanxue ~]# cat text 
3 the quick brown fox jumps over the lazy cat . dog

# 10. Shell 数据处理

基本命令:

1)数据检索:grep tr cut

2)数据处理:uniq sort tee paste xargs

升级版 awk:

1)字符串截取

2) 数据运算

# 10.1 awk 介绍

在日常计算机管理中,总会有很多数据输出到屏幕或者文件,这些输出包含了标准输出、标准错误输出。默认情况下,这些信息全部输出到默认输出设备—屏幕。然而,大量的数据输出中,只有一小部分是我们需要重点关注的,我们需要把我们需要的或者关注的这些信息过滤或者提取以备后续需要时调用。早先的学习中,我们学过使用 grep 来过滤这些数据,使用 cuttr 命令提出某些字段,但是他们都不具备提取并处理数据的能力,都必须先过滤,再提取转存到变量,然后在通过变量提取去处理,比如:

内存使用率的统计步骤:

1) 通过 free -m 提取出内存总量,赋值给变量 memory_totle

2)通过 free -m 提取出n内存使用量,赋值给变量 memory_use

3)通过数学运算计算内存使用率

awk 是一种可以处理数据、产生格式化报表的语言,功能十分强大。

awk 认为文件中的每一行是一条记录,记录与记录的分隔符为换行符,每一列是一个字段,字段与字段的分隔符默认是一个或多个空格或 tab 制表符.

# 10.2 awk 语法

awk [options] ‘[BEGIN]{program}[END]’ [FILENAME]

# 常用命令选项

-F fs 指定描绘一行中数据字段的文件分隔符 默认为空格 -f file 指定读取程序的文件名 -v var=value 定义awk程序中使用的变量和默认值

注意:awk 程序由左大括号和右大括号定义。 程序命令必须放置在两个大括号之间。由于 awk 命令行假定程序是单文本字符串,所以必须将程序包括在单引号内。

1)程序必须放在大括号内 2)程序必须要用单引号引起来

# awk 程序运行优先级

1)BEGIN:在开始处理数据流之前执行,可选项 2)program:如何处理数据流,必选项 3)END:处理完数据流后执行,可选项

# 10.3 awk 基本应用

学习用例:

[root@zutuanxue ~]# cat test 
1 the quick brown fox jumps over the lazy cat . dog
2 the quick brown fox jumps over the lazy cat . dog
3 the quick brown fox         jumps over the lazy cat . dog
4 the quick brown fox jumps over the lazy cat . dog
5 the quick brown fox jumps over the lazy cat . dog
# awk 对字段(列)的提取

字段提取:提取一个文本中的一列数据并打印输出。

字段相关内置变量:

$0 表示整行文本
$1 表示文本行中的第一个数据字段
$2 表示文本行中的第二个数据字段
$N 表示文本行中的第N个数据字段
$NF 表示文本行中的最后一个数据字段

示例:

# 读入test每行数据并把每行数据打印出来
[root@zutuanxue ~]# awk '{print $0}' test 
1 the quick brown fox jumps over the lazy cat . dog
2 the quick brown fox jumps over the lazy cat . dog
3 the quick brown fox         jumps over the lazy cat . dog
4 the quick brown fox jumps over the lazy cat . dog
5 the quick brown fox jumps over the lazy cat . dog

# 打印test第六个字段
[root@zutuanxue ~]# awk '{print $6}' test
jumps
jumps
jumps
jumps
jumps


# 打印test最后一个字段
[root@zutuanxue ~]# awk '{print $NF}' test
dog
dog
dog
dog
dog
# awk 对记录(行)的提示

记录提取:提取一个文本中的一行并打印输出。

记录的提取方法有两种:

  • 通过行号
  • 通过正则匹配。

记录相关内置变量。

NR:指定行号 number row

# 提取test第三行数据

# 指定行号为3
[root@zutuanxue ~]# awk 'NR==3{print $0}' test 
3 the quick brown fox         jumps over the lazy cat . dog

# 指定行的第一个字段精确匹配字符串为3
[root@zutuanxue ~]# awk '$1=="3"{print $0}' test 
3 the quick brown fox         jumps over the lazy cat . dog

awk 对字符串的提取

记录和字段的汇合点就是字符串。

# 打印test第三行的第六个字段
[root@zutuanxue ~]# awk 'NR==3{print $6}' test
jumps

# 10.4 awk 命令选项

「-F」: 指定字段与字段的分隔符。

当输出的数据流字段格式不是 aw k默认的字段格式时,我们可以使用 -F 命令选项来重新定义数据流字段分隔符。比如:

处理的文件是 /etc/passwd,希望打印第一列、第三列、最后一列:

[root@zutuanxue ~]# awk -F ':' '{print $1,$3,$NF}' /etc/passwd
root 0 /bin/bash
bin 1 /sbin/nologin
daemon 2 /sbin/nologin
adm 3 /sbin/nologin
lp 4 /sbin/nologin
sync 5 /bin/sync
shutdown 6 /sbin/shutdown
halt 7 /sbin/halt
mail 8 /sbin/nologin
operator 11 /sbin/nologin
games 12 /sbin/nologin
ftp 14 /sbin/nologin
nobody 99 /sbin/nologin
systemd-network 192 /sbin/nologin
dbus 81 /sbin/nologin
polkitd 999 /sbin/nologin
postfix 89 /sbin/nologin
chrony 998 /sbin/nologin
sshd 74 /sbin/nologin
ntp 38 /sbin/nologin
tcpdump 72 /sbin/nologin
nscd 28 /sbin/nologin
mysql 997 /sbin/nologin
www 996 /sbin/nologin
apache 48 /sbin/nologin
tss 59 /sbin/nologin
zabbix 995 /sbin/nologin
saslauth 994 /sbin/nologin
grafana 993 /sbin/nologin

可以看的出,awk输出字段默认的分隔符也是空格

「-f file」:如果 awk 命令是日常重复工作,而又没有太多变化,可以将程序写入文件,每次使用 -f 调用程序文件就好,方便,高效。

[root@zutuanxue ~]# cat abc 
{print $1,$3,$NF}

[root@zutuanxue ~]# awk -f abc test
1 quick dog
2 quick dog
3 quick dog
4 quick dog
5 quick dog

「-v」 定义变量。

[root@zutuanxue ~]# awk -v name='baism' 'BEGIN{print name}'
baism

定义了一个变量 name=baism,然后调用变量读出数据。

# 10.5 awk 程序运行优先级

关于 awk 程序的执行优先级,BEGIN 是优先级最高的代码块,是在执行 PROGRAM 之前执行的,不需要提供数据源,因为不涉及到任何数据的处理,也不依赖于 PROGRAM 代码块;PROGRAM是对数据流干什么,是必选代码块,也是默认代码块。所以在执行时必须提供数据源;END 是处理完数据流后的操作,如果需要执行 END 代码块,就必须需要 PROGRAM 的支持,单个无法执行。

BEGIN:处理数据源之前干什么,不需要数据源就可以执行。

PROGRAM: 对数据源干什么,【默认必须有】需要数据源。

END:处理完数据源后干什么,需要 PROGRAM 需要数据源。

# 优先级展示
[root@zutuanxue ~]# awk 'BEGIN{print "hello zutuanxue"}{print $0}END{print "bye zutuanxue"}' test
hello zutuanxue
1 the quick brown fox jumps over the lazy cat . dog
2 the quick brown fox jumps over the lazy cat . dog
3 the quick brown fox         jumps over the lazy cat . dog
4 the quick brown fox jumps over the lazy cat . dog
5 the quick brown fox jumps over the lazy cat . dog
bye zutuanxue

# 不需要数据源,可以直接执行
[root@zutuanxue ~]# awk 'BEGIN{print "hello world"}'
hello world

# 没有提供数据流,所以无法执行成功
[root@zutuanxue ~]# awk '{print "hello world"}'
[root@zutuanxue ~]# awk 'END{print "hello world"}'

# 10.6 awk 高级应用

awk 是一门「语言」,那么就会符合语言的特性,除了可以定义变量外,还可以定义数组,还可以进行运算,流程控制。

# awk 定义变量和数组
# 定义变量
[root@zutuanxue ~]# awk -v name='baism' 'BEGIN{print name}'
baism
[root@zutuanxue ~]# awk 'BEGIN{name="baism";print name}'
baism

# 定义数组
[root@zutuanxue ~]# awk 'BEGIN{array[0]=100;array[1]=200;print array[0],array[1]}'
100 200

awk 运算

  1. 赋值运算 =
  2. 比较运算 > >= == < <= !=
  3. 数学运算 + - * / % ** ++ –
  4. 逻辑运算 && || !
  5. 匹配运算 ~ !~ 精确匹配 == !=
# 赋值运算:主要是对变量或者数组赋值。
[root@zutuanxue ~]# awk -v name='baism' 'BEGIN{print name}'
baism

# 比较运算:如果比较的是字符串则按ascii编码顺序表比较。如果结果返回为真则用1表示,如果返回为假则用0表示。
[root@zutuanxue ~]# awk 'BEGIN{print "a" >= "b" }'
0
[root@zutuanxue ~]# awk 'BEGIN{print "a" <= "b" }'
1
[root@zutuanxue ~]# awk '$1>4{print $0}' test
5 the quick brown fox jumps over the lazy cat . dog

# 数字运算
[root@zutuanxue ~]# awk 'BEGIN{print 100+3 }'
103

# 逻辑运算
[root@zutuanxue ~]# awk 'BEGIN{print 100>=2 && 100>=3 }'
1

# 匹配运算
[root@zutuanxue ~]# awk -F ':' '$1 ~ "^ro" {print $0}' /etc/passwd
root:x:0:0:root:/root:/bin/bash
# awk 环境变量
变量 描述
FIELDWIDTHS 以空格分隔的数字列表,用空格定义每个数据字段的精确宽度
FS 输入字段分隔符号 数据源的字段分隔符 -F
OFS 输出字段分隔符号
RS 输入记录分隔符
ORS 输出记录分隔符号
# FIELDWIDTHS: 重定义列宽并打印,注意不可以使用 $0 打印所有,因为 $0 是打印本行全内容,不会打印你定义的字段
[root@zutuanxue ~]# awk 'BEGIN{FIELDWIDTHS="5 2 8"}NR==1{print $1,$2,$3}' /etc/passwd
root: x: 0:0:root

# FS: 指定数据源中字段分隔符,类似命令选项 -F
[root@zutuanxue ~]# awk 'BEGIN{FS=":"}NR==1{print $1,$3,$NF}' /etc/passwd
root 0 /bin/bash

# OFS: 指定输出到屏幕后字段的分隔符
[root@zutuanxue ~]# awk 'BEGIN{FS=":";OFS="-"}NR==1{print $1,$3,$NF}' /etc/passwd
root-0-/bin/bash

# RS: 指定记录的分隔符
[root@zutuanxue ~]# awk 'BEGIN{RS=""}{print $1,$13,$25,$37,$49}' test
1 2 3 4 5
将记录的分隔符修改为空行后,所有的行会变成一行,所以所有字段就在一行了。

# ORS: 输出到屏幕后记录的分隔符,默认为回车
[root@zutuanxue ~]# awk 'BEGIN{RS="";ORS="*"}{print $1,$13,$25,$37,$49}' test
1 2 3 4 5*[root@zutuanxue ~]# 
可以看出,提示符和输出在一行了,因为默认回车换成了*
# awk 流程控制

if

# 学习用例
[root@zutuanxue ~]# cat num
1
2
3
4
5
6
7
8
9

# 单if语句
# 打印$1大于5的行
[root@zutuanxue ~]# awk '{if($1>5)print $0}' num
6
7
8
9

# if...else语句
# 假如$1大于5则除以2输出,否则乘以2输出
[root@zutuanxue ~]# awk '{if($1>5)print $1/2;else print $1*2}' num
2
4
6
8
10
3
3.5
4
4.5

for

# 学习用例
[root@zutuanxue ~]# cat num2
60 50 100
150 30 10
70 100 40

# 将文件中的每行的数值累加,和大于或等于150就停止累加
[root@zutuanxue ~]# awk '{sum=0;i=1;while(sum<150){sum+=$i;i++}print sum}' num2
210
150
170

do..while

# 学习用例
[root@zutuanxue ~]# cat num2
60 50 100
150 30 10
70 100 40

# 将文件中的每行的数值累加,和大于或等于150就停止累加
[root@zutuanxue ~]# awk '{sum=0;i=1;do{sum+=$i;i++}while(sum<150);print sum}' num2
210
150
170

break/continue

# 学习用例
[root@zutuanxue ~]# cat num2
60 50 100
150 30 10
70 100 40

# 累加每行数值,和大于150停止累加
[root@zutuanxue ~]# awk '{
> sum=0
> i=1
> while (i<4){
>    sum+=$i
>    if (sum>150){
>       break
>    }
>    i++
> }
> print sum
> }' num2

210
180
170
上次更新: 6/24/2021, 6:09:13 PM