Shell编程

Shell

一、变量

自定义变量

# 定义一个变量
name="Robber"
# 输出一个变量
echo $name
# 引用变量
aa=${name}1234
输出:Robber1234

错误规范

  1. 不能以数字开头

  2. 等号两边不能空格

  3. 变量的数值如果有空格,必须用引号包括

输入read

read -p是一个用于提示用户输入的命令。它的作用是显示一个提示信息,并等待用户输入。

-p选项用于指定要显示的提示信息。例如,可以使用以下命令提示用户输入他们的姓名:

read -p "请输入变量" temp
echo $temp

案例

ip=192.168.227.20
ping -c 1 &ip && ${ip}up || ping -c 1 "ip"down

三种引号

  • ”“:弱引用,可以在引号中引用变量
  • '':强引用,所有的数字都是字符串
  • 反引号:优先执行反引号里面的命令
schoo="Robber"
①schoo='$schoo 1234'
输出:$schoo 1234
②schoo="$schoo 1234"
输出:Robber 1234
③schoo=`date`
输出:2000年05月06日

整数运算

expr

语法

num2=1
num1=2
expr $num1 + $num2 

案例

read -p "请输入第一门成绩:" number1
read -p "请输入第二门成绩:" number2
echo -n "总成绩是:"
expr $number1 + $number2

&(())

echo $((1+2))
输出:3
echo $((num1+num2))
输出:3

&[]

num=$[1+2]
输出:3
num=$[num1+num2]
输出:3

let

let sum=2+3
let i++
let sum=$num1+$sum2

位置变量

  • $0:脚本名
  • $*:所有参数
  • $#:参数的个数
  • $?:上一个程序的返回值
  • $$:Shell进程的进程ID
  • $!:它代表在后台运行的最近一个进程的进程ID,如sleep 10 && echo $!echo $!将打印出sleep进程的ID。

test.sh

echo "用户第一个参数:$1"
echo "用户第二个参数:${2}"

执行

./tset.sh aaa bbb
用户第一个参数:aaa
用户第二个参数:bbb

二、条件控制

条件测试

数值比较

gt 大于
lt 小于
eq 等于
ne 不等于
ge 大于等于
le 小于等于

变量长度

num=12341234
echo {#num}
输出:8

测试语法格式

  1. test 条件表达式
  2. [ 条件表达式 ]
  3. [[ 条件表达式 ]]
例子:
aaa=1234 ; test ${#aaa} -ge 7 ; echo $?
aaa=1234 ; [ ${#aaa} -ge 7 ] ; echo $?
输出:0

if语句

read -p "输入您的名字:" name
if [ ${#name} -lt 7 ]
then
        echo "你太短啦";
else
        echo "你太长啦"
fi

#:计算name变量的长度

文件测试

  • -z:如果给定的字符串为空,则为真。
  • -d:如果给定的路径是一个存在的目录,则为真。
read -p "输入文件路径:" pathname
read -p -s "输入密码:" passwd
if [ -d $pathname ]
then 
	echo "存在此路径"
else
	echo "不存在"
fi

字符串对比

read -p "是否需要升级装备[yes/no]" option
if [ $option = "yes" ]
then
        echo "正在升级"
else
        echo "取消升级"
fi
read -p "请输入您的密码:" pwd
if  [[ $pwd =~ [a-z] ]] &&  [[ $pwd =~ [A-Z] ]]
then
        echo "密码设置成功"
else
        echo "密码设置错误"
fi

流程控制 if

多分支

if xxxx then
	echo
elif xxxx then
	echo
else
	echo
if

调试脚本

  • bash -n test1.sh:检查脚本的语法错误
  • bash -vx test1.sh:检查整个执行过程

case语句

read -p "输入你的名字" user_name
case $user_name in
"zhangsan")
        echo "张三你好"
;;
"xiaoli")
        echo "小李你好"
;;
*)
        echo "输入错误"
esac
#!/bin/bash

cat <<EOF
1.当前目录中的目录
2.查看磁盘分区情况
EOF
read -p "请输入:" option
case $option in
1)
        ls
;;
2)
        df -hT
;;
*)
        exit
;;
esac

三、循环

for

for ip in `cat test.txt`
do
        ping -c1 -w1 $ip &> /dev/null
        if [ $? -eq 0 ]; then
                ssh $ip "echo root | passwd --stdin root"
                if [ $? -eq 0 ]; then
                        echo  "$ip修改成功"
                else
                        echo "$ip修改失败"
                fi
        else
                echo $ip >> error.txt
        fi
done

while循环

while :
do
let i++
        if [ $i -eq 7 ]; then
                continue
        fi
echo $i
sleep 1

done

expect

案例:无需交互直接登录

#!/usr/bin/expect
# 执行指令
spawn ssh root@192.168.227.21
# 期望输出结果
 
# 结束
interact

案例:实现公钥发送并且免密登录功能

  • {2..30}:连续
>ip1234.txt
for i in {2..30}
do {
  ip=192.168.227.$i
  ping -c1 -W1 $ip &> /dev/null
  if [ $? -eq 0 ]; then
  
    /usr/bin/expect <<-EOF
      set timeout 10
      spawn ssh-copy-id $ip
      expect {
        "password:" { send "root\r"; exp_continue }
      }
      expect eof 
      
EOF
  fi
} &
done
wait
echo  "完成"

注意EOF前面不能有任何的空格

四、数组和函数

数组

  • {数组:初始索引:步长}
name=meili
echo ${name:2:2}
输出:il

关联性数组

  • 使用关联性数组之前必须要声明
declare -A pos
pos=([up]=xialou [center]=anqila [down]=make)
查看数值:echo ${pos[*]}
查看索引:echo ${!pos[*]}

统计男女性的实际应用

declare -A sexs
while read line
do
type=`echo $line | awk '{print $2}'`
let sexs[$type]++
done < sex.txt
# 打印
for num in ${!sexs[*]}
do
 echo "索引$num${sexs[$num]}"
done
索引f:1
索引m:2

普通数组

position=(asdf 123 41234 [20]=asdf)
test1=(`cat /etc/passwd`) #存放的是每行的数据
test2=(asdf asdf asdf "Tom")
test3=($red $green)
查看数值:echo ${pos[*]}
查看索引:echo ${!pos[*]}

案例:通过两种方式获取行内数据

  • for:会根据空格或者换行成为切片的依据
  • while:只会根据换行成为切片的依据
declare -A sexs
while read line
do
type=`echo $line | awk '{print $2}'`
let sexs[$type]++
done < sex.txt
# 打印
for num in ${!sexs[*]}
do
 echo "索引$num${sexs[$num]}"
done

函数

函数定义

方式一

function fun() {

}

方式二

fun() {

}

函数调用

fun

通过:set list检查文件中是否有隐藏的符号

函数传参

./test.sh 111 222 333
func() {
	local sdf
	echo $1
}
func $1
func $2
func $3
func $4

相当于形参隐藏了

local:可以用来定义局部变量

案例:通过传参的方式计算阶乘

func(){
sum=1
for((i=1;i<=$1;i++))
do
        sum=$[$sum*$i]
done
echo "asdfasdf:$sum"
}
func $1

数组传参

func(){
for i in $*
do
  newarr[j++]=$i
done
echo ${!newarr[*]}
}
arr=(tom cat jack)
func ${arr[*]}

shift移动参数

  • shift作用:每当读取一个参数都会丢弃一个,知道丢完则$#等于0
while [ $# -ne 0 ]
do
         let sum+=$1
shift
done
echo "asdf:$sum"

五、三剑客

正则表达式

  • ^:行首定位符号
# 只显示以root开头的
grep ^root /etc/passwd
  • $:行尾定位符号
# 只显示以root结果的
grep root$ /etc/passwd
  • .:匹配任意单个字符
[root@master-node1 ~]# cat test1.txt 
sdf
saf
sjhjf
[root@master-node1 ~]# grep s.f test1.txt 
sdf
saf
  • *:匹配前导0到多个字符
[root@master-node1 ~]# cat test1.txt 
a
ab
abc
abcd
[root@master-node1 ~]# grep ab* test1.txt 
a
ab
abc
abcd
  • .*:任意多个字符

  • []:匹配指定范围内的一个字符

[root@master-node1 ~]# cat test1.txt 
a
ab
abc
abcd
[root@master-node1 ~]# grep ab* test1.txt 
a
ab
abc
abcd
  • [-]:匹配指定范围内的一个字符,连续的范围
[root@master-node1 ~]# cat t1.txt 
Love 
love
1ove
l0ve
# 查看文件中存在0到9的字符
[root@master-node1 ~]# grep [0-9] t1.txt 
1ove
l0ve
  • [^]:匹配不在指定组内的字符
[root@master-node1 ~]# cat t1.txt 
Love 
love
1ove
l0ve
# 只不匹配行第一个数带有0到9的
[root@master-node1 ~]# grep [^0-9]ove t1.txt 
Love 
love
  • \:转义符
[root@master-node1 ~]# cat t1.txt 
Love 
love
1ove
l0ve
.ove
[root@master-node1 ~]# grep "\.ove" t1.txt 
.ove
  • \<:词首定位符
[root@master-node1 ~]# cat t1.txt 
I Love You
ILoveYou
[root@master-node1 ~]# grep "\<L" t1.txt 
I Love You
  • \>:词尾定位符

  • ():匹配稍后使用的字符的标签

# 将所在行前面添加注释
3,9 s/\(.*\)/#\1/
# 调换数据和注释的位置
3,9 s/(#)(.*)/\2\1/
  • +:匹配1~n个前导字符
  • * 表示匹配前面的元素零次或多次。换句话说,它允许前面的元素出现零次或多次,甚至可以完全没有该元素。例如,表达式 ab*c 可以匹配字符串 “ac”、“abc”、“abbc”、“abbbc” 等。
  • + 表示匹配前面的元素一次或多次。它要求前面的元素至少出现一次,而不允许完全没有该元素。例如,表达式 ab+c 可以匹配字符串 “abc”、“abbc”、“abbbc” 等,但无法匹配 “ac”。

sed

格式

  • sed 选项 命令 文件
  • sed 选项 -f 脚本 文件

命令

删除:d
  • -r 支持正则正则表达式
# 删除root所在行
sed -r '/root/d' passwd
# 删除头三行
sed -r '3d' passwd
sed -r '3{d}' passwd
sed -r '3{d;}' passwd
# 删除三到末尾行
sed -r '3,$d' passwd
# 删除所有行
sed -r '$d' passwd
替换:s
  • 格式:sed -r s/原数据/需要替换/g passwd
# 替换内容出现root的首行
sed -r 's/root/asdf/' passwd
# 替换开头root的行
sed -r 's/^root/asdf/' passwd
# 替换全局的root
sed -r 's/root/asdf/g' passwd
# 以末尾两位是数字的后面添加`.5`
sed -r 's/[0-9][0-9]$/&.5/‘ passwd
注意:`&`相当于`\1`,&是前面的数据在数据后面添加`.5`
# 将所有出现mail的地方替换成Email
sed -r 's/(mail)/E\1/g' passwd
# 将所有出现mail的地方替换成Email
sed -r 's#(mail)#E\1#g' passwd
替换整行:c
# 将来文件 开头是 SELINUX 开头的行替换
sed -ri '/^SELINUX=/c SELINUX=disable' /etc/selinux/config
# 将第二行换成333
sed -ri '2c3333' passwd
# 将第二到第三行换成54444
sed -ri '2,3c12341234' passwd

案例

删除配置文件中所有的#所在行

sed -ri '/^#/d' a.txt

给文件添加注释

# 把行首换成#
sed -r '2,6s/^/#' a.txt
sed -r '2,6s/(.*)/#\1/' a.txt
sed -r '2,6s/.*/#&/' a.txt

修改文件

sed -ri '/^SELINUX=/c SELINUX=DISABLED' a.txt

awk

语法

格式:awk [options] 'commands' filenames (推荐)

  • options:-F 定义输入字段分隔符。
  • command(时空)
    • BEGIN{}:begin发生在行处理前
    • {}:行处理,读一次执行一次
    • END{}:行处理后
[root@master-node1 ~]# cat asd.txt 
sdf:asdf:qwe:zxcv:zxcv
123:321:asd:fdsa:123
[root@master-node1 ~]# awk -F: '{print $1}' asd.txt
sdf
123
  • -F::用冒号作为分隔符

内部变量

  • FS:输入字段分隔符(默认空格)
awk 'BEGIN{FS=":"}{print $1,$3}' asd.txt
  • OFS:输出字段分隔符
[root@master-node1 ~]# awk 'BEGIN{FS=":"}{print $1,$3}' asd.txt
sdf qwe
123 asd
[root@master-node1 ~]# awk 'BEGIN{FS=":";OFS="+"}{print $1,$3}' asd.txt
sdf+qwe
123+asd

格式化输出

print函数
cat /etc/passwd | head -1 | awk -F: '{print "username is: "$1",uid is: "$3""}'
username is: root,uid is: 0
[root@master-node1 ~]# cat /etc/passwd | head -1 | awk -F: '{print "username and uid "$1,$3""}'
username and uid root 0

正则表达和动作

  • /xxx/:开启正则
字符串对比

原数据

[root@master-node1 ~]# cat passwd 
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
operator:x:11:0:operator:/root:/sbin/nologin
[root@master-node1 ~]# cat passwd | awk -F: '/^root/' passwd 
root:x:0:0:root:/root:/bin/bash
[root@master-node1 ~]# cat passwd | awk -F: '$0 ~ /^root/' passwd
root:x:0:0:root:/root:/bin/bash
[root@master-node1 ~]# cat passwd | awk -F: '$0 !~ /^root/' passwd
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
operator:x:11:0:operator:/root:/sbin/nologin
[root@master-node1 ~]# cat passwd  | awk -F: '$1 ~ /^root/' passwd 
root:x:0:0:root:/root:/bin/bash

注意:$0:匹配整行的数据,$1:匹配第一列数据

数值比较

[root@master-node1 ~]# cat passwd  | awk -F: '$3 == 0' passwd 
root:x:0:0:root:/root:/bin/bash
[root@master-node1 ~]# cat passwd  | awk -F: '$3 < 10' passwd 
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
[root@master-node1 ~]# cat passwd  | awk -F: '$7 == "/sbin/nologin"' passwd 
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
operator:x:11:0:operator:/root:/sbin/nologin

多条件

逻辑运算符
[root@master-node1 ~]# awk -F: '$1 ~ /root/ && $3<=15' passwd 
root:x:0:0:root:/root:/bin/bash
[root@master-node1 ~]# awk -F: '$1 ~ /root/ || $3<=15' passwd 
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
operator:x:11:0:operator:/root:/sbin/nologin
[root@master-node1 ~]# awk -F: '!($1 ~ /root/ && $3<=15)' passwd 
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
operator:x:11:0:operator:/root:/sbin/nologin
范围模式
[root@master-node1 ~]# awk -F: '$1~/root/,$1~/bin/' passwd 
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin

awk脚本编程

变量
自定义内部变量
  • -v:定义变量
awk -v user=root -F: '$1 == user' passwd
外部变量
awk -F: '$1 ~ "'"$hell"'"' passwd
条件&判断
awk -F: '{if($3==0){print $1 “是管理员”}else{print $1 "是普通用户"}}' passwd
判断

案例:统计普通用户、管理员、内置用户个数

  • 管理员:管理员ID为0
  • 内置用户:用户ID < 1000
  • 普通用户:用户ID >= 1000
[root@master-node1 ~]# awk -F: '{if($3==0){a++}else if($3<1000){b++}else{c++}}END{print "超级管理员" a;print " 内置用户" b;print "普通用户" c}' /etc/passwd
超级管理员1
内置用户18
普通用户1
循环

案例:每行打印5个

[root@master-node1 ~]# awk -F: 'BEGIN{for(i=1;i<=5;i++){print i}}'
1
2
3
4
5
数组

案例:将所有的用户放入数组中

[root@master-node1 ~]# awk -F: '{username[i++]=$1}END{for(i in username){print i, username[i]}}'  /etc/passwd | sort -n
0 root
1 bin
2 daemon
3 adm
4 lp
5 sync
6 shutdown
7 halt
8 mail
9 operator
10 games
11 ftp
12 nobody
案例

计算shell 的个数

[root@master-node1 ~]# awk -F: '{shells[$NF]++}END{for(i in shells){print i,shells[i]}}' /etc/passwd
/bin/sync 1
/bin/bash 2
/sbin/nologin 15
/sbin/halt 1
/sbin/shutdown 1

$NF:代表最后一列

六、拓展知识

案例:将所有的txt结尾的文件替换成doc

  • ${i%.*}:可以将后面的后缀以及点去掉
[root@master temp]# bash temp.sh txt doc
for i in `ls *.$1`
do
        mv $i ${i%.*}.doc
done
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值