目录
前言
在 Linux 和 Unix 系统中,Shell 脚本编程是自动化系统管理任务、批量处理文件以及快速执行命令的强大工具。本文讲详细讲解shell基础脚本的书写语法。(本文将以 Ubuntu 版本示例)
编程基础
脚本基本结构
// 一个shell脚本文件中主要包含以下内容
各种系统命令的组合
数据存储:变量、数组
表达式:a + b
控制语句:if
// 格式要求:首行shebang机制
#!/bin/bash
#!/usr/bin/python
#!/usr/bin/perl
#!/usr/bin/ruby
#!/usr/bin/lua
Shell 脚本创建过程
1. 用编辑器创建新文件,首行必须是 shell 声明(shebang),编辑完成后保存退出
2. 添加可执行权限
3. 运行脚本
// 新建文件
[root@ubuntu ~]# vim test.sh
#!/bin/bash
echo "hello world"
// 添加执行权限
[root@ubuntu ~]# chmod +x test.sh
// 执行
// 注意,执行时需要添加路径,否则可能无法执行成功,因为系统会识别为执行命令,只会从$PATH路径下寻找
[root@ubuntu ~]# ./test.sh
hello world
脚本注释规范
一般要注明作者,创建时间,修改时间,文件名,版本号,以及该程序的功能及作用。
[root@ubuntu2204 ~]# vim test.sh
#!/bin/bash
#
#****************************************************
#Author: dayunzi
#QQ: 123456
#Date: 2022-08-16
#FileName: test.sh
#URL: http://www.dayunzi.com
#Description: test
#Copyright(C): 2022 All right
#****************************************************
echo "hello world"
当前进程中执行
// test.sh
name="dayunzi"
age=27
// 当前进程中执行脚本会一直影响当前Shell会话
[root@ubuntu:~]$ . test.sh
[root@ubuntu:~]$ echo $name; echo $age
dayunzi
27
子进程中执行
// test.sh
name="dayunzi"
age=27
// 在子进程中执行脚本意味着这是在一个独立的Shell实例中运行,不会影响当前会话
// 输出为空
[root@ubuntu:~]$ ./test.sh
[root@ubuntu:~]$ echo $name; $age
执行前检查语法
// test.sh
name="dayunzi"
age=27"
// 只能检查语法错误,不能检查命令,也不会执行脚本
[root@ubuntu:~]$ bash -n test.sh
test.sh: line 13: unexpected EOF while looking for matching `"'
调试并执行脚本
// test.sh
name=dayunzi
age=27
car lscpu
// 逐行执行命令,并输出结果,可以检查命令错误
[root@ubuntu:~]$ bash -x test.sh
+ name=dayunzi
+ age=27
+ car lscpu
test.sh: line 14: car: command not found
总结
错误类型 | 检查命令 |
---|---|
语法错误 | bash -n |
命令错误 | bash -x |
逻辑错误 | bash -x |
错误案例
// test1.sh
#!/bin/bash
cat > test.conf<<EOF
$(hostname)
$(hostname -I)
EOF
echo "==== over ===="
// 执行报错
// 编写脚本是要注意空格
[root@ubuntu:~]$ bash test1.sh
test1.sh: line 6: warning: here-document at line 2 delimited by end-of-file (wanted `EOF')
// 检查空格
[root@ubuntu:~]$ cat -nA test1.sh
1 #!/bin/bash$
2 cat > test.conf<<EOF$
3 $(hostname)$
4 $(hostname -I)$
5 EOF $
6 echo "==== over ===="$
变量
变量赋值
name='string'
name="$USER"
name=`COMMAND`
name=$(COMMAND)
变量追加
name+=string
name+="string"
变量引用
$name
${name}
保留格式
// 空格分隔
echo $variable
// 保留原格式
echo "$variable"
删除变量
unset <name>
// 删除变量后,引用不存在的变量输出为空
[root@ubuntu:~]$ name=dayunzi
[root@ubuntu:~]$ title=ceo
[root@ubuntu:~]$ echo $name $title
dayunzi ceo
[root@ubuntu:~]$ unset name title
[root@ubuntu:~]$ echo $name $tile
思考
编写一个脚本,显示当前主机系统信息
// 打开并编辑一个文件
// 将执行成功并获取正确主机系统信息的命令输入到文件中
[root@ubuntu:~]$ vim sysyinfo.sh
echo -ne "hostname: "
hostname
echo -ne "ipv4: "
ip add |head -n 3 |tail -1 |tr -s " " |cut -d " " -f3
echo -ne "os: "
echo -ne "$(cat /etc/os-release |head -n 2 |tail -1 |cut -d '"' -f2) "
echo -e "$(cat /etc/os-release |head -n 4 |tail -1 |cut -d '"' -f2)"
echo -n "kernel: "
uname -r
echo -n "cpu: "
lscpu |head -n 9 |tail -1 |tr -s " " |cut -d: -f2
echo -n "Mem: "
free -h | grep "^Mem" |tr -s " "|cut -d " " -f2
echo -n "disk: "
df -h |grep "^/dev/m" |tr -s " " |cut -d " " -f2
// 添加执行权限
[root@ubuntu:~]$ chmod +x sysinfo.sh
// 执行脚本
[root@ubuntu:~]$ ./sysinfo.sh
环境变量
// test6.sh
#!/bin/bash
echo $var1
echo $var2
echo $PATH
echo $ps1
// 定义变量
[root@ubuntu:~]$ var=123;export var2=456
// 执行脚本
// 普通定义的变量在子进程中执行时不会生效,而export将变量导出为环境变量
[root@ubuntu:~]$ bash test6.sh
456
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin
只读变量
// 设置只读变量,不能修改和删除,但退出会话不再生效
// 永久生效可写入配置文件中,可防止恶意修改关键变量的值
[root@ubuntu:~]$ readonly PI=3014159
[root@ubuntu:~]$ echo $PI
3014159
[root@ubuntu:~]$ PI=314
-bash: PI: readonly variable
[root@ubuntu:~]$ unset PI
-bash: unset: PI: cannot unset: readonly variable
位置变量
项目 | Value |
---|---|
$1,$2… | 代表第1个参数、第2个参数… |
$0 | 运行脚本的命令名 |
$# | 参数的总和 |
$@ | 代表所有位置参数,每个参数独立 |
$* | 代表所有位置参数,将所有参数看作一个整体 |
$? | 代表最后一个命令的退出状态码(0代表成功,1-255代表失败) |
$$ | 代表当前脚本的进程 |
$! | 代表最后一个后台命令的进程id |
$_ | 代表最后一个命令的最后一个参数 |
// 打开并编辑文件
[root@ubuntu:~]$ vim arg.sh
# !/bin/bash
echo "1st arg is $1"
echo "2st arg is $2"
echo "3st arg is $3"
echo "10st arg is ${10}"
echo "11st arg is ${11}"
echo "The number of arg is $#"
echo "All args are $*"
echo "All args are $@"
echo "The scriptname is `basename $0`"
// 执行脚本
[root@ubuntu:~]$ bash arg.sh {a..z}
1st arg is a
2st arg is b
3st arg is c
10st arg is j
11st arg is k
The number of arg is 26
All args are a b c d e f g h i j k l m n o p q r s t u v w x y z
All args are a b c d e f g h i j k l m n o p q r s t u v w x y z
The scriptname is arg.sh
思考
写一个使用删除命令时安全备份的脚本
// 打开并编辑文件
[root@ubuntu:~]$ vim rm.sh
# !/bin/bash
// 将告警输出结果标红
WARNING_COLOR="echo -e \E[1;31m"
END="\E[0m"
// 定义变量
DIR=/tmp/`date +%F_%H-%M-%S`
// 创建目录
mkdir $DIR
// 剪切到创建的目录下
mv $* $DIR
// 输出告警自定义文本
${WARNING_COLOR}Move $* to $DIR $END
// 添加执行权限
[root@ubuntu:~]$ chmod +x rm.sh
// 定义别名
[root@ubuntu:~]$ alias rm='/root/rm.sh'
// 创建一系列文件
[root@ubuntu:~]$ touch {1..10}.txt
// 删除批量文件,脚本自动将删除的文件进行备份
[root@ubuntu:~]$ rm *.txt
Move 10.txt 1.txt 2.txt 3.txt 4.txt 5.txt 6.txt 7.txt 8.txt 9.txt to /tmp/2020-04-01_15-15-28