一、Shell 是什么

  Shell 是一个命令行解释器,它为用户提供了一个向 Linux 内核发送请求以便运行程序的界面系统级程序,用户可以用 Shell 来启动、挂起、停止甚至是编写一些程序。

1、Shell 能做些什么
  • Linux 运维工程师在进行服务器集群管理时,需要编写 shell 程序来进行服务器管理。
  • 对于 JavaEE 和 Python 程序员来说,可以编写一些 shell 脚本进行程序或者服务器的维护。比如:编写一个定时备份数据库的脚本。
  • 对于大数据程序员来说,需要编写 shell 程序来管理集群。

二、第一个 shell 脚本

1、脚本格式要求
  • 脚本以#!/bin/bash 开头。
  • 脚本后缀为 .sh。
  • 脚本需要有可执行权限。
2、输出 hello world
#!/bin/bash
echo "hello world"
3、常用执行方式

方式一

  输入脚本的绝对路径或相对路径执行。首先属于 xx.sh 脚本 +x(可执行)权限,然后执行脚本。

方式二

  不赋予脚本 +x 权限,直接使用 sh + 脚本执行。

4、特殊字符的应用

美元符号($)

  美元符号“$”表示变量替换,即用其后指定的变量的值来代替变量。

单引号(')

  由单引号括起来的字符都作为普通字符出现。

双引号(")

  由双引号括起来的字符,除 $、倒引号、反斜线仍保留其特殊功能外,其余字符均作为普通字符对待。

倒引号(`)

  由倒引号括起来的字符串被 shell 解释为命令行,在执行时,shell 会先执行该命令行,并以它的标准输出结果取代整个倒引号部分。

反斜线(\)

  反斜线“\”为转义字符,转义字符告诉 shell 不要对其后面的那个字符进行特殊处理,只是当做普通字符。

5、注释

单行注释

  以 # 开头的行就是注释,会被解释器忽略。

#!/bin/bash

# 第一个 shell 程序
echo "hello world"

多行注释

  多行注释使用以下格式:

:<<EOF
注释内容...
注释内容...
注释内容...
EOF

  EOF 也可以使用其他符号:

:<<'
注释内容...
注释内容...
注释内容...
'

:<<!
注释内容...
注释内容...
注释内容...
!

三、Shell 的变量

  • shell 中的变量分为,系统变量和用户自定义变量。
  • 系统变量:$HOME$PWD 等等。
  • 显示当前 shell 中的所有变量:set。
1、变量的定义

基本语法

  • 定义变量:变量 = 值。
  • 撤销变量:unset 变量。
  • 声明静态变量:readonly 变量,注意:静态变量不能 unset。

命名规则

  • 变量名可以由字母、数字和下划线组成,但是不能以数字开头。
  • 变量名中间不能有空格,不能使用标点符号和关键字。
  • 变量名称一般习惯为大写。

将命令返回值赋给变量

  • A=`ls -la` 反引号,运行里面的命令,并把结果返回给变量 A。
  • A=$(ls -la) 等价于反引号。

快速入门

  定义变量 A 并输出。

#!/bin/bash
A=3
echo $A

  获取命令返回结果并输出。

#!/bin/bash
A=`ls -l`
echo $A

  

2、设置环境变量

基本语法

  • export 变量名=变量值(将 shell 变量输出为环境变量)
  • source 配置文件(让修改后的配置信息立即生效)
  • echo $变量名(查询环境变量的值)

自定义环境变量

  • 打开 /etc/profile 文件;
  • 使用 export 命令在文件末尾定义环境变量;
  • 使用 source 刷新配置文件;
  • 使用 echo 查看环境变量的值或使用 env 查看所有环境变量。
打开 /etc/profile 文件
vi /etc/profile

使用 export 命令在文件末尾定义环境变量
export A=zyx.sh

使用 source 刷新配置文件
source /etc/profile

使用 echo 查看环境变量的值
echo $A
3、位置参数变量

  如果希望获取到命令行的参数信息,就可以使用到位置参数变量。比如:./myshell.sh 100 200,这就是一个执行 shell 的命令行,可以在 shell 脚本中获取到参数信息。

基本语法

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

实例

#!/bin/bash

echo $0
echo $1
echo $*
echo $@
echo $#

  输出结果。

[root@localhost shell]# ./zyx.sh a b
./zyx.sh
a
a b
a b
2
4、预定义变量

  预定义变量就是 shell 设计者事先定义好的变量,可以直接在 shell 脚本中使用。

基本语法

  • $$:当前进程的进程号(PID)。
  • $!:后台运行的最后一个进程的进程号(PID)。
  • $?:最后一次执行命令的状态。如果值为 0,证明上一个命令正确执行,如果为 非 0(具体是哪个数,由命令自己决定),则证明上一个命令没执行成功。
5、数组

  数组中可以存放多个值。Bash Shell 只支持一维数组(不支持多维数组),初始化时不需要定义数组大小。Shell 数组用括号来表示,元素用"空格"符号分割开,语法格式如下:

array_name=(value1 value2 ... valuen)

  也可以使用下标来定义数组:

array_name[0]=value0
array_name[1]=value1
array_name[2]=value2

  获取数组长度,利用 @*,可以将数组扩展成列表,然后使用 # 来获取数组元素的个数,格式如下:

${#array_name[@]}
${#array_name[*]}

实例

  定义数组并输出其中的值。

#!/bin/bash

my_array=(A B "C" D)
echo "第一个元素为: ${my_array[0]}"

my_array[0]=1
echo "第一个元素为: ${my_array[0]}"
echo "第二个元素为: ${my_array[1]}"
echo "第三个元素为: ${my_array[2]}"
echo "第四个元素为: ${my_array[3]}"

for i in ${my_array[*]}
do
        echo "$i"
done
echo "数组的长度为:${#my_array[@]}"

四、运算符

  使用运算符在 shell 中进行运算操作。

基本语法

  • $((运算式));
  • $[运算式];
  • expr m + n (注意 expr 运算符间要有空格);
  • expr-\*/%;减、乘、除、取余。

实例

  计算 (2+3)*4 的值。

#!/bin/bash

echo $(((2+3)*4))
echo $[(2+3)*4]
expr \( 2 + 3 \) \* 4

  求出命令行的两个参数的和。

#!/bin/bash

echo $[$1+$2]

  输出结果。

[root@localhost shell]# ./zyx.sh 1 2
3

五、条件判断

1、判断语句

基本语法

  condition 前后要有空格,非空返回 true,可以使用$?验证( 0 为true,>1 为 false)

[ condition ] && 为true时执行 || 为false时执行

实例

  判断指定文件是否存在,不存在则创建

[ ] && echo ok || echo notok
2、判断条件

两个整数的比较

  • =:字符串比较
  • -lt:小于
  • -le:小于等于
  • -eq:等于
  • -gt:大于
  • -ge:大于等于
  • -ne:不等于

按照文件的权限进行判断

  • -r:有读的权限
  • -w:有写的权限
  • -x:有执行的权限

按照文件类型进行判断

  • -f:文件存在并且是一个常规的文件
  • -e:文件存在
  • -d:文件存在并且是目录

六、流程控制

1、if 语句

基本语法

  下面是 if 语句的语法。注意:[ 判断条件 ],中括号和判断条件之前必须有空格。

if [ 判断条件 ]
then
	程序
elif [ 判断条件 ]
then
	程序
else
	程序
fi

实例

  "1"是否等于"ok"

if [ "1" = "ok" ]
then
        echo "equal"
fi

  23是否大于等于22

if [ 23 -ge 22 ]
then
        echo "大于"
fi

  判断 /root/install.log 文件是否存在

if [ -e /root/zyx/zyx.sh ]
then
        echo "存在"
fi

  编写一个 shell 程序,如果输入的参数,大于等于 60,则输出"及格了",如果小于 60,则输出"不及格"。

if [ $1 -ge 60 ]
then
        echo "及格"
else
        echo "不及格"
fi
2、case 语句

基本语法

case $变量名 in
	"值1")
		如果变量的值等于值1,则执行程序1
		;;
	"值2")
		如果变量的值等于值2,则执行程序2
		;;
	...省略其他分支
	*)
		如果变量的值都不是以上的值,则执行此程序
		;;
esac

实例

  当命令参数行是 1 时,输出“周一”;是 2 时,就输出“周二”;其他情况输出“other”。

case $1 in
        "1")
                echo "周一"
                ;;
        "2")
                echo "周二"
                ;;
        *)
                echo "other"
                ;;
esac
3、for 循环

语法1

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

语法2

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

实例

  打印命令行输入的参数。

for i in "$*"
do
        echo "the num is $i"
done

for i in "$@"
do
        echo "the num is $i"
done

  输出 1 加到 100 的和。

sum=0
for ((i=0;i<=100;i++))
do
        sum=$[$sum+$i]
done
echo "sum=$sum"
4、while 循环

语法

while [ 条件判断 ]
do
	程序
done

实例

  从命令行输入一个数 n ,统计 1+...+n 的值是多少。

um=0
i=0
while [ $i -le $1 ]
do
        sum=$[$sum+$i]
        i=$[$i+1]
done
echo "sum=$sum"

七、read 读取控制台输入

  使用 read 可以读取控制台输入的数据,类似于 Java 的 Scanner。

语法

read 选项 变量名
  • -p:指定读取值时的提示符;
  • -t:指定读取值时等待的时间(秒),如果没有在指定的时间内输入,那就不再等待。

实例

  读取控制台输入的值。

read -p "请给i赋值" i
echo "i的初值为:$i"

  读取控制台输入的值,必须在 10 秒内输入。

read -t 10 -p "为i赋初值" i

八、函数

  shell 编程和其他编程语言一样,有系统函数,也可以自定义函数。

1、系统函数

1)basename 的用法

  basename 会删掉 string 中 / 的所有前缀包括最后一个 / 字符,然后将字符串显示出来,常用于获取文件名。suffix 为后缀,如果 suffix 被指定了,basename 会将 string 中的 suffix 去掉。

basename [string] [suffix]

  返回 /home/aaa/test.txt 的 test.txt 部分

2)dirname 的用法

  dirname 返回 string 最后 / 的前面的部分,常用于返回路径。

dirname [string]

  返回 /home/aaa/test.txt 的 /home/aaa

2、自定义函数

基本语法

  调用函数,直接写函数名:funname。函数参数的获取方式和获取位置参数的方式一样。

[function] funname [()]
{
	Action;
	[return value]
}

  如果函数有返回值,则在调用该函数后通过 $? 来获得。$? 仅对其上一条指令负责,一旦函数返回后其返回值没有立即保存入参数,那么其返回值将不再能通过 $? 获得。

注意:所有函数在使用前必须定义。这意味着必须将函数放在脚本开始部分,直至 shell 解释器首次发现它时,才可以使用。

实例

  接收三个参数,返回后两个参数的和。

#!/bin/bash

function getSum(){
        sum=$[$1+$2]
        return $sum
}

echo "第二个参数为:$2"
echo "第三个参数为:$3"

getSum $2 $3

echo "和 getSum $?"

九、自动备份数据库

1、需求分析
  1. 每天凌晨 2 点备份数据库。
  2. 备份时,给出响应的提示信息
  3. 备份成功后将备份文件以时间命名并打包成 .tar.gz 的形式
  4. 在备份时,检查是否有 2 月前备份的数据库文件,如果有则删除。
2、代码实现
#!/bin/bash

# 备份的路径
path=/data/mysql

# 获取当前时间
date=`date +"%F"`

# 主机
host=localhost
# 用户
user=root
# 密码
password=root
# 备份的数据库名
dataName=(A B)

echo "开始备份数据库,时间:`date "+%F %T"`,备份路径:$path"

# 判断目录是否存在
[ ! -d "$path/$date" ] && mkdir -p "$path/$date"

# 开始备份
for i in ${dataName[@]}
do
	tmp="mysqldump -u${user} -p${password} --host=${host} $i > /sql/$i.sql"
	docker exec mysql /bin/bash -c "$tmp"
	echo "$i 备份完成"
done

echo "sql文件备份完成"
# 备份完将sql移动到指定目录中
mv /data/docker/mysql/* $path/$date

# 打包
tar -zcvPf $path/$date.tar.gz $path/$date
echo "sql文件打包完成"

# 删除原文件
rm -rf $path/$date

# 删除30天前的备份文件
find $path -mtime +30 -name "*.tar.gz" -exec rm -rf {} \;

echo "已删除30天前的备份文件"

echo "============================"
3、定义定时器

  Linux 内置的 cron 进程能帮我们实现这些需求,cron 搭配 shell 脚本,非常复杂的指令也没有问题。定时器的日志文件: /var/spool/mail/root。

语法规则:

crontab [-u username]    # 省略用户表表示操作当前用户的crontab
    -e      (编辑工作表)
    -l      (列出工作表里的命令)
    -r      (删除工作作)

  crontab -e 进入当前用户的工作表编辑,是常见的 vim 界面。每行是一条命令。crontab 的命令构成为
时间+动作,其时间有分、时、日、月、周五种,操作符有:

  • *:取值范围内的所有数字;
  • /:每过多少个数字;
  • -:从X到Z;
  • ,:散列数字。

例如

* * * * *		每1分钟执行一次
30 21 * * *		每晚的21:30
3,15 8-11 * * *		在上午8点到11点的第3和第15分钟执行

标题:Shell 编程
作者:Yi-Xing
地址:http://zyxwmj.top/articles/2020/10/21/1603249619063.html
博客中若有不恰当的地方,请您一定要告诉我。前路崎岖,望我们可以互相帮助,并肩前行!