(20231017更新)
资料来源
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 (变量为空,设置默认值)
使用这些变量替换和删除的操作,我们可以轻松地修改和处理变量的值,以满足脚本的需求。
脚本范例
文件中删除所有空行
#!/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 protected]"
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
目录下,并以指定的文件名格式进行命名。
SSH
#bin/bash
Ssh -t -t [email protected] # 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端口上。
标签:脚本,iptables,命令,--,ACCEPT,echo,备忘录,bash
From: https://www.cnblogs.com/mugetsukun/p/17310597.html