Shell
一、变量
自定义变量
# 定义一个变量
name="Robber"
# 输出一个变量
echo $name
# 引用变量
aa=${name}1234
输出:Robber1234
错误规范
-
不能以
数字
开头 -
等号两边不能
空格
-
变量的数值如果有
空格
,必须用引号
包括
输入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
测试语法格式
test 条件表达式
[ 条件表达式 ]
[[ 条件表达式 ]]
例子:
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