Linux入门(八)Shell脚本

1.Shell基础

1.1 Shell概述

1)Shell是什么

  • SHell是一个命令行解释器,它为用户提供了一个向Linux内核发送请求以便运行程序的界面系统级程序,用户可以用Shell来启动、挂起、停止系统,甚至是编写一些程序。
  • Shell还是一个功能相当强大的编程语言,易编写,易调试,灵活性较强。Shell是解释执行的脚本语言,在Shell中可以直接调用Linux系统命令。

2)Shell的分类

  • Bourne Shell:从1979年起Unix就开始使用Bourne Shell,Bourne Shell的主文件名为sh。(Bourne 读音[bʊən])
  • C Shell:C Shell主要在BSD版的Unix系统中使用,其语法和C语言相类似而得名。
  • Shell的两种主要语法类型有Bourne和C,这两种语法彼此不兼容。Bourne家族主要包括sh、ksh、Bash、psh、zsh;C家族主要包括:csh、tcsh。
  • Bash:Bash与sh兼容,现在使用的Linux就是使用Bash作为用户的基本Shell。

3)Linux支持的Shell

  • /etc/shells

补充:Linux最常用的sh是bash。

1.2 Shell脚本的执行方法

1)echo输出命令

$ echo [选项] [输出内容]

选项:

  • -e 支持反斜线控制的字符转换
$ echo -e "ab\bc"
# \b删除左侧字符

$ echo -e "a\tb\tc\nd\te\tf"
# \t制表符 \n换行符

$ echo -e "\x61\t\x62\t\x63\n\x64\t\x64\t\x66"
# \x按照十六进制ASCII码也同样可以输出(与上一个例子输出同样的结果)

$ echo -e "\e[1;31m abcd \e[0m"
# 输出颜色 \e[1;表示开始颜色输出 \e[0m表示结束颜色输出
# 30m=黑色,31m=红色,32m=绿色,33m=黄色
# 34m=蓝色,35m=洋红,36m=青色,37m=白色

补充:echo 后面双引号里面不能打印特殊符号,单引号里面可以。

2)第一个脚本

$ vim hello.sh
#!/bin/bash
# The first program
# Author: ACGkaka(Email:771679640@qq.com)

echo "Linux learning every day, happy every day."

3)脚本执行

  • 赋予执行权限,直接运行
    • chmod 755 hello.sh
    • ./hello.sh
  • 通过Bash调用执行脚本
    • bash hello.sh

1.3 Bash的基本功能

1.3.1 历史命令与命令补全

1)历史命令

$ history [选项] [历史命令保存文件]

选项:

  • -c 清空历史命令

  • -w 把缓存中的历史命令写入历史命令保存文件~/.bash_history

  • 历史命令默认会保存1000条,可以在环境变量配置文件/etc/profile中进行修改

历史命令的调用

  • 使用 ↑、↓ 箭头调用以前的历史命令
  • 使用 !n 重复执行第n条历史命令
  • 使用 !字符串 重复执行最后一条以该字符串开头的命令

2)命令与文件补全

  • 在Bash中,命令与文件补全是非常方便与常用的功能,我们只要在输入命令或文件时,按Tab键就会自动进行补全。
1.3.2 命令别名与常用快捷键

1)命令别名

$ alias 别名='原命令'
# 设定命令别名

$ alias
# 查询命令别名

命令执行时顺序

  1. 第一顺位执行用绝对路径或相对路径执行的命令;
  2. 第二顺位执行别名;
  3. 第三顺位执行Bash的内部命令;
  4. 第四顺位执行按照$PATH环境变量定义的目录查找顺序找到的第一个命令。
$ vim /root/.bashrc
# 让别名永久生效,写入文件

$ unalias 别名
# 临时删除别名

2)Bash常用快捷键

1.3.3 输入输出重定向

1)标准输入输出

设备设备文件名文件描述符类型
键盘/dev/stdin0标准输入
显示器/dev/stdout1标准输出
显示器/dev/stderr2标准错误输出

2)输出重定向

3)输入重定向

$ wc [选项] 文件名

选项:

  • -c 统计字节数

  • -w 统计单词数

  • -l 统计行数

  • 命令<文件 把文件作为命令的输入

  • 命令<< 标识符

    ……

    标识符 把标识符之间内容作为命令的输入

1.3.4 多命令顺序执行与管道符

1)多命令顺序执行

多命令执行符格式作用
;命令1 ; 命令2多个命令顺序执行,命令之间没有任何逻辑联系
&&命令1 && 命令2逻辑与
当命令1正确执行,则命令2才会执行
当命令1执行不正确,则命令2不会执行
||命令1 || 命令2逻辑非
当命令1执行不正确,则命令2才会执行
当命令1正确执行,则命令2不会执行

示例:

$ ls ; date ; cd /usr ; pwd

# 判断命令是否执行成功
$ ls anaconda-ks.cfg && echo yes
# 判断命令是否执行失败
$ ls /root/test || echo no
# 判断命令执行成功还是失败
$ 命令 && echo yes || echo no

读取、转换并输出命令 dd

$ dd if=输入文件 of=输出文件 bs=字节数 count=个数

选项:

  • if=[输入文件] 指定源文件或源设备
  • of=[输出文件] 指定目标文件或目标设备
  • bs=[字节数] 指定一次输入/输出多少个字节,即把这些字节看作一个数据块
  • count=[个数] 指定输入/输出多少个数据块

示例:

$ date ; dd if=/dev/zero of=/root/testfile bs=1k count=100000 ; date
# 实现文件内容拷贝

2)管道符

$ 命令1 | 命令2
# 命令1的正确输出,作为命令2的操作对象

示例:

$ ll -a /etc/ | more
$ netstat -an | grep "ESTABLISHED"

管道符最常用命令 grep

$ 命令 | grep [选项] 搜索内容

选项:

  • -i 忽略大小写
  • -n 输出行号
  • -v 反向查找
  • –color=auto 搜索处的关键字用颜色显示
1.3.5 通配符与其他特殊符号

1)通配符

通配符作用
?匹配一个任意字符
*匹配0个或任意多个任意字符,也就是可以匹配任何内容
[]匹配中括号中的任意一个字符。例如:[abc]代表匹配a、b、c中的任意一个字符。
[-]匹配中括号中的任意一个字符,-代表一个范围。例如:[a-z]代表匹配任意一个小写字母。
[^]逻辑非,代表匹配不是中括号中的一个字符。例如:[^0-9]代表匹配一个不是数字的字符。

示例:

$ cd /tmp/
$ rm -rf *

$ touch abc
$ touch 012
$ touch 0abc

$ ls ?abc
# 匹配 0abc
$ ls [0-9]*
# 匹配 012
$ ls [^0-9]*
# 匹配 abc

补充:通配符主要用于匹配文件名

2)Bash中的其他特殊符号

符号作用
单引号。在单引号中的所有特殊符号,如$和`(反引号)都没有特殊含义。
"双引号。在双引号中特殊符号都没有特殊含义,但是"$“、”`“、”\" 是例外,拥有"调用变量的值"、“引用命令”、"转义符"的特殊含义。
`反引号。反引号括起来的内容是系统命令,在Bash中会先执行它。和 ( ) 作用一样,不过推荐使用 ()作用一样,不过推荐使用 ()作用一样,不过推荐使用(),因为反引号非常容易看错。
$()和反引号作用一样,用来引用系统命令。
#在Shell脚本中,#开头的行代表注释。
$用于调用变量的值,如需要调用变量name的值时,需要用$name的方式得到变量的值。
\转义符,跟在\之后的特殊符号将失去特殊含义,变为普通字符。如\ 将输出为 将输出为 将输出为符号,而不当作时变量引用。

反引号与$()示例:

$ echo `date`
$ echo $(date)
# 两条命令执行结果相同

单引号与双引号示例:

$ name=sc
$ echo '$name'
# 输出$name
$ echo "$name"
# 输出sc
$ echo '$(date)'
# 输出$(date)
$ echo "$(date)"
# 输出Sat Dec 11 04:07:23 PST 2021

1.4 Bash的变量

1.4.1 用户自定义变量

1)什么是变量

  • 变量是计算机内存的单元,其中存放的值可以改变。当Shell脚本需要保存一些信息时,如一个文件名或是一个数字,就把它存放在一个变量中。每个变量有一个名字,所以很容易引用它。使用变量可以保存有用信息,是系统获知用户相关设置,变量也可以用于保存暂时信息。

2)变量设置规则

  • 变量名称可以由字母、数字和下划线组成,但是不能以数字开头。如果变量名是"2name"则是错误的。
  • 在Bash中,变量的默认类型都是字符串型,如果要进行数值运算,则必须指定变量类型为数值型。
  • 变量用等号连接值,等号左右两侧不能有空格。
  • 变量的值如果有空格,需要使用单引号或双引号包括。
  • 在变量的值中,可以使用\转义符。
  • 如果需要增加变量的值,那么可以进行变量值的叠加。不过变量需要用双引号包含" 变量名 " 或用 变量名"或用 变量名"或用{变量名}包含。
  • 如果是把命令的结果作为变量值赋予变量,则需要使用反引号或$()包含命令。
  • 环境变量名建议大写,便于区分。

3)变量分类

  • 用户自定义变量
  • 环境变量:这种变量中主要保存的是和系统操作环境相关的数据。
  • 位置参数变量:这种变量主要是用来向脚本当中传递参数或数据的,变量名不能自定义,变量作用是固定的。(例如:$0、$1…)
  • 预定义变量:是Bash中已经定义好的变量,变量名不能自定义,变量作用也是固定的。

4)本地变量

  • 变量定义

    $ name="ACGkaka"
    
  • 变量叠加

    $ aa=123
    # aa为123
    $ aa="$aa"456
    # aa为123456
    $ aa=${aa}789
    # aa为123456789
    
  • 变量调用

    $ echo $name
    
  • 变量查看

    $ set | grep name=
    
  • 变量删除

    $ unset name
    
1.4.2 环境变量

1)环境变量是什么

  • 用户自定义变量只在当前的Shell中生效,而环境变量会在当前Shell和这个Shell的所有子Shell当中生效。如果把这个环境变量写入相应的配置文件,那么这个环境变量就会在所有的Shell中生效。

2)设置环境变量

$ export 变量名=变量值
# 声明变量
$ env
# 查询变量
$ unset 变量名
# 删除变量

3)系统常见环境变量

  • PATH:系统查找命令的路径

    $ echo $PATH
    # /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin: 
    $ PATH="$PATH":/root/sh
    # PATH变量叠加
    
  • PS1:定义系统提示符的变量

    \d:显示日期,格式为“星期 月 日”

    \h:显示简写主机名。如默认主机名“localhost”

    \t:显示24小时制时间,格式为“HH:MM:SS”

    \T:显示12小时制时间,格式为“HH:MM:SS”

    \A:显示24小时制时间,格式为“HH:MM”

    @:显示12小时制时间,格式为“HH:MM AM/PM”

    \u:显示当前用户名

    \w:显示当前所在目录的完整名称

    \W:显示当前所在目录的最后一个目录

    \#:执行的第几个命令

    \KaTeX parse error: Expected 'EOF', got '#' at position 22: …果是root用户会显示提示符为#̲,如果是普通用户会显示提示符为

    示例:

    [root@localhost ~]$ PS1='[\u@\t \w]\$'
    # 系统默认提示符为PS1='[\u@\h \W]\$'
    [root@04:50:08 /usr/local/src]$ PS1='[\u@\@ \h \# \W]\$'
    [root@04:53 AM localhost 31 rsc]$
    
1.4.3 位置参数变量

1)位置参数变量

位置参数变量作用
$nn为数字,$0代表命令本身,$1- 9 代表第一到第九个参数,十以上的参数需要用大括号包含,如 9代表第一到第九个参数,十以上的参数需要用大括号包含,如 9代表第一到第九个参数,十以上的参数需要用大括号包含,如{10}
$*这个变量代表命令行中所有的参数,$*把所有的参数看成一个整体
$@这个变量也代表命令行中所有的参数,不过$@把每个参数区分对待
$#这个变量代表命令行中所有参数的个数

示例1:

#!/bin/bash
num1=$1
num2=$2
sum=$(( $num1 + $num2 ))
# 变量sum的和是num1加num2
echo $sum
# 打印变量sum的值

示例2:

#!/bin/bash
echo "A total of $# parameters"
# 使用$#代表所有参数的个数
echo "The parameter is: $*"
# 使用$*代表所有的参数
echo "The parameter is: $@"
# 使用$@也代表所有参数

示例3:$*与$@的区别

#!/bin/bash
for i in $*
# $*中的所有参数看成是一个整体,所以这个for循环只会循环一次
	do
		echo "The parameter is: $i"
    done
x=1
for y in $@
# $@中的每个参数都看成是独立的,所以"$@"是一个数组,有几个参数,就会循环几次
	do
		echo "The parameter is: $y"
		x=$(( $x + 1 ))
	done
1.4.4 预定义变量

1)预定义变量

预定义变量作用
$?最后一次执行的命令的返回状态。
值为0,证明上一个命令正确执行;值为非0,则证明上一个命令执行不正确了。(具体是哪个数,根据脚本对错误的定义而定)
$$当前进程的进程号(PID)
$!后台运行的最后一个进程的进程号(PID)

示例:

$ vim variable.sh
#!/bin/bash
# Author:ACGkaka(E-mail:771679640@qq.com)

echo "The current process is $$"
# 输出当前进程的PID。
# 这个PID就是variable.sh这个脚本执行时,生成的进程的PID

find /root -name hello.sh &
# 使用find命令在root目录下查找hello.sh文件
# 符号&的意思是把命令放入后台执行
echo "The last one Daemon process is $"

补充:逻辑与&&、逻辑或|| 是怎么判断命令有没有正确执行的,就是通过$?s

2)接受键盘输入

$ read [选项] 变量名

选项:

  • -p [提示信息]:在等待read输入时,输出提示信息
  • -t [秒数]:read命令会一直等待用户输入,使用此选项可以指定等待时间
  • -n [字符数]:read命令只接受指定的字符数,就会执行
  • -s:隐藏输入的数据,适用于机密信息的输入

示例:

#!/bin/bash
# Author:ACGkaka(E-mail:771679640@qq.com)
read -t 30 -p "Please input your name: " name
# 提示“请输入姓名”,并等待30秒,把用户的输入保存入变量name中
echo "Name is $name"

read -s -t 30 -p "Please enter your age: " age
# 年龄时隐私,所以我们用“-s”选项隐藏输入
echo -e "\n"
# echo -e 输出转移字符 -n 输出不换行
echo "Age is $age"

read -n 1 -t 30 -p "Please select your gender[M|F]: " gender
# 使用“-n 1” 选项只接收一个输入字符就会执行(都不用输入回车)
echo -e "\n"
echo "Gender is $gender"

注意:在Linux当中,变量类型默认全部是字符串类型。

1.5 Bash的运算符

1.5.1 数值运算与运算符

1)declare 声明变量类型

$ declare [+/-][选项] 变量名

选项:

  • - 给变量设定类型属性
  • + 取消变量的类型属性
  • -i 将变量声明为整数型(integer)
  • -x 将变量声明为环境变量
  • -p 显示指定变量的被声明的类型

2)数值运算

方法1:

$ aa=11
$ bb=22
# 给变量aa和bb赋值
$ declare -i cc=$aa+$bb
# cc=33

方法2:expr或let数值运算工具

$ aa=11
$ bb=22
# 给变量aa和变量bb赋值
$ dd=$(expr $aa + $bb)
# dd=33。注意:+两边必须有空格

方法3:$((运算式)) 或 $[运算式]

#!/bin/bash
$ aa=11
$ bb=22
$ ff=$(( $aa+$bb ))
$ gg=$[ $aa+$bb ]

运算式整理:

  • $(命令)相当于`命令`
  • $((运算式))相当于$[运算式]
  • ${变量名}相当于"$变量名"(包括双引号)
  • [条件判断]相当于“test 条件判断”

3)运算符

示例:

$ aa=$(( (11+3)*3/2 ))
# 小学生数学,优先计算括号内的
$ bb=$(( 14%3 ))
# 14对3取余,结果为2
$ cc=$(( 1 && 0 ))
# 逻辑与运算,两边都为1才为1,否则为0
1.5.2 变量测试与内容替换

请添加图片描述

示例:测试x=${y-新值}

$ unset y
# 删除变量y
$ x=${y-new}
$ echo $x
# 因为变量y不存在,所以x=new

$ y=""
# 给变量y赋值为空
$ x=${y-new}
$ echo $x
# x为空

$ y=old
# 给变量y赋值
$ x=${y-new}
$ echo $x
# x=old

1.6 环境变量配置文件

1.6.1 环境变量配置文件简介

1)source命令

$ source 配置文件
# 或 $ .配置文件

补充:source 和. 可以让配置文件立即生效 而不需要重启。

2)环境变量配置文件简介

  • 环境变量配置文件中主要是定义对系统的操作环境生效的系统默认环境变量,比如PATH、HISTSIZE、PS1、HOSTNAME等默认环境变量。
  • /etc/profile
  • /etc/profile.d/*.sh
  • ~/.bash_profile
  • ~/.bashrc
  • /etc/bashrc

补充:/etc/下面的配置文件是对全部用户生效,~/下面的配置文件是对相应的用户生效。

1.6.2 环境变量配置文件作用

配置文件:

  • /etc/profile
  • /etc/profile.d/*.sh
  • ~/.bash_profile
  • ~/.bashrc
  • /etc/bashrc

配置文件生效顺序:

请添加图片描述

/etc/profile的作用:

  • 初始化部分环境变量:
    • USER变量:
    • LOGNAME变量:
    • MAIL变量:
    • PATH变量:
    • HOSTNAME变量:
    • HISTSIZE变量:
  • 初始化umask(文件的默认权限)
  • 调用/etc/profile.d/*.sh文件

~/.bash_profile的作用:

  • 调用了~/.bashrc文件;
  • 在PATH变量后面加入了“:$HOME/bin”这个目录

~/.bashrc的作用:

  • 定义默认别名
  • 调用/etc/bashrc

/etc/bashrc的作用:

  • 初始化部分环境变量:
    • PS1变量:
    • PATH变量:
  • 初始化umask
  • 调用/etc/profile.d/*.sh文件
1.6.3 其他配置文件和登录信息

1)注销时生效的环境变量配置文件

  • ~/.bash_logout

2)其他配置文件

  • ~/bash_history 操作历史记录文件

3)Shell登录信息

  • 本地终端欢迎信息:/etc/issue

    请添加图片描述

  • 远程终端欢迎信息:/etc/issue.net

    • 转义符在/etc/issue.net文件中不能使用
    • 是否显示此欢迎信息,由ssh的配置文件/etc/ssh/sshd_config决定,加入 “Banner /etc/issue.net”行才能显示(记得重启SSH服务)
  • 登录后欢迎信息:/etc/motd

    不管是本地登录,还是远程登录,都可以显示此欢迎信息。

2.Shell编程

2.1 基础正则表达式

1)正则表达式与通配符

  • 正则表达式用来在文件中配合符合条件的字符串,正则是包含匹配。grep、awk、sed等命令可以支持正则表达式。
  • 通配符用来匹配符合条件的文件名,通配符是完全匹配。ls、find、cp这些命令不支持正则表达式,所以只能使用shell自己的通配符来进行匹配了。

2)基础正则表达式

请添加图片描述

*”:前一个字符匹配0此,或任意多次。

$ grep "a*" test_rule.txt
# 匹配所有内容,包括空白行
$ grep "aa*" test_rule.txt
# 匹配至少包含有一个a的行
$ grep "aaa*" test_rule.txt
# 匹配最少包含2个连续a的字符串
$ grep "aaaaa*" test_rule.txt
# 匹配最少包含4个连续a的行字符串

.”:匹配除了换行符外的任意一个字符。

$ grep "s..d" test_rule.txt
# “s..d”会匹配在s和d这两个字母之间一定有两个字符的字符串
$ grep "s.*d" test_rule.txt
# 匹配在s和d字母之间有任意字符的字符串
$ grep ".*" test_rule.txt
# 匹配所有内容

^”匹配行首,“$”匹配行尾。

$ grep "^M" test_rule.txt
# 匹配以大写“M”开头的行
$ grep "n$" test_rule.txt
# 匹配以小写“n“结尾的行
$ grep -n "^$" test_rule.txt
# 匹配空白行

[]”匹配中括号中指定的任意一个字符,只匹配一个字符。

$ grep "s[ao]id" test_rule.txt
# 匹配said和soid
$ grep "[0-9]" test_rule.txt
# 匹配任意一个数字
$ grep "^[a-z]" test_rule.txt
# 匹配以小写字母开头的行

^”取反,匹配除中括号的字符以外的任意一个字符。

$ grep "^[^a-z]" test_rule.txt
# 匹配不是小写字母开头的行
$ grep "^[^a-zA-Z]" test_rule.txt
# 匹配不是字母开头的行

\” 转义符。

$ grep "\.$" test_rule.txt
# 匹配使用“.”结尾的行

\{n\}”表示其前面的字符恰好出现n次。

$ grep "a\{3\}" test_rule.txt
# 匹配a字母连续出现三次的字符串
$ grep "[0-9]\{3\}" test_rule.txt
# 匹配连续出现三个字母的字符串

\{n,\}“ 表示其前面的字符出现不小于n次

$ grep "^[0-9]\{3,\}[a-z]" test_rule.txt
# 匹配最少用连续三个数字开头的行

\{n,m\}” 匹配其前面的字符至少出现n次,最多出现m次

$ grep "sa\{1,3\}i" test_rule.txt
# 匹配sai、saai、saaai字符串所在的行

2.2 字符截取命令

2.2.1 cut字段提取命令
$ cut [选项] 文件名

选项:

  • -f [列号] 提取第几列
  • -d [分隔符] 按照指定分隔符分割列

示例:

$ vi student.txt
ID	Name	gender	Mark
1	Liming	M		86
2	Sc		M		90
3	Gao		M		83
# 创建测试用例

$ cut -f 2 student.txt
$ cut -f 2,3 student.txt
$ cut -d ":" -f 1,3 /etc/passwd

cut命令的局限:

  • 如果是不是用固定的制表符、空格、固定字符串进行分割,无法正确拆分列,这时候需要使用awk。

    $ df -h | cut -d " " -f 1,3
    
2.2.2 printf命令
$ printf '输出类型输出格式' 输出内容

输出类型:

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

输出格式:

  • \a:输出警告声音
  • \b:输出退格键,也就是Backspace键
  • \f:清空屏幕
  • \n:换行
  • \r:回车,也就是Enter键
  • \t:水平输出退格键,也就是Tab键
  • \v:垂直输出退格键,也就是Tab键
$ printf %s 1 2 3 4 5 6
# 输出123456
$ printf %s %s %s 1 2 3 4 5 6
# 输出%s%s123456
$ printf '%s %s %s' 1 2 3 4 5 6
# 输出1 2 34 5 6
$ printf '%s %s %s\n' 1 2 3 4 5 6
# 输出如下:
# 1 2 3
# 4 5 6

使用printf输出文本内容:

$ vim student.txt
ID	Name	PHP	Linux	MySQL	Average
1	Liming	82	95		86		87.66
2	Sc		74	96		87		85.66
3	Gao		99	83		93		91.66

$ printf '%s' $(cat student.txt)
# 不调整输出格式
$ printf '%s\t%s\t%s\t%s\t%s\t%s\t\n' $(cat student.txt)
# 调整格式输出

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

  • print:print会在每个输出之后自动加入一个换行符(Linux默认没有print命令)
  • printf:printf是标准格式输出命令,并不会自动加入换行符,如果需要换行,需要手工加入换行符
2.2.3 awk命令
$ awk '条件1{动作1} 条件2{动作2}...' 文件名

条件(Pattern):

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

动作(Action):

  • 格式化输出
  • 流程控制语句

示例:

$ vim student.txt
ID	Name 	PHP	Linux	MySQL	Average
1	Liming	82	95		86		87.66
2	Sc		74	96		87		85.66
3	Gao		99	83		93		91.66

$ awk '{printf $2"\t"$6"\n"}' student.txt
Name    Average
Liming  87.66
Sc      85.66
Gao     91.66

$ df -h | awk '{print $1"\t"$3}'

BEGIN

$ awk 'BEGIN{printf "This is a transcript \n"} {printf $2"\t"$6"\n"}' student.txt
This is a transcript
Name    Average
Liming  87.66
Sc      85.66
Gao     91.66

END

$ awk 'END{printf "The End. \n"} {printf $2"\t"$6"\n"}' student.txt
Name    Average
Liming  87.66
Sc      85.66
Gao     91.66
The End.

FS内置变量

$ cat /etc/passwd | grep "/bin/bash" | \
awk 'BEGIN {FS=":"} {printf $1"\t"$3"\n"}'
root    0
zhj     1000
test    1001

补充:FS 作用是指定分隔符。

注意:awk命令默认先读取第一行,在执行命令,加了BEGIN之后就会限制性BEGIN中的命令,再读取内容,所以FS要加载BEGIN中,不然会少分析第一行。

关系运算符

$ cat student.txt | grep -v Name | \
awk '$6 >= 87 {printf $2"\n"}'
Liming
Gao
2.2.4 sed命令
  • sed 是一种几乎包括在所有UNIX平台(包括 Linux)的轻量级流编辑器。sed主要是用来将数据进行选取、替换、删除、新增的命令。
$ sed [选项] '[动作]' 文件名

选项:

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

动作:

  • a\:追加,在当前行后添加一行或多行。添加多行时,除最后一行外,每行末尾需要使用“\”代表数据未完结。
  • c\:行替换,用c后面的字符串替换原数据行,替换多行时,除最后一行外,每行末尾需用“\”代表数据未完结。
  • i\:插入,在当前行之前插入一行或多行。插入多行时,出最后一行外,每行末尾需要用“\”代表数据未完结。
  • d:删除,删除指定的行。
  • p:打印,输出指定的行。
  • s:字串替换,用一个字符串替换另外一个字符串。格式为“行范围s/旧字串/新字串/g”(和vim中的替换格式类似)

学生成绩表

$ vim student.txt
ID	Name 	PHP	Linux	MySQL	Average
1	Liming	82	95		86		87.66
2	Sc		74	96		87		85.66
3	Gao		99	83		93		91.66

行数据操作

$ sed '2p' student.txt
# 查看文件的第二行(打印第二行之后还会打印一遍全文)

$ sed -n '2p' student.txt
# 只查看文件的第二行

$ sed '2,4d' student.txt
# 删除第二行开始的四行数据,但不修改文件本身

$ sed '2a hello' student.txt
# 在第二行后追加一行 hello

$ sed '2i hello\
world' student.txt
# 在第二行前插入两行 hello\n world

$ sed '2c No such person'
# 将第二换内容替换

字符串替换

$ sed '行号s/旧字串/新字串/g' 文件名

$ sed '3s/74/99/g' student.txt
# 在第三行中,把74替换为99

$ sed -i '3s/74/99/g' student.txt
# 在第三行中,把74替换为99,并写入文件

$ sed -e 's/Liming//g; s/Gao//g' student.txt
# 同时把“Liming”和“Gao”替换为空

中括号[]替换

echo '[test]' |sed 's/[][]//g'

补充:sed 命令中单引号和双引号的用法区别:https://blog.csdn.net/genghongsheng/article/details/120432010

2.3 字符处理命令sort、wc

1)排序命令sort

$ sort [选项] 文件名

选项:

  • -f 忽略大小写
  • -n 以数值型进行排序,默认使用字符串型排序
  • -r 反向排序
  • -t 指定分隔符,默认分隔符是制表符
  • -k n[,m] 按照指定的字段范围排序。从第n字段开始,m字段结束(默认到行尾)

示例:

$ sort /etc/passwd
# 排序用户信息文件

$ sort -r /etc/passwd
# 反向排序

$ sort -t ":" -k 3,3 /etc/passwd
# 指定分隔符是“:”,用第三字段开头,第三字段结尾排序,就是只用第三字段排序

$ sort -n -t ":" -k 3,3 /etc/passwd
# 同上,增加了会将第三字段按照数值排序

2)统计命令wc

$ wc [选项] 文件名

选项:

  • -l 只统计行数
  • -w 只统计单词书
  • -m 只统计字符数

2.4 条件判断

查看条件判断结果的两种方式:

  • [ -e test.txt ] && echo “yes” || echo “no”

    成功yes 失败no

  • [ -e test.txt ]

    echo $?

    成功0 失败1

1)按照文件类型进行判断

请添加图片描述

两种判断格式

$ test -e /root/install.log
$ [ -e /root/install.log ]
# 注意:[]内测两边必须要有空格

示例:

$ [ -d /root ] && echo "yes" || echo "no"
# 第一个判断命令如果正确执行,则打印“yes”,否则打印“no”

2)按照文件权限进行判断

示例:

$ [ -w student.txt ] && echo "yes" || echo "no"
# 判断文件是否拥有写权限

3)两个文件之间进行比较

测试选项作用
文件1 -nt 文件2判断文件1的修改时间是否比文件2的新(如果新则为真)
文件1 -ot 文件2判断文件1的修改时间是否比文件2的旧(如果旧则为真)
文件1 -ef 文件2判断文件1是否和文件2的inode一致,可以理解为两个文件是否为同一个文件。这个判断是个用于判断硬链接的好方法。

示例:

$ ln /root/student.txt /tmp/stu.txt
# 创建个硬链接

$ [ /rooot/student.txt -ef /tmp/stu.txt ] && echo "yes" || echo "no"
# 用test测试下,果然很有用

4)两个整数之间比较

示例:

$ [ 23 -ge 22 ] && echo "yes" || echo "no"
# 判断23是否大于等于22,输出yes

$ [ 23 -le 22 ] && echo "yes" || echo "no"
# 判断23是否小于等于22,输出no

5)字符串的判断

示例:

$ name=sc
# 给name变量赋值
$ [ -z $name ] && echo "yes" || echo "no"
# 判断name变量是否为空或不存在,输出no

$ aa=11
$ bb=22
# 给变量aa和变量bb赋值
$ [ $aa==$bb ] && echo "yes" || echo "no"
# 判断两个变量的值是否相等,输出no

6)多重条件判断

示例:

$ aa=11
$ [ -n $aa -a $aa -gt 23 ] && echo "yes" || echo "no"
# 判断aa是否有值,同时判断aa的值是否大于23,输出no

2.5 流程控制

2.5.1 if语句

1)单分支if条件语句

写法一:

if [ 条件判断式 ];then
	程序
fi

写法二:(常用)

if [ 条件判断式 ]
	then
		程序
fi

单分支条件语句需要注意几个点

  • if语句使用fi结尾,和一般语言使用大括号结尾不同
  • [ 条件判断式 ] 就是使用test命令判断,所以中括号和条件判断式之间必须有空格
  • then 后面跟符合条件之后执行的程序,可以放在[]之后,用“; ”分割。也可以换行写入,就不需要“; ”了。

示例:

#!/bin/bash
# 统计根分区使用率
# Author: ACGkaka(E-mail: 771679640@qq.com)

$ rate=$(df -h | grep "/dev/sda3" | awk '{print $5}' | cut -d "%" -f1)
# 把根分区使用率作为变量值赋予变量rate

if [ $rate -ge 80 ]
	then
		echo "Warning! /dev/sda3 is full!!
fi

2)双分支if条件语句

if [ 条件判断式 ]
	then
		条件成立时,执行的程序
	else
		条件不成立时,执行的另一个程序
fi

示例1:

#!/bin/bash
# 备份MySQL数据库
# Author: ACGkaka(E-mail: 771679640@qq.com)

ntpdate asia.pool.ntp.org &>/dev/null
# 同步系统时间(需联网)
date=$(date +%y%m%d)
# 把当前系统时间按照“年月日”格式赋予变量date
size=$(du -sh /var/lib/mysql)
# 统计MySQL数据库的大小,并把大小赋予size变量

if [ -d /tmp/dbbak ]
	then
		echo "Date: $date" > /tmp/dbback/dbinfo.txt
		echo "Data size: $size" >> /tmp/dbbak/dbinfo.txt
		cd /tmp/dbbak
		tar -zcf mysql-lib-$date.tar.gz /var/lib/mysql dbinfo.txt &>/dev/null
		rm -rf /tmp/dbbak/dbinfo.txt
	else
		mkdir /tmp/dbbak
		echo "Date: $date" > /tmp/dbbak/dbinfo.txt
		echo "Date size: $size" >> /tmp/dbbak/dbinfo.txt
		cd /tmp/dbbak
		tar -zcf mysql-lib-$date.tar.gz /var/lib/mysql dbinfo.txt &>/dev/null
		rm -rf /tmp/dbbak/dbinfo.txt

示例2:

#!/bin/bash
# 判断apache是否启动
# Author: ACGkaka(E-mail: 771679640@qq.com)

port=$(nmap -sT 192.168.1.156 | grep tcp | grep http | awk '{print $2}')
# 使用nmap命令扫描服务器,并截取apache服务的状态,赋予变量port

if [ $port == open ]
    then
        echo "$(date) httpd is ok." >> /tmp/autostart-acc.log
    else
    	/etc/rc.d/init.d/httpd start &>/dev/null		echo "$(date) restart httpd..." >> /tmp/autostar-err.log
fi

补充:Linux 判断字符串是否相等用==

3)多分支if条件语句

if [ 条件判断式1 ]
	then
		当条件判断式1成立时,执行的程序
elseif [ 条件判断式2 ]
	then
		当条件判断式2成立时,执行的程序
……
else
	当所有条件都不成立时,执行的程序
fi

示例:

#!/bin/bash
# 判断用户输入的是什么文件
# Author: ACGkaka(E-mail: 771679640@qq.com)

read -p "Please input a filename: " file
# 接收键盘输入,并赋予变量file

if [ -z $file ]
# 判断file变量是否为空
	then
		echo "Error, file name cannot be empty"
		exit 1
		# 返回错误码1
elif [ ! -e $file ]
# 判断file的值是否存在
	then
		echo "Your input is not a file."
		exit 2
		# 返回错误码2
elif [ ! -f $file ]
# 判断file的值是否为普通文件
	then
		echo "$file is a regulare file."
elif [ -d $file ]
	then
		echo "$file is a directory."
else
		echo "$file is an unkown file."
fi
2.5.2 case语句

多分支case语句

  • case语句和if…elif…else语句一样都是多分支条件语句,不过和if不同的是,case语句只能判断一种条件关系,而if语句可以判断多种条件关系。
case $变量名 in
	"值1")
		如果变量的值等于值1,执行的程序
		;;
	"值2")
		如果变量的值等于值2,执行的程序
		;;
	……
	*)
		如果变量的值都不是以上的值,则执行此程序
		;;
esac

示例:

#!/bin/bash
# 判断用户输入
# Author: ACGkaka(E-mail: 771679640@qq.com)

read -p "Please choose yes/no" -t 30 cho
# 读取用户输入,赋予变量cho

case $cho in
	"yes")
		echo "Your choose is yes."
		;;
	"no")
		echo "Your choose is no."
		;;
	*)
		echo "Your choose is invalid."
		;;
esac
2.5.3 for循环

语法一:

for 变量 in 值1 值2 值3...
	do
		程序
	done

示例1:

#!/bin/bash
# 打印时间
# Author: ACGkaka(E-mail: 771679640@qq.com)

for time in morning noon afternoon evening
	do
		echo "This time is $time."
	done

示例2:

#!/bin/bash
# 批量解压缩脚本
# Author: ACGkaka(E-mail: 771679640@qq.com)

cd /lamp
ls *.tar.gz>ls.log
for i in $(cat ls.log)
	do
		tar -zxf $i &>/dev/null
	done
rm -rf /lamp/ls.log

语法二:

for (( 初始值;循环控制条件;变量变化 ))
	do
		程序
	done

示例1:

#!/bin/bash
# 从1加到100
# Author: ACGkaka(E-mail: 771679640@qq.com)

s=0
for (( i=1;i<=100;i=i+1 ))
	do
		s=$(( $s+$i ))
	done
echo "The sum of 1+2+...+100 is $s"

示例2:

#!/bin/bash
# 批量添加指定数量的用户
# Author: ACGkaka(E-mail: 771679640@qq.com)

read -p "Please input user name: " -t 30 name
read -p "Please input the number of users: " -t 30 num
read -p "Please input the password of users: " -t 30 pass

if [ ! -z $name -a ! -z $num -a ! -z $pass ]
	then
		y=$(echo $num | sed 's/[0-9]*//g')
		# 判断数量是否为数字
		if [ -z $y ]
			then
				for (( i=1;i<=$num;i=i+1 ))
					do
						/usr/sbin/useradd $name$i &>/dev/null
						echo $pass | /usr/bin/passwd --stdin $name$i &>/dev/null
					done
2.5.4 while循环与until循环

1)while循环

  • while循环是不定循环,也称作条件循环。只要条件判断式成立,循环就会一直继续,直到条件判断式不成立,循环才会停止。这就和for的固定循环不太一样了。
while [ 条件判断式 ]
	do
		程序
	done

示例:

#!/bin/bash
# 从1加到100
# Author: ACGkaka(E-mail:771679640@qq.com)

i=1
s=0
while [ $i -le 100 ]
# 如果变量i的值小于等于100,则循环执行
	do
		s=$(( $s+$i ))
		i=$(( $i+1 ))
	done
echo "The sum is: $s"

2)until循环

  • until循环,和while循环相反,until循环时只要条件判断式不成立则进行循环,并执行循环程序。一旦循环条件成立,则终止循环。
until [ 条件判断式 ]
	do
		程序
	done

示例:

#!/bin/bash
# 从1加到100
# Author: ACGkaka(E-mail:771679640@qq.com)

i=1
s=0
until [ $i -gt 100 ]
# 循环直到变量i的值大于100,就停止循环
	do
		s=$(( $s+$i ))
		i=$(( $i+1 ))
	done
echo "The sum is: $s"

整理完毕,完结撒花~ 🌻

  • 3
    点赞
  • 34
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不愿放下技术的小赵

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值