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循环使用场景
- 需要反复、重复执行的任务
- 如创建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
}
}'
代码解释:
- 获取
PATH
变量的值:通过path_value="$PATH"
将系统的PATH
变量值赋给path_value
变量。 - 使用
awk
处理PATH
变量的值:-F ':'
:指定awk
的字段分隔符为冒号:
,因为PATH
变量中的路径是用冒号分隔的。{ for (i = 1; i <= NF; i++) { print "Path entry " i ": " $i } }
:这是awk
的动作部分,使用for
循环遍历每个字段(路径),NF
是awk
的内置变量,表示当前记录的字段数。$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
解释:
awk -F: '$3 >= 1000 {print $1}' /etc/passwd
:使用awk
命令从/etc/passwd
文件中提取 UID 大于等于 1000 的用户名称。for user in $users
:遍历用户列表。userdel -r "$user"
:使用userdel
命令删除用户及其主目录。$?
:表示上一个命令的退出状态码,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
解释:
awk -F: '$3 >= 1000 {print $1}' /etc/passwd
:提取 UID 大于等于 1000 的用户名称。while read user
:逐行读取用户列表。userdel -r "$user"
:删除用户及其主目录。
方法三:使用 xargs
命令
#!/bin/bash
# 获取所有普通用户列表并使用 xargs 删除用户
awk -F: '$3 >= 1000 {print $1}' /etc/passwd | xargs -I {} userdel -r {}
解释:
awk -F: '$3 >= 1000 {print $1}' /etc/passwd
:提取 UID 大于等于 1000 的用户名称。xargs -I {} userdel -r {}
:将用户名称作为参数传递给userdel
命令,-I {}
表示将{}
作为占位符替换为实际的用户名称。
注意事项
- 权限问题:执行这些脚本需要使用
root
权限,因为删除用户需要管理员权限。 - 数据丢失:
userdel -r
命令会删除用户的主目录及其所有内容,请确保在执行之前备份重要数据。 - 系统稳定性:删除所有普通用户可能会影响系统的正常运行,请谨慎操作。
三剑客处理普通用户删除
题目解析
删除系统中 UID 大于 1000 的普通用户涉及到修改 /etc/passwd
文件中的用户信息。以下是使用 sed
、awk
和 grep
实现的具体方法。
方法一:使用 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
表示删除匹配的行。
- 匹配 UID 大于 1000 的用户行(格式如
方法二:使用 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
:替换原始文件。
注意事项
- 备份文件:修改
/etc/passwd
前,务必备份。cp /etc/passwd /etc/passwd.bak
- 同步组文件:删除用户后,可能需要同步更新
/etc/group
文件。 - 谨慎操作:误操作可能导致系统无法正常登录或使用。
选择哪种方法?
根据实际需求,awk
和 grep
通常更直观,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
在这个例子中,外层循环会遍历数字 1
、2
、3
,每次外层循环执行时,内层循环都会完整地遍历字母 a
、b
、c
。所以,总共会输出 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 行结果。
九九乘法表
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 遍历到当前行号i
,j
是每一列的数。 - 乘法计算:将当前行号
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"
新增功能说明
- 日志记录:
- 日志文件路径:
${BACKUP_DIR}/backup_${DATE}.log
。 - 记录备份的每一步,包括成功或失败的信息。
- 日志文件路径:
- 压缩备份文件:
- 将整个备份目录压缩为
.tar.gz
文件后删除原目录,节省空间。 - 压缩文件路径:
${BACKUP_DIR}/backup_${DATE}.tar.gz
。
- 将整个备份目录压缩为
- 错误处理:
- 通过
tee -a
将输出同时打印到控制台和日志文件,方便排查问题。
- 通过
使用方法
赋予执行权限并运行
chmod +x backup_mysql.sh ./backup_mysql.sh
查看日志
如果备份过程中出现问题,可以查看日志文件:
less /path/to/backup/backup_20250125_153000.log
检查压缩文件
最终备份文件为压缩后的
.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个用户,并将用户添加到对应的用户组中。 - 根据命令的退出状态码输出相应的信息。