shell编程基础知识及脚本示例



前言

shell脚本作为运维人员的基操,本博客从实战角度出发,以清晰的结构和通俗的示例,系统梳理 Shell 脚本的核心知识点。内容涵盖从基础变量定义到复杂流程控制,从参数解析到函数封装,最终通过日常脚本示例串联所有技能点。无论你是刚接触 Shell 的新手,还是希望查漏补缺的进阶者,都能在此找到可落地的代码片段和深入浅出的原理剖析。


一、shell变量

1.命名规则

	首个字符必须为字母(a-z,A-Z)。
	中间不能有空格,可以使用下划线(_)。
	不能使用标点符号。
	不能使用bash里的关键字(可用help命令查看保留关键字)

2.定义及使用变量

my_name="AAA"
echo $my_name
echo ${my_name}

#注意
ehco两句效果一样,均为输出变量的值。变量名外面的花括号是可选的,加花括号是为了帮助解释器识别变量的边界。

二、shell传递参数

注意:$10 不能获取第十个参数,获取第十个参数需要${10}。当n>=10时,需要使用${n}来获取参数

1.位置参数

代码如下(示例):
$0:表示脚本名称
$1: 脚本的第一个参数,以此类推

[root@ops ~]# cat i.sh 
#!/bin/bash
echo "The script name is $0"
echo "The number of arguments is $1"

[root@ops ~]# sh i.sh "位置参数"
The script name is i.sh
The number of arguments is 位置参数

2. 任意参数

代码如下(示例):

$*和$@的区别:
	相同点:
		都是引用所有参数。
	不同点:
		只有在双引号中体现出来。假设在脚本运行时写了三个参数 1、2、3,,则 “ * “ 等价于 “1 2 3”(传递了1个参数)
		而 “@” 等价于 “1” “2” “3”(传递了3个参数)

$*示例

[root@ops ~]# cat i.sh 
#!/bin/bash
for i in "$*"; do
  echo $i
done
[root@ops ~]# sh i.sh 1 2 3
1 2 3

$@示例

[root@ops ~]# cat i.sh 
#!/bin/bash
for i in "$@"; do
  echo $i
done
[root@ops ~]# sh i.sh 1 2 3
1
2
3

三、shell一维数组

此处以一维数组为例,只能使用整数作为数组索引

0.定义方式

方法一: 一次赋一个值
	数组名[下标]=变量值
	例如:
		#array[0]=pear
		#array[1]=apple
	查看数组:
		declare -a  | grep  array   
		echo ${array[@]}
方法二: 一次赋多值
	数组名=(值1  值2  值3)  
	查看:echo  ${array [@]}

1.定义并获取数组的单个元素

[root@ops ~]# cat i.sh 
#!/bin/bash
my_array=(A B "C" D)  #定义数组
echo "第一个元素为: ${my_array[0]}"
echo "第二个元素为: ${my_array[1]}"
echo "第三个元素为: ${my_array[2]}"
echo "第四个元素为: ${my_array[3]}"
[root@ops ~]# sh i.sh
第一个元素为: A
第二个元素为: B
第三个元素为: C
第四个元素为: D

2.定义并获取数组的所有元素

[root@ops ~]# cat i.sh 
#!/bin/bash
my_array=(A B "C" D)  #定义数组
echo "my_array元素是: ${my_array[*]}"
echo "my_array元素是: ${my_array[@]}"
[root@ops ~]# sh i.sh
my_array元素是: A B C D
my_array元素是: A B C D

3.定义并获取数组的元素个数

[root@ops ~]# cat i.sh 
#!/bin/bash
my_array=(A B "C" D)  #定义数组
echo "my_array元素个数: ${#my_array[@]}"
[root@ops ~]# sh i.sh
my_array元素个数: 4

4.定义并获取数组的元素索引

[root@ops ~]# cat i.sh 
#!/bin/bash
my_array=(A B "C" D)  #定义数组
echo "my_array元素个数: ${!my_array[@]}"
[root@ops ~]# sh i.sh
my_array元素索引: 0 1 2 3

四、shell条件判断语法

使用test

[root@ops ~]# cat i.sh 
#!/bin/bash
num1=10
num2=100
if test $[num1] -eq $[num2]
then
 echo '两个数相等!'
else
 echo '两个数不相等!'
fi

test 命令的作用

test 是 Shell 内置命令,用于条件判断。它直接评估表达式,返回退出状态码:
0 表示条件为真(True)。
1 表示条件为假(False)。

在示例中:
	test $[num1] -eq $[num2] 比较 num1 和 num2 是否相等。
	$[ ] 是旧式算术展开语法(现代 Shell 中建议用 $(( )) 替代),此处等效于 $((num1)) 和 $((num2))

使用[ ],它是 test 的另一种形式,本质是同一个命令

[root@ops ~]# cat i.sh 
#!/bin/bash
num1=10
num2=100
if [ $[num1] -eq $[num2] ]
then
 echo '两个数相等!'
else
 echo '两个数不相等!'
fi

使用[[ ]] 双重方括号,该条件下可以使用正则

[root@ops ~]# cat i.sh 
#!/bin/bash
num1=10
num2=100
if [[ $[num1] =~ $[num2] ]]
then
 echo '两个数相等!'
else
 echo '两个数不相等!'
fi

五、shell常用的数值比较命令

参数说明
-eq等于
-le小于等于
-ge大于等于
-lt小于
-gt大于
-ne不等于

六、shell常用的字符串比较命令

参数说明
=等于则为真
!=不相等则为真
-z 字符串字符串的长度为0则为真
-n 字符串字符串的长度不为0则为真

判断字符串是否相等示例

[root@ops ~]# cat i.sh 
#!/bin/bash
num1="xx"
num2="xxx"
if [[ $num1 = $num2 ]]
then
 echo '两个数相等!'
else
 echo '两个数不相等!'
fi
[root@ops ~]# sh i.sh 
两个数不相等!

七、shell常用的文件比较命令

在这里插入图片描述
判断文件是否存在示例

[root@ops ~]# cat i.sh 
#!/bin/bash
if test  -e ./bash
then
  echo '文件已存在!'
else
  echo '文件不存在'
fi
[root@ops ~]# sh i.sh 
文件不存在

八、shell处理参数的特殊字符

参数处理说明
$0脚本名称
$#传递到脚本中的参数个数
$*以单一字符串的形式给脚本传递参数
$@以多个字符串的形式给脚本传递参数
$$当前脚本运行的进程号
$!后台运行的最后一个进程的PID号
$-显示Shell使用的当前选项,与set命令功能相同
$?显示最后命令的退出状态。0表示没有错误,其他任何值表明有错误

九、流程控制

1.if else-if else语法格式

if condition1
then
  command1
elif condition2
then
  command2
else
  commandN
fi

2. while语法格式

while condition
do
  command
done

3. for语法格式

for var in item1 item2 ... itemN
do
   command1
done

4.until语法格式

until循环执行一系列命令直至条件为真时停止,与while循环刚好相反

until condition
do
  command
done

5.case

casein
  模式1)
   command1
   command2
   commandN
   ;;
  模式2)
   command1
   command2
   ;;
esac

6. 跳出循环

在循环过程中,有时候需要在未达到循环结束条件时强制跳出循环,Shell使用两个命令来实现该功能:break和continue

break

break命令允许跳出所有循环(终止执行后面的所有循环)

#!/bin/bash
while :
do
   echo -n "输入 1 到 5 之间的数字:"
   read aNum
   case $aNum in
     1|2|3|4|5) echo "你输入的数字为 $aNum!"
     ;;
     *) echo "你输入的数字不是 1 到 5 之间的! 游戏结束"
        break
     ;;
   esac
done

continue

#!/bin/bash
while :
do
   echo -n "输入 1 到 5 之间的数字:"
   read aNum
   case $aNum in
     1|2|3|4|5) echo "你输入的数字为 $aNum!"
     ;;
     *) echo "你输入的数字不是 1 到 5 之间的!"
        continue
        echo "游戏结束"
     ;;
   esac
done

十、shell函数

函数形式的shell编程示例

#!/bin/bash
services=(
aries:8199
nginx:80
)
stop_all_services () {
  for service in "${services[@]}"
  do
    # 获取服务名和端口号
    name=$(echo "$service" | cut -d":" -f1)
    port=$(echo "$service" | cut -d":" -f2)
    cd /export/servers/"$name"/bin
    sh stop.sh
    sleep 2  # 等待一段时间,确保服务已经完全停止
    # 检查服务是否成功停止
    if [ "$(curl -sIL -w '%{http_code}\n' http://xxx:$port/ -o /dev/null)" == "000" ]
    then
      echo "Service $name stopped successfully."
    else
      echo "Failed to stop service $name."
    fi
  done
}
stop_all_services

十一、日常脚本示例

生产环境centos服务器安全加固脚本

#!/bin/bash
#linux脆弱性加固脚本
system_auth(){
  echo "---口令锁定策略---"
  cp -p /etc/pam.d/system-auth /etc/pam.d/system-auth_bak
  if grep -q 'auth required pam_tally2.so' /etc/pam.d/system-auth;then
     sed -i 's/^auth.*required.*pam_tally2.so$/&\nauth required pam_tally2.so deny=5 unlock_time=300 even_deny_root root_unlock_time=10/g' /etc/pam.d/system-auth
  else
     echo "auth required pam_tally2.so deny=5 unlock_time=300 even_deny_root root_unlock_time=10" >> /etc/pam.d/system-auth
  fi  
  
  if grep -q 'account required pam_tally2.so' /etc/pam.d/system-auth;then
     sed -i 's/^account.*required.*pam_tally2.so$/&\n/g' /etc/pam.d/system-auth
  else
     echo "account required pam_tally2.so" >> /etc/pam.d/system-auth
 fi
}
logindefs(){
  echo "---口令生存期---"
  cp -p /etc/login.defs /etc/login.defs_bak
  sed -i 's/^PASS_MAX_DAYS.*/PASS_MAX_DAYS 90/g' /etc/login.defs
  sed -i 's/^PASS_MIN_DAYS.*/PASS_MIN_DAYS 10/g' /etc/login.defs
  sed -i 's/^PASS_WARN_AGE.*/PASS_WARN_AGE 7/g' /etc/login.defs
}
system_auth_crack(){
  echo "---口令复杂度---"
  # 判断 system-auth 配置文件中是否包含 password requisite pam_cracklib.so 的配置
  if grep -q 'password requisite pam_cracklib.so' /etc/pam.d/system-auth; then
  # 如果有,则使用 sed 命令替换原有的行
        sed -i 's/^password.*requisite.*pam_cracklib.so$/& try_first_pass retry=3 dcredit=-1 lcredit=-1 ucredit=-1 ocredit=-1 minlen=8/g' /etc/pam.d/system-auth
  else
    # 如果没有,则添加新的一行
       echo "password requisite  pam_cracklib.so try_first_pass retry=3 dcredit=-1 lcredit=-1 ucredit=-1 ocredit=-1 minlen=8" >> /etc/pam.d/system-auth
  fi
}
file_contro(){
  echo "文件与目录缺省权限控制"
  cp  -p /etc/profile /etc/profile.bak
  if grep -q 'umask 027' /etc/profile;then
    echo "已存在umask 027"
  else
    echo "umask 027" >>/etc/profile
  fi
}
user_control(){
   filename1="/etc/passwd"
   filename2="/etc/shadow"
   filename3="/etc/group"
   filename4="/etc/services"
   filename5="/etc/xinetd.conf"
   filename6="/etc/security"
   filename7="/var/log/messages"
  if [ -e "${filename1}" ]; then
    chmod 644 /etc/passwd
 else
    echo "$filename1 不存在"
 fi

  if [ -e "${filename2}" ];then
    chmod 400 /etc/shadow
  else
    echo "$filename2 不存在"
  fi

  if [ -e "${filename3}" ];then
    chmod 644 /etc/group
  else
    echo "$filename3 不存在"
  fi
   
  if [ -e "${filename4}" ];then
    chmod 644 /etc/services
  else
    echo "$filename4 不存在"
  fi

  if [ -e "${filename5}" ];then
    chmod 600 /etc/xinetd.conf
  else
    echo "$filename5 不存在"
  fi

  if [ -e "${filename6}" ];then
    chmod 600 /etc/security
  else
   echo "$filename6 不存在"
  fi
  
  if [ -e "${filename7}" ];then 
    chattr +a /var/log/messages
  else
    echo "$filename7 不存在"
  fi  
}
security_log(){
  filename="/var/adm/messages"
  if [ -e "${filename}" ]; then
    chmod 666 /var/adm/messages
    systemctl restart rsyslog
else
   touch ${filename}
   chmod 666 /var/adm/messages
   echo "*.err;kern.debug;daemon.notice /var/adm/messages" >> /etc/rsyslog.conf
   systemctl restart rsyslog
fi
}
login_timeout(){
  # 检查 /etc/profile 文件是否存在
if [ ! -f /etc/profile ]; then
  echo "/etc/profile 文件不存在"
  exit 1
fi

# 使用 sed 命令检查文件末尾是否已经存在所需的两行内容,不存在则添加
if ! grep -q '^TMOUT=' /etc/profile; then
  echo 'TMOUT=300' >> /etc/profile
else
  sed -i 's/^TMOUT=.*/TMOUT=300/' /etc/profile
fi

if ! grep -q '^export TMOUT' /etc/profile; then
  echo 'export TMOUT' >> /etc/profile
fi
}
history_set(){
 # 检查 /etc/profile 文件是否存在
if [ ! -f /etc/profile ]; then
  echo "/etc/profile 文件不存在"
  exit 1
fi

# 使用 sed 命令检查文件中是否已经存在所需的两行内容,不存在则添加上去
if ! grep -q '^HISTFILESIZE=' /etc/profile; then
  echo 'HISTFILESIZE=5' >> /etc/profile
else
  sed -i 's/^HISTFILESIZE=.*/HISTFILESIZE=5/' /etc/profile
fi

if ! grep -q '^HISTSIZE=' /etc/profile; then
  echo 'HISTSIZE=5' >> /etc/profile
else
  sed -i 's/^HISTSIZE=.*/HISTSIZE=5/' /etc/profile
fi

# 让配置生效
source /etc/profile
}
core_dump(){

# 检查 /etc/security/limits.conf 文件是否存在
if [ ! -f /etc/security/limits.conf ]; then
  echo "/etc/security/limits.conf 文件不存在"
  exit 1
fi

# 使用 sed 命令检查文件末尾是否已经存在所需的两行内容
if ! grep -q '^\*\s\+soft\s\+core\s\+0$' /etc/security/limits.conf; then
  echo '* soft core 0' >> /etc/security/limits.conf
else
  sed -i 's/^\(\*\s\+soft\s\+core\s\+\).*/\10/' /etc/security/limits.conf
fi

if ! grep -q '^\*\s\+hard\s\+core\s\+0$' /etc/security/limits.conf; then
  echo '* hard core 0' >> /etc/security/limits.conf
else
  sed -i 's/^\(\*\s\+hard\s\+core\s\+\).*/\10/' /etc/security/limits.conf
fi
 
# 检查 /etc/profile 文件是否存在
if [ ! -f /etc/profile ]; then
  echo "/etc/profile 文件不存在"
  exit 1
fi

# 使用 grep 命令检查文件中是否存在指定内容,同时注释掉对应的行
if grep -q 'ulimit\s\+-S\s\+-c\s\+0\s\+>\s\+\/dev\/null\s\+2>&1' /etc/profile; then
  sed -i 's/^ulimit\s\+-S\s\+-c\s\+0\s\+>\s\+\/dev\/null\s\+2>&1/#&/' /etc/profile
fi
}
system_auth
logindefs
system_auth_crack
file_contro
user_control
security_log
login_timeout
history_set
core_dump
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值