Shell脚本编程(bash)简述

目录

基础

脚本编写规范

运行方式

语法

变量

条件判断

循环

示例

免密登录配置

远程执行命令

批量同步配置文件

集群一键启动/停止脚本


针对集群运维

Shell教程

基础

脚本编写规范

①脚本文件后缀:通常以.sh结尾(并非强制,但便于识别)

②首行解释器:#!/bin/bash(固定写法,告诉系统用 bash 解释器执行脚本)

③注释规范

  • 单行注释:# 注释内容(用于解释单行命令或变量含义)

  • 多行注释:

    方式1:多行单注释(适合少量多行)

    这是一个大数据批量处理脚本

    功能:同步原始数据并进行格式转换

    方式2:利用here文档(适合大量多行注释)

    : <<EOF
    这是多行注释内容

    1. 脚本作者:XXX
    2. 适用场景:Hadoop数据预处理
    3. 执行条件:需拥有bigdata目录读写权限
      EOF

运行方式

①赋予执行权限:

复制代码
# 方式1:给当前用户添加执行权限(推荐,最安全)
chmod u+x bigdata_task.sh
# 方式2:给所有用户添加执行权限(适合公共脚本)
chmod a+x bigdata_task.sh
# 方式3:给当前用户读+写+执行权限,其他用户读+执行权限(精细权限控制)
chmod 755 bigdata_task.sh
# 权限说明:数字对应r(读=4)、w(写=2)、x(执行=1),7=4+2+1,5=4+1

②运行脚本:

复制代码
# 方式1:相对路径运行(推荐,需先赋予执行权限)
./bigdata_task.sh
# 适用:脚本在当前工作目录下

# 方式2:绝对路径运行(推荐,需先赋予执行权限)
/opt/bigdata/scripts/bigdata_task.sh
# 适用:脚本在固定目录,便于定时任务调用

# 方式3:通过解释器直接运行(无需赋予执行权限)
bash bigdata_task.sh
# 或
sh bigdata_task.sh
# 适用:临时运行脚本,无需修改权限

# 方式4:在当前shell环境中运行(用.或source)
. ./bigdata_task.sh
# 或
source ./bigdata_task.sh
# 适用:脚本中定义的变量需要在当前shell中生效(如配置环境变量的脚本)

语法

变量

①变量的定义:

  1. 变量名=值(等号前后无空格,不能包含特殊字符)

  2. 变量名只能包含字母、数字、下划线,不能以数字开头

  3. 变量值如果包含空格,需要用双引号包裹

    #!/bin/bash

    定义集群运维常用变量(贴合实际场景)

    HADOOP_HOME="/opt/hadoop" # Hadoop安装目录(无空格,直接定义)
    SPARK_HOME="/opt/spark-3.3.0" # Spark安装目录
    CLUSTER_NODES="master slave1 slave2 slave3" # 集群节点列表(有空格,用双引号包裹也可以)
    LOG_DIR="/bigdata/ops/logs" # 运维日志目录
    DATA_DIR="/bigdata/data" # 数据存储目录

②变量的使用:

  • 调用变量时,在变量名前加 ,推荐用 {变量名} 的格式,更规范。

    #!/bin/bash

    定义变量

    HADOOP_HOME="/opt/hadoop"
    LOG_DIR="/bigdata/ops/logs"

    使用变量

    echo "Hadoop安装目录:HADOOP_HOME" # 简单调用 echo "运维日志目录:{LOG_DIR}" # 推荐格式,更规范
    echo "HDFS启动脚本路径:${HADOOP_HOME}/sbin/start-dfs.sh" # 拼接路径(核心用法)

③特殊变量:

  • $0:脚本自身文件名

  • 1、2...:接收脚本运行时传入的参数

    #!/bin/bash

    脚本名称:check_node.sh

    运行方式:./check_node.sh slave1 20251221

    echo "要检查的节点名称:1" # 接收第一个参数(slave1) echo "检查日期:2" # 接收第二个参数(20251221)
    echo "脚本文件名:0" # 0 固定表示脚本本身的名字(check_node.sh)

  • {n}:第 3\~9 个位置参数(超过 9 个需用{10}格式)

  • $#:位置参数的总个数(统计传入参数的数量)

  • $*:所有位置参数的集合,将所有参数视为一个整体(命令用双引号包裹时生效)

  • $@:所有位置参数的集合,将所有参数中每个参数视为独立个体(用双引号包裹时生效)

    #!/bin/bash

    示例:区分*和@

    echo "===== 遍历$*(视为整体) ====="
    for arg in "*"; do echo "参数:arg" # 仅输出1行:参数:a b c
    done

    echo "===== 遍历$@(视为独立个体,推荐) ====="
    for arg in "@"; do echo "参数:arg" # 输出3行:参数:a;参数:b;参数:c
    done

  • ?:获取上一条命令的执行结果,? 只有两个值:0 表示上一条命令执行成功,非0 表示执行失败,这是运维脚本中 "判断命令是否生效"的用法。

    #!/bin/bash

    尝试启动HDFS

    /opt/hadoop/sbin/start-dfs.sh

    判断启动是否成功

    if [ $? -eq 0 ]; then
    echo "HDFS启动成功!"
    else
    echo "HDFS启动失败!"
    fi

条件判断

①语法格式:

复制代码
# 格式1:test + 空格 + 条件表达式(空格不可省略,用于区分命令和表达式)
#  Shell 内置的条件检测命令,无需依赖特殊解释器
test -f "$DATA_FILE"

# 格式2:[ + 空格 + 条件表达式 + 空格 + ](推荐,更直观,注意前后必须有空格)
[ -f "$DATA_FILE" ]

# 格式3:[[ + 条件表达式 + ]](bash专属,支持正则匹配、逻辑运算更简洁)
[[ "$DATA_DATE" =~ ^[0-9]{8}$ ]]


# 写法
if [ 条件表达式 ]; then
    # 条件满足时,执行的命令(可以写多行)
    命令1
    命令2
else
    # 条件不满足时,执行的命令(可以写多行)
    命令3
    命令4
fi

②常用判断条件:

判断类型 条件表达式 含义
文件判断 -f "$file" 判断是否为普通文件
文件判断 -d "$dir" 判断是否为目录
文件判断 -e "$path" 判断文件 / 目录是否存在
文件判断 -r "$file" 判断是否有读权限(读取数据文件时校验)
文件判断 -w "$file" 判断是否有写权限(写入数据时校验)
字符串判断 "\\(str1" = "\\)str2" 判断两个字符串相等(注意等号前后有空格)
字符串判断 "\\(str1" != "\\)str2" 判断两个字符串不相等
字符串判断 -n "$str" 判断字符串非空(校验参数是否传入)
字符串判断 -z "$str" 判断字符串为空
数字判断 $num1 -eq $num2 等于(equal)
数字判断 $num1 -ne $num2 不等于(not equal)
数字判断 $num1 -gt $num2 大于(greater than)
数字判断 $num1 -lt $num2 小于(less than)
数字判断 $num1 -ge $num2 大于等于(greater or equal)
数字判断 $num1 -le $num2 小于等于(less or equal)
逻辑运算 条件 1 && 条件 2 逻辑与(两个条件都成立)
逻辑运算 条件 1 || 条件 2 逻辑或(两个条件有一个成立)
逻辑运算 ! 条件 逻辑非(条件不成立)
复制代码
#!/bin/bash
# 条件判断实战:集群环境前置检查
HADOOP_HOME="/opt/hadoop"
LOG_DIR="/bigdata/ops/logs"
DISK_USAGE=86  # 模拟磁盘使用率

# 1. 判断Hadoop目录是否存在
if [ -d "${HADOOP_HOME}" ]; then
    echo "Hadoop目录存在:${HADOOP_HOME}"
else
    echo "错误:Hadoop目录不存在!"
    exit 1  # 退出脚本,不再执行后续内容
fi

# 2. 判断日志目录是否存在,不存在则创建
if [ ! -d "${LOG_DIR}" ]; then  # ! 表示"非",即目录不存在时
    echo "日志目录不存在,正在创建:${LOG_DIR}"
    mkdir -p "${LOG_DIR}"  # -p 递归创建目录
fi

# 3. 判断磁盘使用率是否超标(大于85%)
if [ ${DISK_USAGE} -gt 85 ]; then
    echo "警告:磁盘使用率超标(${DISK_USAGE}%),请及时清理!"
else
    echo "磁盘使用率正常(${DISK_USAGE}%)"
fi

循环

①for循环:

  • 基础格式:

    格式1:遍历固定列表(适合集群节点批量操作)

    for 变量名 in 列表内容; do
    # 循环执行的命令
    命令
    done

    格式2:数字循环(适合按序号遍历分区/任务)

    for ((i=1; i<=5; i++)); do
    # 循环执行的命令
    命令
    done

    #!/bin/bash

    批量检查所有从节点的DataNode进程

    定义节点列表

    nodes=("slave1" "slave2" "slave3")

    遍历节点列表

    for node in "{nodes[@]}"; do # "{nodes[@]}" 表示遍历所有节点
    echo "===== 正在检查节点:{node} =====" # 远程执行命令,检查DataNode进程 ssh root@{node} "jps | grep DataNode | grep -v grep"
    # 判断进程是否存在
    if [ ? -eq 0 ]; then echo "节点{node}:DataNode进程正常"
    else
    echo "节点${node}:DataNode进程缺失(异常)"
    fi
    done

②while循环:

  • 基础格式

    while [ 条件表达式 ]; do
    # 条件满足时,循环执行的命令
    命令
    sleep 10 # 休眠10秒,避免循环过快占用资源
    done

    特殊:无限循环(适合集群监控脚本)

    while true; do
    # 持续执行的命令(如监控进程状态)
    命令
    sleep 60 # 每分钟检查一次
    done

    #!/bin/bash

    监控NameNode进程,异常时打印告警

    while true; do
    # 检查NameNode进程
    jps | grep NameNode | grep -v grep
    if [ ? -ne 0 ]; then echo "[(date +'%Y-%m-%d %H:%M:%S')] 告警:NameNode进程异常退出!"
    else
    echo "[$(date +'%Y-%m-%d %H:%M:%S')] NameNode进程运行正常"
    fi
    sleep 60 # 每分钟检查一次
    done

示例

免密登录配置

复制代码
# 1. 在主节点生成RSA密钥对(一路回车,不设置密钥密码)
ssh-keygen -t rsa -b 4096  # -b 4096提升密钥安全性

# 2. 将公钥批量分发到所有集群节点(包括主节点自身)
# 定义节点列表(替换为你的集群节点)
nodes=("master" "slave1" "slave2" "slave3")
for node in "${nodes[@]}"; do
    ssh-copy-id -i ~/.ssh/id_rsa.pub root@${node}
done

# 3. 验证免密登录(无需密码即可登录从节点)
ssh slave1

远程执行命令

复制代码
# 场景1:批量检查所有从节点的DataNode进程
nodes=("slave1" "slave2" "slave3")
for node in "${nodes[@]}"; do
    echo "===== 节点${node}进程状态 ====="
    ssh root@${node} "jps | grep DataNode | grep -v grep"
done

# 场景2:批量在从节点创建数据目录
for node in "${nodes[@]}"; do
    ssh root@${node} "mkdir -p /bigdata/data /bigdata/logs && chmod 755 /bigdata"
done

# 场景3:批量查看从节点磁盘使用率
for node in "${nodes[@]}"; do
    echo "===== 节点${node}磁盘使用情况 ====="
    ssh root@${node} "df -h | grep /bigdata"
done

批量同步配置文件

复制代码
#!/bin/bash
# 批量同步Hadoop配置文件
SRC_DIR="/opt/hadoop/conf"  # 主节点配置源目录
DST_DIR="/opt/hadoop/conf"  # 从节点配置目标目录
nodes=("slave1" "slave2" "slave3")  # 从节点列表

for node in "${nodes[@]}"; do
    echo "===== 同步配置到节点${node} ====="
    # 步骤1:备份从节点原有配置
    BACKUP_DIR="${DST_DIR}_backup_$(date +%Y%m%d%H%M%S)"
    ssh root@${node} "mkdir -p ${BACKUP_DIR} && cp -r ${DST_DIR}/* ${BACKUP_DIR}/"
    
    # 步骤2:批量同步配置文件(-r 递归复制,-p 保留权限)
    scp -rp "${SRC_DIR}"/* root@${node}:"${DST_DIR}/"
    
    # 步骤3:验证配置一致性(对比MD5值)
    SRC_MD5=$(md5sum "${SRC_DIR}/core-site.xml" | awk '{print $1}')
    DST_MD5=$(ssh root@${node} "md5sum ${DST_DIR}/core-site.xml | awk '{print \$1}'")
    if [ "${SRC_MD5}" = "${DST_MD5}" ]; then
        echo "节点${node}配置同步成功,一致性验证通过"
    else
        echo "节点${node}配置同步失败,已自动回滚到${BACKUP_DIR}"
        ssh root@${node} "cp -r ${BACKUP_DIR}/* ${DST_DIR}/"
    fi
done

集群一键启动/停止脚本

复制代码
#!/bin/bash
# 集群一键运维脚本:启动/停止Hadoop+Spark+HBase
# 用法:./cluster_start_stop.sh [start|stop]

# 全局配置
HADOOP_HOME="/opt/hadoop-3.3.4"
SPARK_HOME="/opt/spark-3.3.0"
HBASE_HOME="/opt/hbase-2.5.3"
SLAVE_NODES=("slave1" "slave2" "slave3")
OPS_LOG_DIR="/bigdata/ops/logs"
mkdir -p "${OPS_LOG_DIR}"
LOG_FILE="${OPS_LOG_DIR}/cluster_start_stop_$(date +%Y%m%d).log"
exec > "${LOG_FILE}" 2>&1

# 颜色输出函数
echo_info() { echo -e "\033[32m[INFO] $(date +'%Y-%m-%d %H:%M:%S') $1\033[0m"; }
echo_error() { echo -e "\033[31m[ERROR] $(date +'%Y-%m-%d %H:%M:%S') $1\033[0m"; }

# 集群启动函数
start_cluster() {
    echo_info "===== 开始启动大数据集群 ====="
    
    # 1. 启动Hadoop(HDFS+YARN)
    echo_info "步骤1:启动Hadoop集群..."
    ${HADOOP_HOME}/sbin/start-dfs.sh
    if [ $? -ne 0 ]; then
        echo_error "HDFS启动失败,查看日志:${LOG_FILE}"
        exit 1
    fi
    sleep 10  # 等待HDFS初始化完成
    ${HADOOP_HOME}/sbin/start-yarn.sh
    if [ $? -ne 0 ]; then
        echo_error "YARN启动失败,查看日志:${LOG_FILE}"
        exit 1
    fi
    # 启动HistoryServer(便于查看任务历史)
    ${HADOOP_HOME}/sbin/mr-jobhistory-daemon.sh start historyserver

    # 2. 启动Spark集群
    echo_info "步骤2:启动Spark集群..."
    ${SPARK_HOME}/sbin/start-all.sh
    if [ $? -ne 0 ]; then
        echo_error "Spark启动失败,查看日志:${LOG_FILE}"
        exit 1
    fi
    # 启动Spark HistoryServer
    ${SPARK_HOME}/sbin/start-history-server.sh

    # 3. 启动HBase集群
    echo_info "步骤3:启动HBase集群..."
    ${HBASE_HOME}/bin/start-hbase.sh
    if [ $? -ne 0 ]; then
        echo_error "HBase启动失败,查看日志:${LOG_FILE}"
        exit 1
    fi

    # 4. 验证启动状态
    echo_info "===== 集群启动完成,验证核心进程 ====="
    jps | grep -E "NameNode|ResourceManager|Master|HMaster"
    for node in "${SLAVE_NODES[@]}"; do
        echo_info "节点${node}进程状态:"
        ssh root@${node} "jps | grep -E 'DataNode|NodeManager|Worker|RegionServer'"
    done
    echo_info "集群启动日志已保存至:${LOG_FILE}"
}

# 集群停止函数
stop_cluster() {
    echo_info "===== 开始停止大数据集群 ====="
    
    # 1. 停止HBase集群
    echo_info "步骤1:停止HBase集群..."
    ${HBASE_HOME}/bin/stop-hbase.sh

    # 2. 停止Spark集群
    echo_info "步骤2:停止Spark集群..."
    ${SPARK_HOME}/sbin/stop-history-server.sh
    ${SPARK_HOME}/sbin/stop-all.sh

    # 3. 停止Hadoop集群
    echo_info "步骤3:停止Hadoop集群..."
    ${HADOOP_HOME}/sbin/mr-jobhistory-daemon.sh stop historyserver
    ${HADOOP_HOME}/sbin/stop-yarn.sh
    ${HADOOP_HOME}/sbin/stop-dfs.sh

    # 4. 验证停止状态
    echo_info "===== 集群停止完成,验证进程是否清理 ====="
    jps | grep -vE "Jps|${0##*/}"
    for node in "${SLAVE_NODES[@]}"; do
        echo_info "节点${node}剩余进程:"
        ssh root@${node} "jps | grep -vE 'Jps'"
    done
    echo_info "集群停止日志已保存至:${LOG_FILE}"
}

# 主逻辑
if [ $# -ne 1 ]; then
    echo "用法:$0 [start|stop]"
    exit 1
fi

case $1 in
    "start") start_cluster ;;
    "stop") stop_cluster ;;
    *) echo_error "无效参数,仅支持start/stop"; exit 1 ;;
esac

exit 0
相关推荐
Scott9999HH6 小时前
【IIoT流量实战】蒸汽管道阀门全关却仍有流量?用 Python 实现涡街信号 FFT 频谱分析与温压全补偿积算网关,深度拆解靠谱的涡街流量计厂家硬核技术标准
开发语言·python
码智社7 小时前
AES加密原理详解及Java实现加解密实战
java·开发语言
AI云海7 小时前
python 列表、元组、集合和字典
开发语言·python
萧瑟余晖8 小时前
JDK 26 新特性详解
java·开发语言
马优晨8 小时前
Freemarker 完整讲解(后端 Java 模板引擎)
java·开发语言·freemarker·freemarker 完整讲解·freemarker模板引擎
人邮异步社区10 小时前
怎么把C语言学到精通?
c语言·开发语言
心平气和量大福大11 小时前
C#-WPF-控件-TextBox 数据绑定
开发语言·c#·wpf
ttwuai11 小时前
Cursor 生成 CRUD 后,Go 后台接口别只测 200:JWT、RBAC 和 tenant_id 怎么验
开发语言·后端·golang
এ慕ོ冬℘゜11 小时前
前端基础:什么是时间戳?JS获取时间戳三种方法与实战用途
开发语言·前端·javascript
执明wa11 小时前
LayoutInflater详解: XML是如何变成View的?
android·xml·开发语言·android studio