bash手册
(20231102更新)
资料来源
shell
基础知识
三种引号区分
在Shell脚本中,有三种常见的引号用于包围字符串,它们是单引号(' ')、双引号(" ")和反引号(` `),它们各自有不同的用途和作用范围。
- 单引号(' '):
- 单引号是最简单的引号形式,它会完全保留字符串中的原始内容,不对其中的特殊字符进行转义,也不进行变量替换。例如,'Hello World!' 中的内容将会作为字面字符串输出,不进行任何处理。
- 单引号的作用范围仅限于单引号内部,不会扩展成其他字符。
- 双引号(" "):
- 双引号可以对字符串中的特殊字符进行转义,例如使用双引号可以直接表示换行符(\n)、制表符(\t)等特殊字符。例如,"Hello\nWorld!" 中的内容会转义为两行,第一行是 "Hello",第二行是 "World!"。
- 双引号可以进行变量替换,即将变量的值嵌入到字符串中。例如,"My name is $name" 中的 $name 会被替换为变量 name 的值。
- 反引号(` `):
- 反引号用于执行命令替换,将反引号内的命令执行后的结果作为字符串返回。
- 例如,`ls` 将会执行 ls 命令,并将输出作为字符串返回。
- 注意,反引号已经不再推荐使用,一般使用更加现代的命令替换方式 $( ) 来替代。
单引号保留原样,双引号适合变量替换和处理特殊字符,反引号用于命令替换(推荐使用 $( ))。根据需要选择合适的引号来包围字符串。
配置文件的启动顺序
四个常见的配置文件,其启动顺序如下:
- /etc/profile:这是系统级的配置文件,对所有用户生效。当用户登录时,会首先读取并执行该文件中的配置,用于设置系统范围的环境变量和执行系统级的配置操作。
- /etc/bashrc:同样是系统级的配置文件,对所有用户生效。当用户打开一个新的终端窗口或者执行一个新的bash shell时,会读取并执行该文件中的配置,用于设置系统范围的bash shell的环境变量和执行系统级的bash shell配置。
- ~/.bash_profile:这是每个用户的个人配置文件,对特定用户生效。当用户登录时,会读取并执行该文件中的配置,用于设置用户级的环境变量和执行用户级的配置操作。该文件一般包含了一些用户级别的环境变量设置和个人化的操作。
- ~/.bashrc:同样是每个用户的个人配置文件,对特定用户生效。当用户打开一个新的终端窗口或者执行一个新的bash shell时,会读取并执行该文件中的配置,用于设置用户级的bash shell的环境变量和执行用户级的bash shell配置。该文件一般包含了一些用户级别的环境变量设置和个人化的操作。
启动顺序为:/etc/profile -> /etc/bashrc -> ~/.bash_profile -> ~/.bashrc。
当用户登录时,会执行系统级的配置文件(/etc/profile和/etc/bashrc),然后再执行用户级的配置文件(~/.bash_profile和~/.bashrc)。
read的交互
在Shell脚本中,"read"命令用于与用户进行交互,可以读取用户的输入并将其赋值给一个变量。以下是使用"read"命令进行交互的一般用法和常见选项:
一般用法:
read variable_name
上述命令将提示用户输入,并将用户的输入赋值给变量 variable_name。
常见选项:
- "-p":指定一个提示信息,用于提示用户输入。
read -p "Please enter your name: " name
上述命令将提示用户输入,并将用户输入的内容赋值给变量 name。
- "-s":使输入内容不回显到终端上,通常用于输入密码等敏感信息。
read -s -p "Please enter your password: " password
上述命令将提示用户输入,并将用户输入的内容赋值给变量 password,同时不回显输入内容。
- "-t":设置一个超时时间,若用户在指定时间内未输入,则"read"命令会自动退出。
read -t 5 -p "Please enter your name within 5 seconds: " name
上述命令将提示用户在5秒钟内输入姓名,如果超过5秒未输入,则"read"命令会自动退出。
- "-a":将用户的输入作为数组元素。
read -a fruits
上述命令会提示用户输入多个水果名称,然后将每个水果名称作为数组 fruits 的元素。
bash调试
在Bash脚本中,可以通过使用bash -x
命令来启动调试模式。该命令会逐行执行脚本,并将执行过程以及执行结果输出到终端上,方便定位和解决问题。
以下是使用bash -x
进行脚本调试的示例:
bash -x scriptname.sh
上述命令将以调试模式运行名为scriptname.sh
的脚本。
在调试模式下,Bash脚本会按照以下方式进行执行:
- 打印出每一行要执行的命令。
- 将变量的值打印出来(使用
${}
标记法)。 - 展示实际执行的命令行及其输出结果。
调试模式会将每一行执行后的结果输出,以及未定义的变量,语法错误等信息。通过观察输出结果,您可以判断脚本的执行流程和问题所在,进而进行调试和修正。
需要注意的是,调试模式会在终端上产生大量的输出信息,可能会导致输出过多以致看不清楚。因此,在调试模式下,您可以将输出重定向到一个文件中,以便于查看和分析。
bash -x scriptname.sh > debug.log
使用bash -x
命令进行脚本调试可以方便地跟踪脚本的执行过程,帮助定位问题并解决错误。
$0、$1、$#、$@、$* 、$?、$$
在Bash脚本中,以下是常见的特殊变量含义:
$0
:代表脚本本身的名称(即脚本的文件名)。$1
:代表第一个参数。$#
:代表传递给脚本的参数个数。$@
:代表所有传递给脚本的参数,每个参数都用双引号引起来,可以作为一个单独的参数进行引用。$*
:代表所有传递给脚本的参数,每个参数都用单个空格分隔开,作为一个连续的字符串进行引用。$?
:代表上一个命令(或脚本)的退出状态,如果命令执行成功则为0,否则为非零值。$$
:代表当前脚本执行时的进程ID。
下面是一个示例来说明这些特殊变量的含义和用法:
#!/bin/bash
echo "脚本名称为:$0"
echo "第一个参数为:$1"
echo "传递给脚本的参数个数为:$#"
echo "传递给脚本的参数列表为:$@"
echo "传递给脚本的参数列表为:$*"
echo "上一个命令的退出状态为:$?"
echo "当前脚本的进程ID为:$$"
如果保存为test.sh
并执行 bash test.sh arg1 arg2
,将会得到以下输出:
脚本名称为:test.sh
第一个参数为:arg1
传递给脚本的参数个数为:2
传递给脚本的参数列表为:arg1 arg2
传递给脚本的参数列表为:arg1 arg2
上一个命令的退出状态为:0
当前脚本的进程ID为:1234 (假设进程ID为1234)
通过使用这些特殊变量,可以方便地在脚本中获取脚本名、接收传递的参数、获取命令执行的结果等信息,进行进一步的处理和判断。
$(( ))、$[ ]、expr
在Bash脚本中,有多种方式可以进行变量运算:
$(( ))
:这种方式用于进行算术运算,支持基本的算术运算符(如加法、减法、乘法、除法、取余等)和比较运算符(如等于、不等于、大于、小于等)。示例如下:
num1=10
num2=5
result=$((num1 + num2))
echo "Result: $result" # Output: 15
$[ ]
:与$(( ))
类似,也用于进行算术运算,但在一些较旧的Bash版本中仍然被支持。示例如下:
num1=10
num2=5
result=$[num1 + num2]
echo "Result: $result" # Output: 15
expr
命令:这是一个用于进行基本算术运算和字符串处理的命令。它使用一种特定的语法,并将结果输出到标准输出。示例如下:
num1=10
num2=5
result=$(expr $num1 + $num2)
echo "Result: $result" # Output: 15
请注意,expr
命令在执行算术运算时,需要使用反斜杠\
对一些特殊字符进行转义,比如乘法运算符*
,如下示例:
num1=10
num2=5
result=$(expr $num1 \* $num2)
echo "Result: $result" # Output: 50
但是,对于整数运算,建议使用$(( ))
或$[ ]
,因为它们更简洁和易读,并且不需要对特殊字符进行转义。
变量的替换和删除
在Bash脚本中,可以使用变量的替换和删除操作来修改或删除变量的值:
- 前向删除:
${变量#模式}
或者${变量##模式}
。这种操作会从变量的开头匹配并删除最短或最长的指定模式。如果使用#
,则删除最短匹配模式;如果使用##
,则删除最长匹配模式。示例如下:
name="Hello World"
echo ${name#Hello } # Output: World (删除最短匹配模式)
echo ${name##Hello } # Output: World (删除最长匹配模式)
- 后向删除:
${变量%模式}
或者${变量%%模式}
。这种操作会从变量的末尾匹配并删除最短或最长的指定模式。如果使用%
,则删除最短匹配模式;如果使用%%
,则删除最长匹配模式。示例如下:
filename="test.tar.gz"
echo ${filename%.gz} # Output: test.tar (删除最短匹配模式)
echo ${filename%%.tar.gz} # Output: test (删除最长匹配模式)
- 变量替换:
${变量/模式/替换}
或者${变量//模式/替换}
。这种操作会从变量中找到并替换指定的模式。如果使用单斜杠/
,则只会替换第一个匹配;如果使用双斜杠//
,则会替换所有匹配。示例如下:
str="I love apples, apples are delicious."
echo ${str/apple/orange} # Output: I love oranges, apples are delicious. (替换第一个匹配)
echo ${str//apple/orange} # Output: I love oranges, oranges are delicious. (替换所有匹配)
- 变量默认值:
${变量:-默认值}
或者${变量:=默认值}
。这种操作在变量为空或未设置时,默认使用指定的默认值。如果使用:-
,则只有在变量为空或未设置时才使用默认值;如果使用:=
,则设置变量的同时,若变量为空或未设置,也使用默认值。示例如下:
value=""
echo ${value:-"Default Value"} # Output: Default Value (变量为空,使用默认值)
echo ${value:="Default Value"} # Output: Default Value (变量为空,设置默认值)
使用这些变量替换和删除的操作,我们可以轻松地修改和处理变量的值,以满足脚本的需求。
for循环打印一个序列
for (( i=10;i>=1;i-- ));do echo $i;done
脚本范例
文件中删除所有空行
#!/bin/bash
# 要处理的文件
file_path="path/to/file.txt"
# 判断文件是否存在
if [ -f "$file_path" ]; then
# 使用sed命令删除空行,并将结果保存到临时文件
sed '/^\s*$/d' "$file_path" > "$file_path.tmp"
# 将临时文件替换原始文件
mv "$file_path.tmp" "$file_path"
echo "空行已删除!"
else
echo "文件不存在!"
fi
备份一个目录中的所有文件到另一个目录
#!/bin/bash
# 源目录和目标目录
source_dir="/path/to/source_directory"
target_dir="/path/to/target_directory"
# 创建目标目录(如果不存在)
mkdir -p $target_dir
# 备份源目录中的所有文件
cp -r $source_dir/. $target_dir
echo "备份完成!"
查找某个字符串,并将包含该字符串的行输出到另一个文件中
#!/bin/bash
# 源文件和目标文件
source_file="/path/to/source_file.txt"
target_file="/path/to/target_file.txt"
# 要查找的字符串
search_string="search_string"
# 查找包含字符串的行并输出到目标文件
grep "$search_string" $source_file > $target_file
echo "查找完成!结果已保存到 $target_file"
监控一个指定的进程是否在运行,并在进程停止时发送邮件通知
#!/bin/bash
# 要监控的进程名
process_name="process_name"
# 发送邮件的配置
email_recipient="email@example.com"
email_subject="进程停止通知"
email_message="进程 $process_name 已停止运行!"
# 检查进程是否在运行
check_process() {
if pgrep -x "$process_name" > /dev/null; then
return 0
else
return 1
fi
}
# 发送邮件通知
send_email_notification() {
echo "$email_message" | mail -s "$email_subject" "$email_recipient"
echo "邮件通知已发送至 $email_recipient"
}
# 持续监控进程状态
while true; do
if check_process; then
sleep 10
else
send_email_notification
break
fi
done
统计history的top10
#!/bin/bash
# 统计history命令使用记录
output=$(history | awk '{print $2}' | sort | uniq -c | sort -nr)
# 找出前10个使用最频繁的命令
top_ten=$(echo "$output" | head -n 10)
# 输出结果
echo "前10个使用最频繁的命令:"
echo "$top_ten"
history | awk '{print $2}'
:运行history
命令并使用awk
仅获取每行的第二列,即命令本身。sort
:对命令进行排序,以便可以对其进行统计。uniq -c
:统计命令出现的次数并添加到每行前面。sort -nr
:按照出现次数进行逆序排序,使最常用的命令位于前面。head -n 10
:截取前10行,即前10个使用最频繁的命令。- 最后将结果输出。
备份并压缩/etc目录里面所有的内容,存放在/root/bak目录里面,备份完成的文件格式为:“yymmdd_etc”,脚本名称为backup.sh存放在/server/scripts目录下
#!/bin/bash
# 获取当前日期
current_date=$(date "+%y%m%d")
# 检查是否为每月第一天
if [ $(date "+%d") -eq 01 ]; then
# 备份并压缩/etc目录
backup_file="/root/bak/${current_date}_etc.tar.gz"
tar -czvf $backup_file /etc
# 打印备份完成信息
echo "备份完成,备份文件在: $backup_file"
else
# 打印非每月第一天的信息
echo "今天不是每月第一天,不进行备份"
fi
#!/bin/bash
:指定脚本使用Bash shell进行解析。current_date=$(date "+%y%m%d")
:使用date
命令获取当前日期,格式为yymmdd
。if [ $(date "+%d") -eq 01 ]; then
:使用date
命令获取当前日期的天数,当天数等于01时,表示为每月第一天。backup_file="/root/bak/${current_date}_etc.tar.gz"
:指定备份文件的路径和文件名格式。tar -czvf $backup_file /etc
:使用tar
命令备份并压缩/etc
目录,并将结果保存为指定的备份文件。echo "备份完成,备份文件在: $backup_file"
:打印备份完成的信息,包括备份文件的路径和名称。echo "今天不是每月第一天,不进行备份"
:打印非每月第一天的信息。
保存脚本为backup.sh
,将其放置在/server/scripts
目录下。然后,在终端中切换到脚本所在的目录,并使用以下命令为脚本添加执行权限:
chmod +x backup.sh
最后,通过以下命令执行脚本:
./backup.sh
脚本将检查当前日期是否为每月第一天,如果是,则执行备份并压缩/etc
目录;如果不是,则只打印相应的提示信息。成功备份后,将输出备份完成的信息。备份文件将保存在/root/bak
目录下,并以指定的文件名格式进行命名。
当内存/磁盘用量达到80%的时候触发邮件报警
#!/bin/bash
# 内存使用量阈值,单位为百分比
memory_threshold=80
# 磁盘使用量阈值,单位为百分比
disk_threshold=80
# 获取内存使用率(百分比)
memory_usage=$(free | awk '/Mem/{printf("%d"), $3/$2*100}')
# 获取磁盘使用率(百分比)
disk_usage=$(df -h | awk '/\/$/{print $5}' | sed 's/%//')
# 检查内存使用率是否超过阈值
if [ $memory_usage -ge $memory_threshold ]; then
# 发送邮件报警
echo "Warning: Memory usage exceeds ${memory_threshold}%!" | mail -s "Memory Usage Alert" your_email@example.com
fi
# 检查磁盘使用率是否超过阈值
if [ $disk_usage -ge $disk_threshold ]; then
# 发送邮件报警
echo "Warning: Disk usage exceeds ${disk_threshold}%!" | mail -s "Disk Usage Alert" your_email@example.com
fi
解释:
memory_threshold
:设置内存使用量的阈值,单位为百分比,在本例中为80%。disk_threshold
:设置磁盘使用量的阈值,单位为百分比,在本例中为80%。memory_usage=$(free | awk '/Mem/{printf("%d"), $3/$2*100}')
:使用free
命令获取内存使用情况,并通过awk
命令计算出内存使用率(使用的内存量除以总内存量再乘以100)。disk_usage=$(df -h | awk '/\/$/{print $5}' | sed 's/%//')
:使用df
命令获取磁盘使用情况,并通过awk
命令提取根目录的使用率(即/
的行),再通过sed
命令去除百分号。if [ $memory_usage -ge $memory_threshold ]; then
:检查内存使用率是否超过阈值。echo "Warning: Memory usage exceeds ${memory_threshold}%!" | mail -s "Memory Usage Alert" your_email@example.com
:如果内存使用率超过阈值,则发送邮件报警,内容包含警告信息,并使用mail
命令发送给指定的邮箱地址。if [ $disk_usage -ge $disk_threshold ]; then
:检查磁盘使用率是否超过阈值。echo "Warning: Disk usage exceeds ${disk_threshold}%!" | mail -s "Disk Usage Alert" your_email@example.com
:如果磁盘使用率超过阈值,则发送邮件报警,内容包含警告信息,并使用mail
命令发送给指定的邮箱地址。
将脚本保存为usage_alert.sh
,然后在终端上运行该脚本。如果内存或磁盘使用量超过设定的阈值,脚本将触发邮件报警并将相应的警告信息发送到指定的邮箱地址。
批量添加用户,提示输入前缀和用户个数,随机密码
#!/bin/bash
# 提示输入前缀和用户个数
read -p "请输入用户前缀:" prefix
read -p "请输入用户个数:" count
# 循环添加用户
for ((i=1; i<=count; i++)); do
# 生成随机密码
password=$(date +%s%N | sha256sum | head -c12)
# 添加用户
username="${prefix}${i}"
useradd -m -s /bin/bash $username
# 设置用户密码
echo $password | passwd --stdin $username
# 打印用户信息
echo "已添加用户:${username},密码:${password}"
done
以上脚本会提示用户输入前缀和需要创建的用户个数。然后,它会使用循环逐个添加用户,并为每个用户生成一个随机密码。最后,脚本会打印用户的用户名和对应的密码。
批量探测主机
#!/bin/bash
# 输入主机网段
read -p "请输入主机网段(例如:192.168.0.0/24): " subnet
# 遍历网段中的所有IP地址
for ip in $(nmap -sn $subnet | grep "Nmap scan report" | awk '{print $NF}'); do
# Ping测试
ping -c1 $ip >/dev/null 2>&1
if [ $? -eq 0 ]; then
echo "$ip is alive"
else
echo "$ip is unreachable"
fi
done
这个脚本使用了nmap
命令来扫描指定的主机网段,并遍历网段中的所有IP地址。然后,使用ping
命令来对每个IP地址进行Ping测试。如果Ping成功,则输出相应的IP地址为"alive",如果Ping失败,则输出相应的IP地址为"unreachable"。
分库分表备份
分库分表备份是一种数据库备份策略,用于处理大型数据库的备份需求。在分库分表架构下,数据被分散存储在多个数据库和表中,这样可以提高数据库的性能和扩展能力。然而,由于数据分布在多个地方,备份变得更加复杂。以下是一个简单的分库分表备份脚本示例:
#!/bin/bash
# 定义数据库列表
databases=("database1" "database2" "database3")
# 定义分表列表
tables=("table1" "table2" "table3")
# 定义备份目录
backup_dir="/path/to/backup"
# 获取当前日期和时间
current_date=$(date +%Y%m%d)
current_time=$(date +%H%M%S)
# 创建备份目录
mkdir -p $backup_dir/$current_date/$current_time
# 循环备份数据库
for db in "${databases[@]}"; do
# 循环备份分表
for table in "${tables[@]}"; do
# 构建备份文件路径
backup_file="$backup_dir/$current_date/$current_time/$db.$table.sql"
# 执行备份命令
mysqldump -u username -p password $db $table > $backup_file
# 检查备份是否成功
if [ $? -eq 0 ]; then
echo "备份 $db.$table 成功"
else
echo "备份 $db.$table 失败"
fi
done
done
这个脚本使用了mysqldump
命令来备份MySQL数据库的指定表。首先,定义了要备份的数据库列表和分表列表。然后,定义了备份目录,这是存储备份文件的位置。接下来,获取当前的日期和时间,并创建对应的备份目录。接下来,使用嵌套的循环来备份每个数据库的每个分表。备份文件的命名方式是数据库名.表名.sql
,备份文件路径是备份目录/日期/时间/数据库名.表名.sql
。最后,使用mysqldump
命令来备份指定的数据库表,并将备份结果保存到相应的备份文件中。备份完成后,检查备份命令的退出状态码是否为0,以判断备份是否成功。
主从状态监测
#!/bin/bash
# 主服务器
master_host="主服务器IP"
master_port="主服务器端口"
master_user="主服务器用户名"
master_password="主服务器密码"
# 从服务器列表
declare -A slave_servers=(
[slave1]="从服务器1IP"
[slave2]="从服务器2IP"
[slave3]="从服务器3IP"
)
# 遍历从服务器列表
for server in "${!slave_servers[@]}"; do
echo "检查从服务器 $server"
slave_host="${slave_servers[$server]}"
# 检查从服务器与主服务器的连接状态
slave_status=$(mysql -h $slave_host -u $master_user -p$master_password -e "SHOW SLAVE STATUS\G" 2>/dev/null)
if [ $? -ne 0 ]; then
echo "无法连接到从服务器 $slave_host"
continue
fi
# 检查主从复制状态是否正常
replication_status=$(echo "$slave_status" | grep "Slave_IO_Running:\|Slave_SQL_Running:" | awk '{print $2}')
if [[ "$replication_status" != "Yes" ]]; then
echo "主从复制状态异常 on $slave_host"
else
echo "主从复制状态正常 on $slave_host"
fi
echo
done
该脚本使用了mysql
命令在从服务器上执行SHOW SLAVE STATUS
命令,以获取主从复制的状态信息。脚本首先定义了主服务器和从服务器的相关参数,包括主服务器IP、端口、用户名和密码,以及从服务器列表。
然后,使用for
循环遍历从服务器列表,通过mysql
命令连接到每个从服务器,并执行SHOW SLAVE STATUS
命令获取复制状态信息。如果无法连接到从服务器,脚本输出连接错误信息并继续下一个服务器。
在连接成功后,脚本从SHOW SLAVE STATUS
的输出中提取Slave_IO_Running
和Slave_SQL_Running
字段的值,这两个字段分别表示主从复制的IO进程和SQL进程是否正常运行。如果其中任何一个字段的值不是"Yes",则表示主从复制状态异常,脚本输出相应的错误信息。否则,脚本输出主从复制状态正常的信息。
监控网站
#!/bin/bash
# 网站URL
website_url="http://www.example.com"
# 发送邮件的邮箱配置
from_email="example@gmail.com"
to_email="admin@example.com"
smtp_server="smtp.gmail.com"
smtp_port="587"
smtp_username="example@gmail.com"
smtp_password="password"
# 日志文件路径
log_file="/path/to/log/file.log"
# 检查网站状态函数
check_website() {
response=$(curl -Is "$website_url" | head -n 1 | awk '{print $2}')
if [ "$response" != "200" ]; then
send_alert_email
fi
}
# 发送邮件函数
send_alert_email() {
echo "Website is down at $(date)" >> "$log_file"
(echo "Subject: Website is down"; echo "Website $website_url is down at $(date)") | \
msmtp -t -a default -d -h "$smtp_server" -p "$smtp_port" -f "$from_email" -r "$from_email" \
-x -au "$smtp_username" -ap "$smtp_password" "$to_email" >> "$log_file"
}
# 检查网站状态并发送报警邮件
check_website
该脚本用于监控指定网站的状态,并在网站挂掉时发送邮件报警。脚本中的相关参数需要根据实际情况进行修改。
website_url
:需要监控的网站的URL。将此变量更改为实际的网站URL。from_email
:发送邮件的邮箱地址。将此变量更改为实际的发送邮箱地址。to_email
:接收报警邮件的邮箱地址。将此变量更改为实际的接收邮箱地址。smtp_server
和smtp_port
:SMTP服务器名称和端口号。将这两个变量更改为实际的SMTP服务器信息。smtp_username
和smtp_password
:SMTP服务器的用户名和密码。将这两个变量更改为实际的SMTP用户名和密码。log_file
:日志文件的路径。将此变量更改为实际的日志文件路径。
脚本通过使用curl
命令发送HTTP请求并检查响应状态码来检查网站的状态。如果响应状态码不是"200",即网站挂掉,脚本将调用send_alert_email
函数发送报警邮件。
send_alert_email
函数会将网站挂掉的时间记录到日志文件中,并使用msmtp
命令发送报警邮件。脚本通过echo
命令构造邮件的主题和内容,然后将邮件内容通过管道传递给msmtp
命令发送邮件。msmtp
命令通过提供SMTP服务器的相关参数和身份验证信息来发送邮件。
SSH
#bin/bash
Ssh -t -t test@127.0.0.1 # remotessh 进入远程系统
Source /etc/profile #刷新配置文件,可以跳过,这一步就是在远程系统上执行命令了
Echo “test”
Cp /etc/a /etc/b #这里就是你需要执行的命令,和正常的操作一样的。
Exit #如果你执行完了,就可以退出了,一定要退出。
Remotessh #这个命令是结束的意思,一定要有。
防火墙
iptables
#!/bin/bash
# 删除已有规则
iptables -F
# 设置链的默认策略
iptables -P INPUT DROP
iptables -P FORWARD DROP
iptables -P OUTPUT ACCEPT
# 阻止指定的IP地址(这里以192.168.1.100为例)
iptables -A INPUT -s 192.168.1.100 -j DROP
# 使用multiport将ssh,http,https的流量访问连接
iptables -A INPUT -p tcp -m multiport --dports 22,80,443 -j ACCEPT
iptables -A OUTPUT -p tcp -m multiport --sports 22,80,443 -j ACCEPT
# 允许本地发起的SSH请求
iptables -A OUTPUT -p tcp --dport 22 -m state --state NEW,ESTABLISHED -j ACCEPT
iptables -A INPUT -p tcp --sport 22 -m state --state ESTABLISHED -j ACCEPT
# 做好负载平衡传入的网络流量(这里以两台服务器进行负载均衡为例)
iptables -A PREROUTING -t nat -i eth0 -p tcp --dport 80 -m state --state NEW -m nth --counter 0 --every 2 --packet 0 -j DNAT --to-destination 192.168.1.10:80
iptables -A PREROUTING -t nat -i eth0 -p tcp --dport 80 -m state --state NEW -m nth --counter 1 --every 2 --packet 0 -j DNAT --to-destination 192.168.1.11:80
# 内部网络与外部网络的通信
iptables -A FORWARD -i eth0 -o eth1 -j ACCEPT
iptables -A FORWARD -i eth1 -o eth0 -j ACCEPT
# 出站的DNS连接(这里以Google Public DNS为例)
iptables -A OUTPUT -p udp --dport 53 -d 8.8.8.8 -j ACCEPT
iptables -A OUTPUT -p udp --dport 53 -d 8.8.4.4 -j ACCEPT
# 指定网络的mysql连接(这里以192.168.1.0/24为例)
iptables -A INPUT -p tcp -s 192.168.1.0/24 --dport 3306 -j ACCEPT
iptables -A OUTPUT -p tcp -d 192.168.1.0/24 --sport 3306 -m state --state ESTABLISHED -j ACCEPT
# 允许IMAP和IMAPS
iptables -A INPUT -p tcp --dport 143 -j ACCEPT
iptables -A OUTPUT -p tcp --sport 143 -j ACCEPT
iptables -A INPUT -p tcp --dport 993 -j ACCEPT
iptables -A OUTPUT -p tcp --sport 993 -j ACCEPT
# 防止DoS攻击(这里以synflood为例)
iptables -N synflood
iptables -A synflood -m limit --limit 1/s --limit-burst 3 -j RETURN
iptables -A synflood -j DROP
iptables -A INPUT -p tcp --syn -j synflood
# 422端口的流量全部转移到22端口
iptables -A PREROUTING -t nat -p tcp --dport 422 -j REDIRECT --to-port 22
解释
1. 第一行“#!/bin/bash”指定了这个脚本使用的解释器是Bash。
2. “iptables -F”命令会清空iptables中的所有规则,相当于删除已有规则。
3. “iptables -P INPUT DROP”命令设置INPUT链的默认策略为DROP,即拒绝所有传入的数据包;“iptables -P FORWARD DROP”命令设置FORWARD链的默认策略为DROP,即拒绝所有转发的数据包;“iptables -P OUTPUT ACCEPT”命令设置OUTPUT链的默认策略为ACCEPT,即允许所有传出的数据包。
4. “iptables -A INPUT -s 192.168.1.100 -j DROP”命令添加一条规则,将源IP地址为192.168.1.100的数据包丢弃。
5. “iptables -A INPUT -p tcp -m multiport --dports 22,80,443 -j ACCEPT”命令添加一条规则,允许TCP协议的ssh、http、https流量访问连接;“iptables -A OUTPUT -p tcp -m multiport --sports 22,80,443 -j ACCEPT”命令添加一条规则,允许TCP协议的ssh、http、https流量访问连接的回复数据包。
6. “iptables -A OUTPUT -p tcp --dport 22 -m state --state NEW,ESTABLISHED -j ACCEPT”命令添加一条规则,允许TCP协议的ssh连接的发起数据包;“iptables -A INPUT -p tcp --sport 22 -m state --state ESTABLISHED -j ACCEPT”命令添加一条规则,允许TCP协议的ssh连接的回复数据包。
7. “iptables -A PREROUTING -t nat -i eth0 -p tcp --dport 80 -m state --state NEW -m nth --counter 0 --every 2 --packet 0 -j DNAT --to-destination 192.168.1.10:80”和“iptables -A PREROUTING -t nat -i eth0 -p tcp --dport 80 -m state --state NEW -m nth --counter 1 --every 2 --packet 0 -j DNAT --to-destination 192.168.1.11:80”命令添加了两条规则,将传入的80端口的TCP流量进行负载均衡,分别转发到两台服务器上。
8. “iptables -A FORWARD -i eth0 -o eth1 -j ACCEPT”和“iptables -A FORWARD -i eth1 -o eth0 -j ACCEPT”命令添加了两条规则,允许eth0和eth1之间的数据包转发。
9. “iptables -A OUTPUT -p udp --dport 53 -d 8.8.8.8 -j ACCEPT”和“iptables -A OUTPUT -p udp --dport 53 -d 8.8.4.4 -j ACCEPT”命令添加了两条规则,允许向Google Public DNS发送DNS查询请求。
10. “iptables -A INPUT -p tcp -s 192.168.1.0/24 --dport 3306 -j ACCEPT”和“iptables -A OUTPUT -p tcp -d 192.168.1.0/24 --sport 3306 -m state --state ESTABLISHED -j ACCEPT”命令添加了两条规则,允许192.168.1.0/24网段的主机通过TCP协议访问本机的3306端口,并允许本机向192.168.1.0/24网段的主机发送TCP连接的回复数据包。
11. “iptables -A INPUT -p tcp --dport 143 -j ACCEPT”和“iptables -A OUTPUT -p tcp --sport 143 -j ACCEPT”命令添加了两条规则,允许IMAP协议的流量访问连接和回复连接。
12. “iptables -N synflood”命令添加了一个名为synflood的用户自定义链。
13. “iptables -A synflood -m limit --limit 1/s --limit-burst 3 -j RETURN”命令向synflood链中添加了一条规则,限制每秒最多处理一个syn数据包,每次处理不超过三个syn数据包。
14. “iptables -A synflood -j DROP”命令向synflood链中添加了一条规则,将超出限制的syn数据包丢弃。
15. “iptables -A INPUT -p tcp --syn -j synflood”命令向INPUT链中添加了一条规则,将所有的syn数据包传递到synflood链中进行处理。
16. “iptables -A PREROUTING -t nat -p tcp --dport 422 -j REDIRECT --to-port 22”命令添加了一条规则,将422端口的流量全部转发到22端口上。