06-流程控制之for循环结构

运维为什么学bash for循环

运维人员学习bash for循环主要有以下几个重要原因:

批量操作

  • 在运维工作中,经常需要对大量服务器、文件或系统配置进行相同的操作。例如,要在多个服务器上安装相同的软件包、配置相同的服务参数等。使用bash for循环可以轻松实现批量操作,通过遍历服务器列表或文件列表,在每次循环中执行相应的命令,大大提高工作效率,减少手动操作的时间和工作量。比如以下代码可以批量在多个服务器上安装httpd服务: ```bash

    !/bin/bash

    数组 vmware 多开三个虚拟机

    servers=("10.0.0.2" "127.0.0.1" )

for server in "${servers[@]}"; do echo "本次循环拿到的IP是:$server" ssh -o StrictHostKeyChecking=no $server "apt install -y httpd" done


### 自动化配置
- 运维人员需要对系统进行各种配置,如设置环境变量、创建用户、配置网络等。for循环可以与其他命令结合,根据不同的条件或需求,自动生成和执行相应的配置命令。以创建多个用户并设置密码为例:
```bash
#!/bin/bash

users=(user1 user2 user3)


for user in "${users[@]}"; do

    useradd -m -s /bin/bash $user
    if [ $? -eq 0  ];then
        echo "$user:${user}666" |chpasswd
         if [ $? -eq 0  ];then
             echo "$user 密码设置成了!"
             fi
        fi
    #echo "password" | passwd --stdin $user
done


for user in "${users[@]}"; do
    id $user
done

日志处理

  • 运维工作中经常需要处理大量的日志文件,比如分析日志查找错误信息、统计日志中的特定事件数量等。for循环可以方便地遍历日志文件列表,对每个日志文件执行相应的处理命令。以下是一个简单的示例,用于统计每个日志文件中特定错误信息的出现次数: ```bash

    !/bin/bash

log_files=("/var/log/syslog" "/var/log/dmesg")

for file in "${log_files[@]}"; do echo "统计$file中错误信息出现的次数:" grep "error" $file | wc -l done


### 监控与告警
- 在系统监控方面,for循环可以用于定期检查系统的各项指标,如CPU使用率、内存使用率、磁盘空间等。通过在循环中执行监控命令,并设置阈值,当指标超过阈值时发送告警信息。例如,以下代码可以每隔1分钟检查一次服务器的CPU使用率,如果超过80%就发送告警邮件:
```bash
#!/bin/bash

while true; do
    cpu_usage=$(top -bn1 | grep "Cpu(s)" | awk '{print $2 + $4}')
    if (( $(echo "$cpu_usage > 80" | bc -l) )); then
        echo "CPU使用率过高:$cpu_usage%" | mail -s "CPU告警" admin@example.com
    fi
    sleep 60
done

脚本编写

  • bash脚本是运维工作中常用的工具,for循环是编写高效、灵活脚本的重要组成部分。掌握for循环可以使运维人员编写出更复杂、功能更强大的脚本,实现各种自动化任务和流程。比如一个简单的备份脚本,使用for循环遍历需要备份的目录,并将其打包备份: ```bash

    !/bin/bash

backup_dirs=(/home/user1 /home/user2 /var/www) backup_path=/backup

for dir in "${backup_dirs[@]}"; do file_name=$(basename $dir).tar.gz tar -czvf $backup_path/$file_name $dir done


# 1.for循环使用场景
  1. 需要反复、重复执行的任务
  2. 如创建100个用户,打印一百遍 chaoge666、插入数据库一万条数据等。

SQL


# 2.for语法

for 变量名 in 取值列表 do 每次循环要执行的命令 done

for默认以空格分割独立的元素


# 3.for循环几个场景

## 3.1 循环多个字符串参数

场景1,多个单个参数

!/bin/bash

for cai in "红烧肉" "酱牛肉" "烤羊肉串" "烤土豆片" do echo "菜品:" $cai done


场景2,不一样的写法,如下结果是什么?

!/bin/bash

for cai in "红烧肉 五花肉 爆炒腰花" "酱牛肉 酱牛毽子" "烤羊肉串 烤羊宝" "烤土豆片 烤菜花" do echo "菜品:" $cai done




## 3.2 从变量中循环取值

简单语法

!/bin/bash

all_path="/opt /var /etc /home"

all_cal="红烧肉 五花肉 爆炒腰花 酱牛肉 酱牛毽子 烤羊肉串 烤羊宝 烤土豆片 烤菜花"

for cai in $all_cal do echo $cai done




## 面试题,提取PATH的值

需求:提取现有机器上PATH变量的每一个路径。

!/bin/bash

local_path=$(echo $PATH | sed 's#\:# #g') for l in $local_path do echo $l done


在不同的操作系统和编程语言中,`PATH` 变量的含义和提取方式有所不同。`PATH` 变量通常用于存储系统在查找可执行文件时会搜索的目录列表。以下分别介绍在 shell 脚本(常用于 Linux、macOS 等系统)和 Python 语言中如何使用 `for` 循环来提取 `PATH` 值。

### 1. 使用 shell 脚本提取 `PATH` 值
在 Linux 和 macOS 等系统中,`PATH` 是一个环境变量,目录之间用冒号 `:` 分隔。下面是一个使用 `for` 循环提取 `PATH` 中各个目录的 shell 脚本示例:
```bash
#!/bin/bash

# 遍历 PATH 变量中的每个目录
IFS=':' read -ra mypath <<< "$PATH"
for dir in "${mypath[@]}"; do
    echo "$dir"
done

代码解释:

  • IFS=':':将内部字段分隔符(Internal Field Separator)设置为冒号 :,这样在分割字符串时会以冒号为分隔符。
  • read -ra dirs <<< "$PATH":将 PATH 变量的值按冒号分割成一个数组 dirs
  • for dir in "${dirs[@]}":遍历数组 dirs 中的每个元素,将其赋值给变量 dir
  • echo "$dir":打印当前目录。

使用方法:

将上述代码保存为一个文件,例如 extract_path.sh,然后给该文件添加执行权限并运行:

chmod +x extract_path.sh
./extract_path.sh

2. 使用 Python 提取 PATH

在 Python 中,可以使用 os.environ 来访问环境变量,然后使用 split() 方法分割 PATH 变量的值。以下是一个使用 for 循环提取 PATH 中各个目录的 Python 示例:

import os

# 获取 PATH 环境变量的值
path = os.environ.get('PATH')

# 分割 PATH 变量的值
path_dirs = path.split(':')

# 遍历每个目录并打印
for dir in path_dirs:
    print(dir)

代码解释:

  • os.environ.get('PATH'):获取 PATH 环境变量的值。
  • path.split(':'):将 PATH 变量的值按冒号分割成一个列表 path_dirs
  • for dir in path_dirs:遍历列表 path_dirs 中的每个元素,将其赋值给变量 dir
  • print(dir):打印当前目录。

使用方法:

将上述代码保存为一个文件,例如 extract_path.py,然后运行:

python extract_path.py

请注意,在 Windows 系统中,PATH 变量的目录分隔符是分号 ;,因此在 Windows 系统中使用上述代码时,需要将分隔符修改为分号。在 Python 中,可以使用 os.pathsep 来自动处理不同系统的分隔符:

import os

# 获取 PATH 环境变量的值
path = os.environ.get('PATH')

# 分割 PATH 变量的值,使用 os.pathsep 自动处理不同系统的分隔符
path_dirs = path.split(os.pathsep)

# 遍历每个目录并打印
for dir in path_dirs:
    print(dir)

awk 中提取 PATH 变量的值通常可以结合 shell 命令来完成。PATH 变量是一个环境变量,它存储了一系列用冒号 : 分隔的目录路径,这些目录是系统在查找可执行文件时会搜索的位置。

方法一:在 shell 脚本中直接使用 awk 处理 PATH 变量

以下是一个示例脚本,展示了如何使用 awk 来提取并处理 PATH 变量的值:

#!/bin/bash

# 获取 PATH 变量的值
path_value="$PATH"

# 使用 awk 处理 PATH 变量的值
echo "$path_value" | awk -F ':' '{
    for (i = 1; i <= NF; i++) {
        print "Path entry " i ": " $i
    }
}'

代码解释:

  1. 获取 PATH 变量的值:通过 path_value="$PATH" 将系统的 PATH 变量值赋给 path_value 变量。
  2. 使用 awk 处理 PATH 变量的值
    • -F ':':指定 awk 的字段分隔符为冒号 :,因为 PATH 变量中的路径是用冒号分隔的。
    • { for (i = 1; i <= NF; i++) { print "Path entry " i ": " $i } }:这是 awk 的动作部分,使用 for 循环遍历每个字段(路径),NFawk 的内置变量,表示当前记录的字段数。$i 表示第 i 个字段的值。

方法二:直接在命令行中使用 awk 处理 PATH 变量

你也可以直接在命令行中执行以下命令来达到相同的效果:

echo $PATH | awk -F ':' '{for (i = 1; i <= NF; i++) print "Path entry " i ": " $i}'

这个命令会将 PATH 变量的值通过管道传递给 awk 进行处理,最终输出每个路径条目及其对应的编号。

输出示例

假设 PATH 变量的值为 /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin,上述命令的输出可能如下:

Path entry 1: /usr/local/sbin
Path entry 2: /usr/local/bin
Path entry 3: /usr/sbin
Path entry 4: /usr/bin
Path entry 5: /sbin
Path entry 6: /bin

通过这种方式,你可以方便地提取和处理 PATH 变量中的每个路径条目。

3.3 读取花括号序列

#!/bin/bash
for num in {0..15}
do
echo "循环的数字是:$num"
done

写法2,c语言风格,了解即可

#!/bin/bash
# for (初始化变量;限定条件;增长规则)
for (( num=0;num<=15;num++ ))
do
    echo "循环的数字是:${num}"
done

3.4 从文件读取数据

注意,for循环是以遇见空格来进行换行,确认每一个元素,因此注意这个细节。

cat > shaokao.log <<'EOF'
烤羊头 烤羊肉串 烤羊宝
烤韭菜
烤土豆
EOF

# 循环读取
for cai in $(cat shaokao.log)
do
    echo "老板,来俩: $cai"
done

3.5 循环创建用户

#!/bin/bash
for num in {1..10}
do
    # 创建用户
    useradd -m -s /bin/bash yu${num}
    # 创建密码 chpasswd
    echo "yu${num}:pwd${num}" |chpasswd
done

for num in {1..10};do
    id yu${num}
    done

删除系统上所有的普通用户,思考怎么写

# 普通用户,uid > 1000 ,sed awk grep

在Bash中,删除系统上所有普通用户可以通过多种方式实现。普通用户通常指的是 UID 大于等于 1000 的用户(在大多数 Linux 发行版中,UID 1 - 999 通常为系统用户,UID 1000 及以上为普通用户)。以下是几种不同的实现方式:

方法一:使用 for 循环和 userdel 命令

#!/bin/bash

# 获取所有普通用户列表
users=$(awk -F: '$3 >= 1000 {print $1}' /etc/passwd)

# 遍历用户列表并删除用户
for user in $users; do
    userdel -r "$user"
    if [ $? -eq 0 ]; then
        echo "用户 $user 已成功删除。"
    else
        echo "删除用户 $user 时出错。"
    fi
done

解释

  1. awk -F: '$3 >= 1000 {print $1}' /etc/passwd:使用 awk 命令从 /etc/passwd 文件中提取 UID 大于等于 1000 的用户名称。
  2. for user in $users:遍历用户列表。
  3. userdel -r "$user":使用 userdel 命令删除用户及其主目录。
  4. $?:表示上一个命令的退出状态码,0 表示成功,非 0 表示失败。

方法二:使用 while 循环和 userdel 命令

#!/bin/bash

# 获取所有普通用户列表
awk -F: '$3 >= 1000 {print $1}' /etc/passwd | while read user; do
    userdel -r "$user" > /dev/null 2>&1
    if [ $? -eq 0 ]; then
        echo "用户 $user 已成功删除。"
    else
        echo "删除用户 $user 时出错。"
    fi
done

解释

  1. awk -F: '$3 >= 1000 {print $1}' /etc/passwd:提取 UID 大于等于 1000 的用户名称。
  2. while read user:逐行读取用户列表。
  3. userdel -r "$user":删除用户及其主目录。

方法三:使用 xargs 命令

#!/bin/bash

# 获取所有普通用户列表并使用 xargs 删除用户
awk -F: '$3 >= 1000 {print $1}' /etc/passwd | xargs -I {} userdel -r {}

解释

  1. awk -F: '$3 >= 1000 {print $1}' /etc/passwd:提取 UID 大于等于 1000 的用户名称。
  2. xargs -I {} userdel -r {}:将用户名称作为参数传递给 userdel 命令,-I {} 表示将 {} 作为占位符替换为实际的用户名称。

注意事项

  • 权限问题:执行这些脚本需要使用 root 权限,因为删除用户需要管理员权限。
  • 数据丢失userdel -r 命令会删除用户的主目录及其所有内容,请确保在执行之前备份重要数据。
  • 系统稳定性:删除所有普通用户可能会影响系统的正常运行,请谨慎操作。

三剑客处理普通用户删除

题目解析

删除系统中 UID 大于 1000 的普通用户涉及到修改 /etc/passwd 文件中的用户信息。以下是使用 sedawkgrep 实现的具体方法。


方法一:使用 sed

sed 是一种流编辑工具,可以直接编辑文本文件内容。

sed -i '/:[1-9][0-9][0-9][0-9]:[0-9]*:/d' /etc/passwd

解释

  • -i:直接在文件中修改。
  • /:[1-9][0-9][0-9][0-9]:[0-9]*:/d
    • 匹配 UID 大于 1000 的用户行(格式如 username:x:1001:1001:...)。
    • :[1-9][0-9][0-9][0-9]: 匹配 UID 大于 1000 的字段。
    • d 表示删除匹配的行。

方法二:使用 awk

awk 可以按条件筛选并输出符合需求的行。

awk -F: '$3 <= 1000 {print}' /etc/passwd > /tmp/passwd && mv /tmp/passwd /etc/passwd

解释

  • -F::设置字段分隔符为 :
  • $3:表示第三列(即 UID)。
  • $3 <= 1000:保留 UID 小于等于 1000 的用户。
  • > /tmp/passwd:将结果输出到临时文件。
  • mv /tmp/passwd /etc/passwd:替换原始文件。

方法三:使用 grep

grep 可以用来排除匹配的行,然后输出到新文件。

grep -Ev ':[1-9][0-9][0-9][0-9]:[0-9]*:' /etc/passwd > /tmp/passwd && mv /tmp/passwd /etc/passwd

解释

  • -E:启用扩展正则表达式。
  • -v:排除匹配的行。
  • '...:[1-9][0-9][0-9][0-9]:...':匹配 UID 大于 1000 的用户行。
  • > /tmp/passwd:将排除后的内容输出到临时文件。
  • mv /tmp/passwd /etc/passwd:替换原始文件。

注意事项

  1. 备份文件:修改 /etc/passwd 前,务必备份。
    cp /etc/passwd /etc/passwd.bak
    
  2. 同步组文件:删除用户后,可能需要同步更新 /etc/group 文件。
  3. 谨慎操作:误操作可能导致系统无法正常登录或使用。

选择哪种方法?

根据实际需求,awkgrep 通常更直观,sed 更适合直接操作小型文件。

3.6 循环删除用户

#!/bin/bash
for user in yu{1..10}
do
    userdel ${user}
done

3.7 使用case开发用户创建脚本

提供菜单选项
1. useradd
2. userdel
以及输入用户名前缀,即可快速创建10个用户

代码

#!/bin/bash
echo -e "====用户快捷创建系统=====
1. 用户创建
2. 用户删除"

read -p "请选择你要执行的操作:" choice

case $choice in
1)
    read -p "请输入用户名前缀:" user
    read -p "请输入密码前缀:" pwd
    # 循环创建10个用户
    for num in {1..10}
    do
        echo "本次循环序号:$num"
        useradd -m -s /bin/bash ${user}${num}
        echo "${user}${num}:${pwd}${num}" | chpasswd
    done
    ;;

2)
    read -p "请输入要删除的用户名前缀:" del_user
    for num in {1..10}
    do
        userdel -r ${del_user}${num} > /dev/null 2>&1
    done
    ;;
*)
    echo "只支持1~2选项!!"

esac

3.8 读取密码本,创建用户

码本

# 提取用户名,useradd创建,for循环创建
# echo user1:pass1 | chpasswd


cat >user_info.log<<'EOF'
user1:pass1
user2:pass2
user3:pass3
user4:pass4
user5:pass5
user6:pass6
user7:pass7
user8:pass8
user9:pass9
user10:pass10
EOF

开发脚本读取密码本,创建用户且设置密码

# 
for i in $(cat user_info.log)
do
    # 提取用户名
    user=$(echo $i|awk -F':' '{print $1}')
    # 创建用户
    # 校验,如果用户已存在,就别执行useradd了
    #useradd -m -s /bin/bash ${user}

    echo "$i" | chpasswd

    # 提取密码
    #pwd=$(echo $i|awk -F':' '{print $2}')
    # 创建密码
    #echo ${pwd}|passwd --stdin ${user}
done

3.9 主机存活检测

这种写法是单进程检测

#!/bin/bash
for i in {1..254}
do
    ping -w 1 192.168.1.${i} > /dev/null 2>&1
    # 存活判断
    if [ $? == 0 ];then
        echo "192.168.1.${i} 运行中" >> online_ip.log
    fi
done

优化写法,全部放入后台执行,并发执行。

可以通过jobs查后台进程列表。

#!/bin/bash
> ip.txt

for ip in {1..50}
do  
   ping -w 30 192.168.110.$ip >> ip.txt &
done

echo "存活主机如下:"
# 寻找有数据包返回的行就行了
awk -F"[: ]" '/icmp/{print $4}' ip.txt

3.10 提取于超老师博客园博文链接

# 思路

# 步骤1.下载超哥博客园首页html,里面有相关文章的url,提取即可
curl -s https://www.cnblogs.com/pyyu -o www.yuchaoit.cn.html


# 思路1,提取出关于具体文章的url,请用多种命令提取。
awk -F'\"' '/postTitle2/{print $4}' www.yuchaoit.cn.html > all_url.log
grep 'postTitle2' www.yuchaoit.cn.html |grep -o 'ht.*l'
grep 'postTitle2' www.yuchaoit.cn.html |sed -r 's/(^.*)="(.*)">/\2/g'
grep 'postTitle2' www.yuchaoit.cn.html |sed -r 's/.*="(.*)">/\1/g'



# 思路2,提取出文章的标题
sed -rn '/postTitle2/,/<\/span>/p' www.yuchaoit.cn.html |grep -Ev '[<>]' | sed 's/ //g' > all_title.log

# 思路3,拼接url和标题(paste命令)
[root@www.yuchaoit.cn ~]#paste -d " " all_title.log all_url.log  > chaoge_blog.log
[root@www.yuchaoit.cn ~]#
[root@www.yuchaoit.cn ~]#cat chaoge_blog.log |column -t

# 思路4,shell拼接url和标题(遍历数组写法)
all_url=($(cat all_url.log))
all_title=($(cat all_title.log))
url_num=$(cat all_url.log|wc -l)
for (( i=0;i<$url_num;i++ ))
do
    echo -e "【于超老师博客】${all_url[i]} \t  ${all_title[i]}"
done



# 思路5
title_num=$(cat all_title.log|wc -l)
# 循环用sed打印每一行,拼接即可
for line_num in $(seq 1 ${title_num})
do
    each_title=$(sed -n "${line_num}"p all_title.log)
    each_url=$(sed -n "${line_num}"p all_url.log)
    echo -e "${each_title} \t ${each_url}" >> chaoge_blog.log
done

# 查看结果
cat chaoge_blog.log |column -t

3.11 mysql数据库分库分表备份

安装mysql

# 1  .安装mysql数据库,启动
apt install mysql-server mysql-client
mysql> ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'chaoge666';
Query OK, 0 rows affected (0.00 sec)

mysql> flush privileges;
Query OK, 0 rows affected (0.01 sec)

mysql>

# 2. 创建测试库,测试表数据

# 3. bash脚本,分库,分表的备份

创建测试数据

#!/bin/bash

# MySQL连接信息
MYSQL_USER="root"
MYSQL_PASSWORD="chaoge666"
MYSQL_HOST="localhost"
MYSQL_PORT="3306"

# 配置循环参数
DATABASE_COUNT=5  # 要创建的数据库数量
TABLE_COUNT=3     # 每个数据库中要创建的表数量
ROW_COUNT=100     # 每个表中要插入的数据行数

# db_index自增1,1,2,3,4,5
for ((db_index = 1; db_index <= DATABASE_COUNT; db_index++)); do
    TEST_DATABASE="test_database_$db_index"
    # 创建数据库
    mysql -h $MYSQL_HOST -P $MYSQL_PORT -u $MYSQL_USER -p$MYSQL_PASSWORD <<EOF
CREATE DATABASE IF NOT EXISTS $TEST_DATABASE;
USE $TEST_DATABASE;
EOF

    for ((table_index = 1; table_index <= TABLE_COUNT; table_index++)); do
        TEST_TABLE="test_table_$table_index"
        # 创建表
        mysql -h $MYSQL_HOST -P $MYSQL_PORT -u $MYSQL_USER -p$MYSQL_PASSWORD $TEST_DATABASE <<EOF
CREATE TABLE IF NOT EXISTS $TEST_TABLE (
    id INT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(255) NOT NULL,
    age INT
);
EOF

        # 插入数据
        for ((row_index = 1; row_index <= ROW_COUNT; row_index++)); do
            random_name="Name_$(shuf -i 1-1000 -n 1)"
            random_age=$(shuf -i 18-60 -n 1)
            mysql -h $MYSQL_HOST -P $MYSQL_PORT -u $MYSQL_USER -p$MYSQL_PASSWORD $TEST_DATABASE <<EOF
INSERT INTO $TEST_TABLE (name, age) VALUES ('$random_name', $random_age);
EOF
        done
    done
done

# 检查操作是否成功
if [ $? -eq 0 ]; then
    echo "数据库、表和测试数据创建成功!"
else
    echo "创建过程中出现错误,请检查MySQL连接信息和权限。"
fi

$(shuf -i 1-1000 -n 1)

这部分使用了命令替换语法 $(...),其作用是执行括号内的命令,并将命令的输出结果替换到当前位置。具体执行的命令是 shuf -i 1-1000 -n 1,下面详细解释 shuf 命令及其参数:

  • shuf 命令shuf 是一个用于随机排列输入行的工具,通常用于生成随机数或打乱文件中的行顺序。
  • -i 1-1000 参数-i 选项用于指定一个整数范围,这里的 1-1000 表示生成的随机数范围是从 1 到 1000(包含 1 和 1000)。
  • -n 1 参数-n 选项用于指定输出的行数,这里的 1 表示只输出一个随机数。

综合起来,shuf -i 1-1000 -n 1 命令会从 1 到 1000 的整数范围内随机选取一个整数并输出。

for循环嵌套

在Bash脚本中,同样可以使用for循环嵌套来处理需要多层迭代的任务,以下是详细介绍。

基本语法及示例

Bash 中有多种方式来实现 for 循环,下面分别介绍不同形式下的 for 循环嵌套。

列表式 for 循环嵌套

列表式 for 循环会依次遍历给定列表中的每个元素。以下是一个简单的嵌套示例:

#!/bin/bash

# 外层循环,遍历数字 1 到 3
for i in 1 2 3; do
    # 内层循环,遍历字母 a 到 c
    for j in a b c; do
        echo "外层循环变量 i 的值为 $i,内层循环变量 j 的值为 $j"
    done
done

在这个例子中,外层循环会遍历数字 123,每次外层循环执行时,内层循环都会完整地遍历字母 abc。所以,总共会输出 9 行结果。

C 风格 for 循环嵌套

C 风格的 for 循环类似于 C 语言中的 for 循环,具有初始化、条件判断和迭代步骤。以下是一个 C 风格 for 循环嵌套的示例:

#!/bin/bash

# 外层 C 风格 for 循环,从 1 到 3
for ((i = 1; i <= 3; i++)); do
    # 内层 C 风格 for 循环,从 1 到 2
    for ((j = 1; j <= 2; j++)); do
        echo "外层循环变量 i 的值为 $i,内层循环变量 j 的值为 $j"
    done
done

在这个脚本中,外层循环从 1 递增到 3,每次外层循环执行时,内层循环从 1 递增到 2。因此,总共会输出 6 行结果。

九九乘法表

image-20250125181707881

1. 理解九九乘法表的结构

九九乘法表由 1 至 9 的两个数相乘组成。每行的规律是:

  • 第 1 行只有 1×1。
  • 第 2 行有 1×2 和 2×2。
  • 第 3 行有 1×3、2×3 和 3×3。
  • ...
  • 第 9 行有 1×9 至 9×9。

2. Bash 实现逻辑

  • 外层循环:控制行数。 for i in {1..9}表示从 1 到 9,每次取一个数字 i,代表当前行号。
  • 内层循环:控制每行的列数。 for j in $(seq 1 $i)表示从 1 遍历到当前行号 ij 是每一列的数。
  • 乘法计算:将当前行号 i 和列号 j 相乘,得到结果 i × j,并格式化输出。 使用 printf 格式化输出形如 1×1=1 的内容,且对齐显示。
  • 换行:内层循环结束后用 echo 打印换行符,保证每行内容独立。

3. 逐步拆解代码

for i in {1..9}; do          # 外层循环:从 1 到 9,每次代表当前行号 i
  for j in $(seq 1 $i); do   # 内层循环:从 1 遍历到 i,代表当前列号 j
    printf "%d×%d=%-2d  " $j $i $((i * j))  # 格式化输出 j×i=结果,%-2d 保持对齐
  done
  echo                       # 换行,进入下一行
done

4. 总结

通过外层循环控制行号,内层循环控制列号,每次格式化输出乘法表达式并自动换行,就完成了九九乘法表的打印。

应用场景

遍历二维数组

虽然 Bash 本身没有真正意义上的二维数组,但可以通过嵌套循环模拟二维数组的遍历。以下是一个示例:

#!/bin/bash

# 模拟一个 3 行 2 列的二维数组
array=(
    "1 2"
    "3 4"
    "5 6"
)

# 外层循环遍历行
for ((i = 0; i < ${#array[@]}; i++)); do
    # 分割当前行的元素
    IFS=' ' read -r -a row <<< "${array[$i]}"
    # 内层循环遍历列
    for ((j = 0; j < ${#row[@]}; j++)); do
        echo "第 $((i + 1)) 行,第 $((j + 1)) 列的元素为 ${row[$j]}"
    done
done

在这个脚本中,我们首先定义了一个一维数组 array 来模拟二维数组。然后使用外层循环遍历行,内层循环遍历列,从而实现对二维数组元素的访问。

注意事项

  • 循环控制变量的作用域:在嵌套循环中,要注意循环控制变量的作用域。不同层次的循环控制变量应该使用不同的名称,避免混淆。
  • 循环结束条件:确保内层和外层循环的结束条件设置正确,避免出现无限循环的情况。可以在编写代码时进行适当的测试和调试,以确保循环按预期结束。

mysql备份方案1,全库、全表备份

mysqldump -uroot -pchaoge666 -A -B --single-transaction > /opt/all-db.sql
一般备份时都会进行压缩处理,以节省磁盘空间,如下

mysqldump -uroot -pwww.yuchaoit.cn -A -B --single-transaction |  gzip >/opt/all-db.sql

还有一种备份方案,可以按不同的库,以及对于库中的表,进行备份,更为独立。

现有的数据库
[root@db-51 ~]#mysql -uroot -pwww.yuchaoit.cn 
Welcome to the MariaDB monitor.  Commands end with ; or \g.
Your MariaDB connection id is 9
Server version: 5.5.68-MariaDB MariaDB Server

Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

MariaDB [(none)]> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| test               |
| wordpress          |
+--------------------+
5 rows in set (0.00 sec)

库和表的结构是

如wordpress数据库的数据表如下

MariaDB [(none)]> use wordpress;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed
MariaDB [wordpress]> show tables;
+-----------------------+
| Tables_in_wordpress   |
+-----------------------+
| wp_commentmeta        |
| wp_comments           |
| wp_links              |
| wp_options            |
| wp_postmeta           |
| wp_posts              |
| wp_term_relationships |
| wp_term_taxonomy      |
| wp_termmeta           |
| wp_terms              |
| wp_usermeta           |
| wp_users              |
+-----------------------+
12 rows in set (0.00 sec)

需求

1. 将wordpress数据库单独备份到 /backup_mysql/wordpress/
2. 以及对应的数据表
/backup_mysql/wordpress/wp_posts.sql

思路

1. 查看库下的表
[root@db-51 ~]#mysql -uroot -pwww.yuchaoit.cn -e "show tables from wordpress;"

2. 单独导出库中的表数据
# mysqldump -uroot -pwww.yuchaoit.cn  库名  表名


mysqldump -uroot -pwww.yuchaoit.cn  wordpress wp_posts > /backup/wordpress/wp_posts.sql

脚本开发

指定备份某个库下,所有的表,存储为单个sql文件

#!/bin/bash
now=$(date +%F-%T)
table_list=$(mysql -uroot -pchaoge666 -e "show tables from test_database_1;" |grep -v 'Tables')

if [ ! -d /backup/test_database_1/ ];then
    mkdir -p /backup/test_database_1/
fi

for table in ${table_list}
do
    mysqldump -uroot -pchaoge666  test_database_1 ${table} > /backup/test_database_1/${table}.sql.${now}
done

复杂的分库分表脚本


脚本内容

#!/bin/bash

# MySQL 连接信息
HOST="localhost"
PORT="3306"
USER="root"
PASSWORD="chaoge666"

# 备份配置
BACKUP_DIR="/backup/"  # 替换为实际的备份路径
DATE=$(date +"%Y-%m-%d_%H:%M:%S")
LOG_FILE="${BACKUP_DIR}/backup_${DATE}.log"

# 创建备份目录和日志文件
mkdir -p "${BACKUP_DIR}/${DATE}"
touch "$LOG_FILE"

# 获取所有数据库列表(排除系统库)
DATABASES=$(mysql -h${HOST} -P${PORT} -u${USER} -p${PASSWORD} -e "SHOW DATABASES;" | grep -Ev "(Database|information_schema|performance_schema|mysql|sys)")

# 记录开始信息
echo "====== MySQL 分库分表备份开始: $(date) ======" | tee -a "$LOG_FILE"

# 遍历所有数据库
for DB in $DATABASES; do
  echo "正在备份数据库: $DB" | tee -a "$LOG_FILE"

  # 创建数据库备份目录
  # 循环,生成当前备份的库名的文件夹
  DB_BACKUP_DIR="${BACKUP_DIR}/${DATE}/${DB}"
  mkdir -p "$DB_BACKUP_DIR"

  # 获取当前数据库下的,所有表
  TABLES=$(mysql -h${HOST} -P${PORT} -u${USER} -p${PASSWORD} -e "SHOW TABLES FROM $DB;" | grep -v "Tables_in_")

  # 遍历数据库中的表
  for TABLE in $TABLES; do
    echo "正在备份表: $TABLE" | tee -a "$LOG_FILE"

    # 导出每个表到单独的 SQL 文件
    SQL_FILE="${DB_BACKUP_DIR}/${TABLE}.sql"
    mysqldump -h${HOST} -P${PORT} -u${USER} -p${PASSWORD} $DB $TABLE > "$SQL_FILE"

    if [[ $? -eq 0 ]]; then
      echo "表 $TABLE 备份成功: $SQL_FILE" | tee -a "$LOG_FILE"
    else
      echo "表 $TABLE 备份失败!" | tee -a "$LOG_FILE"
    fi
  done
done

# 压缩备份目录
echo "正在压缩备份文件..." | tee -a "$LOG_FILE"
tar -czf "${BACKUP_DIR}/backup_${DATE}.tar.gz" -C "${BACKUP_DIR}" "${DATE}" && rm -rf "${BACKUP_DIR}/${DATE}"

if [[ $? -eq 0 ]]; then
  echo "备份压缩成功: ${BACKUP_DIR}/backup_${DATE}.tar.gz" | tee -a "$LOG_FILE"
else
  echo "备份压缩失败!" | tee -a "$LOG_FILE"
fi

# 记录完成信息
echo "====== MySQL 分库分表备份完成: $(date) ======" | tee -a "$LOG_FILE"

新增功能说明

  1. 日志记录
    • 日志文件路径:${BACKUP_DIR}/backup_${DATE}.log
    • 记录备份的每一步,包括成功或失败的信息。
  2. 压缩备份文件
    • 将整个备份目录压缩为 .tar.gz 文件后删除原目录,节省空间。
    • 压缩文件路径:${BACKUP_DIR}/backup_${DATE}.tar.gz
  3. 错误处理
    • 通过 tee -a 将输出同时打印到控制台和日志文件,方便排查问题。

使用方法

  1. 赋予执行权限并运行

    chmod +x backup_mysql.sh
    ./backup_mysql.sh
    
  2. 查看日志

    • 如果备份过程中出现问题,可以查看日志文件:

      less /path/to/backup/backup_20250125_153000.log
      
  3. 检查压缩文件

    • 最终备份文件为压缩后的

      .tar.gz
      

      ,结构如下:

      /path/to/backup/
        ├── backup_20250125_153000.tar.gz   # 压缩的备份文件
        ├── backup_20250125_153000.log      # 日志文件
      

特性总结

  • 高效压缩:大幅减少存储占用,特别适合频繁备份场景。
  • 实时日志:清晰记录每个库和表的备份过程。
  • 清理原始备份文件:压缩后自动删除原目录,避免浪费空间。

for运维脚本

以下为你提供几个不同场景下使用for循环的运维脚本案例:

1. 批量重启服务

此脚本可在多台服务器上重启指定服务。

#!/bin/bash

# 定义服务器列表
servers=("server1.example.com" "server2.example.com" "server3.example.com")
# 定义要重启的服务
service_name="nginx"

for server in "${servers[@]}"; do
    echo "正在尝试在 $server 上重启 $service_name 服务..."
    # 通过ssh在远程服务器上重启服务
    ssh "$server" "sudo systemctl restart $service_name"
    if [ $? -eq 0 ]; then
        echo "$service_name 服务在 $server 上重启成功。"
    else
        echo "在 $server 上重启 $service_name 服务时出错。"
    fi
done

说明

  • 首先定义了服务器列表servers和要重启的服务名service_name
  • 借助for循环遍历服务器列表,使用ssh命令在每台服务器上执行服务重启操作。
  • 依据ssh命令的退出状态码来判断服务是否重启成功。

2. 批量删除过期文件

该脚本用于删除指定目录下超过一定天数的文件。

#!/bin/bash

# 定义要清理的目录
directories=("/var/log/old_logs" "/tmp/backup")
# 定义过期天数
days=7

for dir in "${directories[@]}"; do
    if [ -d "$dir" ]; then
        echo "正在清理 $dir 目录下超过 $days 天的文件..."
        # 删除指定目录下超过指定天数的文件
        find "$dir" -type f -mtime +$days -exec rm -f {} \;
        echo "$dir 目录清理完成。"
    else
        echo "$dir 不是有效的目录。"
    fi
done

说明

  • 定义了要清理的目录列表directories和过期天数days
  • 利用for循环遍历目录列表,对每个目录使用find命令查找并删除超过指定天数的文件。
  • 会先检查目录是否存在,若不存在则输出错误信息。

3. 批量创建用户组和用户

此脚本可以批量创建用户组和用户,并将用户添加到对应的用户组中。

#!/bin/bash

# 定义用户组和用户列表
groups=("group1" "group2" "group3")

for group in "${groups[@]}"; do
    # 创建用户组
    groupadd "$group"
    if [ $? -eq 0 ]; then
        echo "用户组 $group 创建成功。"
        for i in {1..3}; do
            username="${group}user$i"
            # 创建用户并将其添加到对应的用户组
            useradd -G "$group" "$username"
            if [ $? -eq 0 ]; then
                echo "用户 $username 创建成功并加入 $group 组。"
            else
                echo "创建用户 $username 时出错。"
            fi
        done
    else
        echo "创建用户组 $group 时出错。"
    fi
done

说明

  • 定义了用户组列表groups
  • 外层for循环用于创建用户组,内层for循环在每个用户组下创建3个用户,并将用户添加到对应的用户组中。
  • 根据命令的退出状态码输出相应的信息。
Copyright © www.yuchaoit.cn 2025 all right reserved,powered by Gitbook作者:于超 2025-01-25 20:04:39

results matching ""

    No results matching ""