一、Shell 脚本简介
Shell 作为一种极为重要的命令行解释器,主要负责接收用户输入的各种命令,并将这些命令准确地传递至操作系统,以便操作系统能够对其进行执行。而 Shell 脚本则是由一系列 Shell 命令所组成的文本文件,它可以由 Shell 解释器逐行进行执行。通过 Shell 脚本编程,用户能够将多个不同的命令加以巧妙组合,从而实现复杂任务的自动化处理。
在众多常见的 Shell 当中,有 Bash、Zsh、Ksh 等等,其中 Bash(Bourne Again Shell)是最为常用的一种。本文将以 Bash
为例,全面展开详细的介绍。
二、Shell 脚本基础用法
2.1、 脚本结构
一个基本的 Shell 脚本通常是由以下几个部分所构成的:
- Shebang 行:它位于脚本的首行位置,主要用于指定脚本的解释器。例如,#!/bin/bash 就明确表示使用 Bash 解释器来执行该脚本。
- 注释:以 # 开头的行为注释行,其主要作用是对脚本的功能以及用法进行详细的解释说明。
- 命令和语句:其中包含了一系列的 Shell 命令以及控制结构,例如条件判断、循环等等。
2.2、 变量
在 Shell 脚本当中,可以使用变量来存储各种数据。变量的命名规则与多数编程语言比较类似,可以包含字母、数字和下划线,但是绝对不能以数字开头。
- 定义变量:采用 变量名=值 的方式来定义变量。例如,name="John" 定义了一个名为 name 的变量,其值为 "John"。
- 引用变量:使用 $变量名 的方式来引用变量。例如,echo $name 将输出变量 name 的值。
- 变量类型:在 Shell 当中,变量并没有严格的类型定义,可以存储字符串、数字等不同类型的数据。
2.3、 控制结构
Shell 脚本支持多种不同的控制结构,例如条件判断、循环等等。
- 条件判断:使用 if...then...else...fi 结构来进行条件判断。例如:
if [ $condition ]; then
# 若条件成立则执行的命令
else
# 若条件不成立则执行的命令
fi
- 循环:Shell 支持
for
、while
和until
三种不同的循环结构。例如:
# for 循环
for item in list; do
# 循环体
done
# while 循环
while condition; do
# 循环体
done
# until 循环
until condition; do
# 循环体
done
2.4、 函数
在 Shell 脚本当中,可以定义函数以封装可重复使用的代码块。函数的定义方式如下:
function_name() {
# 函数体
}
如下实例4中,配置修改函数:
# 配置修改函数
modify_config() {
local pattern=$1
local replacement=$2
local file=$3
sed -i "s|${pattern}|${replacement}|" "$file"
}
# 防火墙配置函数
open_port() {
local port=$1
local firewall_type=$2
echo $firewall_type 开放端口 $port
case $firewall_type in
....... // 略,见下文
esac
}
在调用函数的时候,直接使用函数名即可。例如,function_name 将调用名为 function_name 的函数。
更多详细介绍可以参考:Shell脚本编程
三、实用脚本实例
3.1、 文件备份脚本
以下是一个非常简单的文件备份脚本,它可以将指定目录下的所有文件备份至另一个目录当中:
#!/bin/bash
# 源目录
source_dir="/path/to/source/directory" #这里是作为文件备份的源头目录,其中可能存放着各种需要进行备份的重要文件。
# 目标目录
target_dir="/path/to/target/directory" #这个目录则是用于接收从源目录备份过来的文件,作为备份文件的存储位置。
# 创建目标目录(若不存在)
if [! -d "$target_dir" ]; then
mkdir -p "$target_dir",#如果目标目录不存在,那么就通过这个命令来创建目标目录,确保备份过程能够顺利进行。
fi
# 备份文件
for file in "$source_dir"/ *; do
if [ -f "$file" ]; then
cp "$file" "$target_dir" #通过循环遍历源目录下的所有文件,如果是普通文件,就将其复制到目标目录中,实现文件的备份操作。
fi
done
echo "备份完成!"#当所有文件备份操作完成后,输出这条提示信息,让用户知晓备份过程已经顺利结束。
3.2、 系统监控脚本
这个脚本能够对系统的 CPU 使用率、内存使用率以及磁盘空间使用率进行实时监控,当超过一定阈值时会发送邮件通知:
#!/bin/bash
# 邮件接收者
recipient="your_email@example.com" #这里指定了接收系统监控警告邮件的邮箱地址,当系统资源使用率超过阈值时,警告邮件将会发送到这个邮箱。
# CPU 使用率阈值
cpu_threshold=80#设定了 CPU 使用率的阈值为 80%,当系统的 CPU 使用率超过这个值时,将会触发邮件通知。
# 内存使用率阈值
memory_threshold=80#同样,这里设定了内存使用率的阈值为 80%,一旦内存使用率超过这个值,也会发送邮件通知。
# 磁盘空间使用率阈值
disk_threshold=90#磁盘空间使用率的阈值设定为 90%,当磁盘空间使用率超过这个值时,同样会有邮件通知发出。
# 获取 CPU 使用率
cpu_usage=$(top -bn1 | grep "Cpu(s)" | sed "s/.*, *\([0-9.]*\)%* id.*/\1" | awk '{print 100 - $1}')#通过一系列的命令组合,获取系统当前的 CPU 使用率。
# 获取内存使用率
memory_usage=$(free | grep "Mem" | awk '{print $3/$2 * 100}')#使用特定的命令获取系统的内存使用率。
# 获取磁盘空间使用率
disk_usage=$(df -h / | awk '{print $5}' | sed 's/%//'),#个命令用于获取磁盘空间的使用率。
# 检查 CPU 使用率
if [ $(echo "$cpu_usage > $cpu_threshold" | bc -l) -eq 1 ]; then
echo "CPU 使用率超过 $cpu_threshold%:$cpu_usage%" | mail -s "系统监控警告" "$recipient"#如果 CPU 使用率超过设定的阈值,就会发送一封邮件通知,告知用户 CPU 使用率过高的情况。
fi
# 检查内存使用率
if [ $(echo "$memory_usage > $memory_threshold" | bc -l) -eq 1 ]; then
echo "内存使用率超过 $memory_threshold%:$memory_usage%" | mail -s "系统监控警告" "$recipient"#当内存使用率超过阈值时,也会发送邮件通知用户。
fi
# 检查磁盘空间使用率
if [ $(echo "$disk_usage > $disk_threshold" | bc -l) -eq 1 ]; then
echo "磁盘空间使用率超过 $disk_threshold%:$disk_usage%" | mail -s "系统监控警告" "$recipient"#如果磁盘空间使用率过高,同样会发送邮件提醒用户。
fi
3.3、 自动化部署脚本
以下是一个简单的自动化部署脚本,用于从版本控制系统中检出代码并部署至服务器上:
#!/bin/bash
# 项目名称
project_name="your_project_name"#这里指定了要进行部署的项目名称。
# 代码仓库地址
repository_url="https://github.com/your_username/$project_name.git"#给出了项目代码所在的仓库地址,以便从这个地址检出代码。
# 部署目录
deploy_dir="/path/to/deploy/directory"#设定了代码部署的目标目录。
# 检出代码
if [ -d "$deploy_dir" ]; then
cd "$deploy_dir"
git pull#如果部署目录已经存在,就进入这个目录并执行 git pull 命令,从代码仓库中拉取最新的代码。
else
git clone "$repository_url" "$deploy_dir"#如果部署目录不存在,就使用 git clone 命令从代码仓库地址克隆代码到指定的部署目录中。
fi
# 安装依赖
cd "$deploy_dir"
npm install#进入部署目录后,执行 npm install 命令安装项目所需的依赖。
# 构建项目
npm run build#使用 npm run build 命令构建项目。
# 启动服务
npm start#最后执行 npm start 命令启动服务。
echo "部署完成!"#当所有部署步骤完成后,输出这条提示信息,让用户知道部署过程已经顺利结束。
3.4、 综合实例:PIP2.0自动化部署脚本
#!/bin/bash
# 定义输出红色字体的函数
echo_red() {
echo -e "\e[31m$1\e[0m"
}
# 获取当前目录
SHELL_FOLDER=$(cd "$(dirname "$0")"; pwd)/PIPPlatform
echo "当前目录为: $SHELL_FOLDER"
nedreboot=0
# 检查 SHELL_FOLDER 是否存在
if [ ! -d "$SHELL_FOLDER" ]; then
echo_red "错误: $SHELL_FOLDER 目录不存在"
exit 1
fi
# 检查是否以root权限运行
if [ "$(id -u)" -ne 0 ]; then
echo_red "请以root权限运行此脚本"
exit 1
fi
# 判断 /etc/selinux/config 是否存在,linux安全机制会引起服务无法启动
if [ -f "/etc/selinux/config" ]; then
# 检查并修改 SELINUX 配置
if grep -q "SELINUX=enforcing" /etc/selinux/config; then
echo "修改 SELINUX 配置为 disabled"
sed -i 's/SELINUX=enforcing/SELINUX=disabled/' /etc/selinux/config
echo "修改完成,请重启服务器以应用所有更改。"
nedreboot=1
else
echo "SELINUX 配置已经是 disabled 或未找到 enforcing 配置"
fi
else
echo_red "警告: /etc/selinux/config 文件不存在"
fi
# nginx_install 安装必备环境
if [ -d "$SHELL_FOLDER/nginx_install" ]; then
if ! grep -q "$SHELL_FOLDER/nginx_install/luajit/lib" /etc/ld.so.conf; then
echo "$SHELL_FOLDER/nginx_install/luajit/lib" >> /etc/ld.so.conf
nedreboot=1
echo "处理完成,请重启服务器以应用所有更改。"
else
echo "路径已存在于 /etc/ld.so.conf 中,无需再次添加。"
fi
/sbin/ldconfig
# 检查 libluajit-5.1.so 库是否存在
if ! ldconfig -p | grep -q libluajit-5.1.so; then
echo_red "错误: libluajit-5.1.so 库不存在,尝试手动处理"
echo "$SHELL_FOLDER/nginx_install/luajit/lib" >> /etc/ld.so.conf
nedreboot=1
echo "处理完成,请重启服务器以应用所有更改。"
fi
# 检查 libpcre.so.1 库是否存在
if ! ldconfig -p | grep -q libpcre.so.1; then
echo_red "错误: libpcre.so.1 库不存在,尝试手动处理"
# 尝试手动处理
libpcre_path=$(find / -name "libpcre.so.1" 2>/dev/null)
if [ -n "$libpcre_path" ]; then
echo "找到 libpcre.so.1 库: $libpcre_path"
echo "$libpcre_path" >> /etc/ld.so.conf
nedreboot=1
echo "处理完成,请重启服务器以应用所有更改。"
else
echo_red "无法找到 libpcre.so.1 库,请手动安装 pcre 库"
exit 1
fi
fi
fi
if [ $nedreboot -eq 1 ]; then
read -p "是否立即重启服务器?[y/N]: " reboot_choice
if [[ "$reboot_choice" =~ ^[Yy]$ ]]; then
sudo reboot
else
echo_red "请尽快手动重启服务器以应用更改。"
fi
exit 1
fi
# database_install 必备环境
if [ -d "$SHELL_FOLDER/database_install" ]; then
# 检查当前字符集
if ! locale | grep -q "LANG=zh_CN.UTF-8"; then
echo_red "自行修改操作系统字符集为zh_CN.UTF-8,否则pgsql无法正常运行"
exit 1
# 配置系统语言
# sudo dpkg-reconfigure locales
# sudo locale-gen zh_CN.UTF-8
# sudo update-locale LANG=zh_CN.UTF-8
fi
fi
# tomcat_idm_install 必备环境
if [ -d "$SHELL_FOLDER/tomcat_idm_install" ]; then
# 检查arp命令是否存在
if ! command -v arp &> /dev/null; then
echo_red "警告: arp命令不存在,IDM注册可能会出现异常"
exit 1
fi
fi
# 添加用户和组
if ! id -u medicom > /dev/null 2>&1; then
sudo useradd medicom
fi
if ! getent group medicom > /dev/null 2>&1; then
sudo groupadd medicom
fi
# 修改目录所有者和权限
sudo chown -R medicom:medicom "$SHELL_FOLDER"
sudo chmod -R 700 "$SHELL_FOLDER"
modify_config() {
local pattern=$1
local replacement=$2
local file=$3
sed -i "s|${pattern}|${replacement}|" "$file"
}
# 执行安装 database_install
if [ -d "$SHELL_FOLDER/database_install" ]; then
# regdatabase service
echo "注册数据库服务"
sh $SHELL_FOLDER/database_install/regService.sh
else
echo_red "警告: database_install 文件不存在,跳过安装"
fi
# 执行安装 redis_install
if [ -d "$SHELL_FOLDER/redis_install" ]; then
#regredis service
echo "注册redis服务"
sh $SHELL_FOLDER/redis_install/regService.sh
else
echo_red "警告: redis_install 文件不存在,跳过安装"
fi
# 执行安装 gateway_install
if [ -d "$SHELL_FOLDER/gateway_install" ]; then
# # reggateway service
echo "注册gateway服务"
sh $SHELL_FOLDER/gateway_install/regService.sh
else
echo_red "警告: gateway_install 文件不存在,跳过安装"
fi
# 执行安装 nacos_install
if [ -d "$SHELL_FOLDER/nacos_install" ]; then
# # regnacos service
echo "注册nacos服务"
sh $SHELL_FOLDER/nacos_install/regService.sh
else
echo_red "警告: nacos_install 文件不存在,跳过安装"
fi
# 执行安装 nginx_install
if [ -d "$SHELL_FOLDER/nginx_install" ]; then
# 修改 nginxconf
nginx_conf_path=$SHELL_FOLDER/nginx_install/nginx/conf/nginx.conf
echo "修改nginx的配置文件serverpath"
modify_config "set \$serverpath \"[^\"]*\"" "set \$serverpath \"${selected_ip}:9000\"" "$nginx_conf_path"
echo "修改nginx的配置文件rootpath"
modify_config "set \$rootpath \"[^\"]*\"" "set \$rootpath \"${SHELL_FOLDER}/resources/html\"" "$nginx_conf_path"
echo "修改nginx的配置文件lua_package_path"
modify_config "lua_package_path \"[^\"]*\"" "lua_package_path \"${SHELL_FOLDER}/nginx_install/lualib/?.lua;;\"" "$nginx_conf_path"
# # regnginx service
echo "注册nginx服务"
sh $SHELL_FOLDER/nginx_install/regService.sh
else
echo_red "警告: nginx_install 文件不存在,跳过安装"
fi
# 执行安装 tomcat_idm_install
if [ -d "$SHELL_FOLDER/tomcat_idm_install" ]; then
# 检查arp命令是否存在
if ! command -v arp &> /dev/null; then
echo_red "警告: arp命令不存在,IDM注册可能会出现异常"
fi
# # idm config path
idm_config_path="$SHELL_FOLDER/tomcat_idm_install/conf/server.xml"
echo "注册idm——tomcat服务"
sh $SHELL_FOLDER/tomcat_idm_install/regService.sh
else
echo_red "警告: tomcat_idm_install 文件不存在,跳过安装"
fi
# 执行安装 tomcat_pass_install
if [ -d "$SHELL_FOLDER/tomcat_pass_install" ]; then
# 定义文件路径
passapplicationfilepath="$SHELL_FOLDER/tomcat_pass_install/webapps-pass/ROOT/WEB-INF/classes/application.properties"
passcachedir="$SHELL_FOLDER/tomcat_pass_install/bin"
echo "修改pass的缓存配置文件cache.dir"
modify_config "spring.cache.dir=[^\"]*" "spring.cache.dir=${passcachedir}/data/run" "$passapplicationfilepath"
echo "修改pass的缓存配置文件cache.source"
modify_config "spring.cache.source=[^\"]*" "spring.cache.source=${passcachedir}/data/install" "$passapplicationfilepath"
echo "修改pass的缓存配置文件pass.resource"
modify_config "medicom.pass.resource=[^\"]*" "medicom.pass.resource=${passcachedir}/MedicomResources" "$passapplicationfilepath"
echo "修改pass的缓存配置文件pass.help"
modify_config "medicom.pass.help=[^\"]*" "medicom.pass.help=${passcachedir}/Help" "$passapplicationfilepath"
echo "修改pass的缓存配置文件upgrader.folder"
modify_config "pass.upgrader.folder=[^\"]*" "pass.upgrader.folder=${passcachedir}/UpdateFiles" "$passapplicationfilepath"
passdiskfilepath="$SHELL_FOLDER/tomcat_pass_install/webapps-pass/ROOT/WEB-INF/classes/ehcache_pass.xml"
echo "修改pass的缓存配置文件diskStore"
modify_config "diskStore path=\"[^\"]*\"" "diskStore path=\"${passcachedir}/data/run\"" "$passdiskfilepath"
echo "注册pass——tomcat服务"
sh $SHELL_FOLDER/tomcat_pass_install/regService.sh
else
echo_red "警告: tomcat_pass_install 文件不存在,跳过安装"
fi
# 执行安装 tomcat_service_install
if [ -d "$SHELL_FOLDER/tomcat_service_install" ]; then
echo "注册service——tomcat服务"
sh $SHELL_FOLDER/tomcat_service_install/regService.sh
else
echo_red "警告: tomcat_service_install 文件不存在,跳过安装"
fi
# 防火墙配置
open_port() {
local port=$1
local firewall_type=$2
echo $firewall_type 开放端口 $port
case $firewall_type in
"firewalld")
if ! firewall-cmd --list-ports | grep -q "${port}/tcp"; then
sudo firewall-cmd --permanent --add-port=${port}/tcp
else
echo "端口${port}已开放"
fi
;;
"ufw")
if ! ufw status | grep -q "${port}/tcp"; then
sudo ufw allow ${port}/tcp
else
echo "端口${port}已开放"
fi
;;
"iptables")
if ! iptables -L INPUT -n | grep -q "ACCEPT.*${port}"; then
sudo iptables -A INPUT -p tcp --dport ${port} -j ACCEPT
else
echo "端口${port}已开放"
fi
;;
esac
}
# 判断当前的防火墙类型
if systemctl status firewalld &> /dev/null; then
firewall_type="firewalld"
elif systemctl status ufw &> /dev/null; then
firewall_type="ufw"
elif iptables -L &> /dev/null; then
firewall_type="iptables"
else
echo_red "未知防火墙类型,无法自动开放端口"
exit 1
fi
echo "当前防火墙类型:$firewall_type"
# 开放端口
open_port 8848 "$firewall_type" #nacos
open_port 9000 "$firewall_type" #nginx
open_port 5432 "$firewall_type" #postgresql
# 根据防火墙类型重载配置
echo $firewall_type 重载配置
case $firewall_type in
"firewalld")
sudo firewall-cmd --reload
;;
"ufw")
sudo ufw reload
;;
"iptables")
echo iptables 持久化配置
sudo mkdir -p /etc/iptables
sudo iptables-save | sudo tee /etc/iptables/rules.v4 > /dev/null
cat <<EOF | sudo tee /etc/network/if-pre-up.d/iptables
#!/bin/sh
/sbin/iptables-restore < /etc/iptables/rules.v4
EOF
sudo chmod +x /etc/network/if-pre-up.d/iptables
;;
esac
echo "安装完成"
exit 0
四、总结
Shell 脚本编程是一项极具实用价值的重要技能,它能够助力用户极大地提高工作效率,实现任务处理的高度自动化。本文简单介绍了 Shell 脚本的基础用法,包括脚本结构、变量、控制结构和函数等各个方面,并提供了一些非常实用的脚本实例,例如文件备份脚本、系统监控脚本和自动化部署脚本。期望通过本文的全面介绍,读者能够对 Shell 脚本编程有入门基础了解,并在实际工作中加以灵活应用。
评论
评论列表
暂无评论