SHELL编程
1.shell简介
Shell就是一种命令解析器,解析用户的命令调用系统内核执行相关的命令:比如我们写一个mkdir—>经过shell解析成Linux能看懂的2进制–>linux执行.一个系统可以存在多个shell,可以通过cat /etc/shells命令查看系统中安装的shell,不同的shell可能支持的命令语法是不相同的.
Shell也是一门编程语言,即shell脚本,shell是解释执行的脚本语言,可直接调用linux命令。Shell脚本的本质,就是大量shell命令的组合,并写入一个文本文件中!
shell1 mkdir ---- > 10101
shell2 md----> 10101
2. 脚本执行
创建脚本
vi helloworld.sh
#脚本以#!/bin/bash开头(指定解析器)
#!/bin/bash
echo "hello world"
touch 1.txt 2.txt 3.txt
ls -l > 1.txt
执行脚本
第一种方式 分配执行权
chmod u+x helloworld.sh
./helloworld.sh
/mysh/helloworld.sh
第二种方式 可以没有执行权
bash helloworld.sh
sh hellowrold.sh
3.变量
3.1 系统变量
使用set可以查看所有系统变量
BASH=/bin/bash
HOME=/root
HOSTNAME=linux01
JAVA_HOME=/opt/apps/jdk1.8.0_191
PATH=/usr/local/sbin:/usr/local
3.2 自定义变量
变量定义
变量名=变量值 #注意不能有空格
删除变量
unset 变量名
声明常量 readonly变量,不能使用unset变量
name=zss
name=lss 改值
age=23
gender=M
readonly USERNAME=Ly 不可变的变量 常量
取值
$name
${name}
"$name"
'$name' ---> $name的字符串
注意字符串的拼接
echo $name lss
echo ${name} lss
echo "$name"aaa lssaaa ""号中取变量的值可以正常取出
echos '$name'aaa $nameaaa ''号中取不出来变量的值 就是$name字符串 单引号会将所有特殊字符脱意
删除只读变量 (了解)
yum -y install gdb
cat << EOF|gdb
attach $$
call unbind_variable("username")
detach
EOF
unset username
3.3 export和source
export username=lisi 将变量的范围作用在所有的子bash中
source 将子bash定义的变量 作用在当前bash
export 修饰的变量的作用域是 当前和所有的子进程
source 将子进程中的变量 拉取到当前
3.4 特殊变量
$1 $2 $n ${10} 接收单个参数
$* 接收所有参数
$@ 接收所有参数
$# 参数的个数
$? 接收上个命令执行的结果 0 执行成功 非0的是执行失败
接收单个参数 一直到$9都可以 10以上 ${10}
vi args01.sh
#!/bin/bash
echo "接收的所有参数$1 $2 $3"
touch $1 $2 $3
sh args01.sh 1.txt 2.txt 3.txt
接收所有参数
$* 接收所有参数
$@ 接收所有参数
$# 参数的个数
$? 接收上个命令执行的结果 0 执行成功 非0的是执行失败
vi args02.sh
#!/bin/bash
echo "接收参数的个数$*"
echo "接收参数的个数$#"
touch $*
#!/bin/bash
echo "接收参数的个数$@"
echo "接收参数的个数$#"
touch $@
sh args02.sh 1.java 2.java
3.5 read交互
#!/bin/bash
read -t 10 -p '请输入您的用户名:' username #username用来接收用户输入的用户名 -t 10代表10秒不输退出
read -t 10 -p '请输入您的密码:' password
echo "$username"
echo "$password"
3.6 算数运算
expr 1+1 # 错误 1 + 1 之间必须有空格
expr 1 + 1
expr 1 + 1 \* 2 由于*有特殊含义 需要转义
expr `expr 1 + 2` \* 3
$((1+1))
$(((1+2)*3))
$[1+1]
$[(1+2)*3]
${a} 取变量a的值
$() 取一个命令的结果
$(( )) 取算术运算表达式的运算结果 $[]相同
4.条件判断
4.1 &&和||
&&:用来执行条件成立后执行的命令
||:用来执行条件不成立后的执行命令
ping windows && echo yes || echo no windows能ping通 输出yes 不能ping通输出no
ls && echo yes || echo no ls执行成功 输出yes 执行不成功输出no
4.2 整数测试
注意 符号 两边要有空格
test 1 = 1 && echo yes || echo no 1 == 1
test 1 != 1 && echo yes || echo no 1 != 1
test 1 -eq 1 && echo yes || echo no 1 == 1
test 1 -ne 1 && echo yes || echo no 1 != 1
test 1 -gt 2 && echo yes || echo no 1 > 2
test 1 -lt 2 && echo yes || echo no 1 < 2
test 1 -ge 2 && echo yes || echo no 1 >= 2
test 1 -le 2 && echo yes || echo no 1 <= 2
a=1
b=2
test $a = $b && echo yes || echo no
test $a != $b && echo yes || echo no
test $a && echo yes || echo no a存在 输出 yes 不存在输出 no
test $a -a $a = 1 && echo yes || echo no yes
test $a -a $a = 2 && echo yes || echo no no
test $a -o $a = 2 && echo yes || echo no yes
test命令通常做判断, test 命令和 [ ] 等同
#前后必须有空格
[1 = 1] && echo yes || echo no 错误
[ 1 = 1 ] && echo yes || echo no 正确
4.3 字符串测试
test "abc" == "bcd" && echo yes || echo no 判断字符串相等 注意 == 两边要有空格
test "abc" != "bcd" && echo yes || echo no 判断字符串不相同
判断字符串不是null
test $abc && echo yes || echo no abc 存在 输出 yes 不存在输出 no
判断字符串是null
test -z $abc && echo yes || echo no abc不存在 输出yes 存在输出 no
[ $abc ] && echo yes || echo no 不是空 yes 是空 no
if(name !=null ){
}
4.4 文件测试
-d 判断是否是文件夹
-f 判断是否是文件
-L 判断是否是超链接 快捷方式
-e 判断是否存在
-r 判断是否有读权限
-w 判断是否有写权限
-x 判断是否是执行权
test -d 1.txt && echo yes || echo no
[ -d 1.txt ] && echo yes || echo no
[ -f 1.txt ] && echo yes || echo no
[ -e 1.txt ] && echo yes || echo no
[ -L /bin ] && echo yes || echo no
[ -r 1.txt -a -w 1.txt ] && echo yes || echo no
[ -x 1.txt ] && echo yes || echo no
4.5 if语句判断
单条件判断
if [ 条件 ]
then
执行
fi
互斥条件判断
if [ 条件 ]
then
程序
else
程序
fi
#!/bin/bash
read -p '请输入您的年龄:' AGE
if [ $AGE -ge 18 ]
then
echo "你成年了可以看片了"
else
echo "你还未成年 滚蛋"
fi
#判断传入的参数 $1是否存在 存在打印值 不存在 则输出不存在
#!/bin/bash
name=$1
if [ $name ]
then
echo "$name"
else
echo "name不存在"
fi
多条件判断
if [ 条件1 ]
then
执行
elif [ 条件2 ]
then
执行
...
else
执行
fi
#!/bin/bash
if [ $1 -ge 90 -a $1 -le 100 ]
then
echo "优秀"
elif [ $1 -ge 80 -a $1 -lt 90 ]
then
echo "良好"
elif [ $1 -ge 60 -a $1 -lt 80 ]
then
echo "及格"
elif [ $1 -ge 0 -a $1 -lt 60 ]
then
echo "潜力非常大"
else
echo "分数有误"
fi
4.6 case选择语句
#!/bin/bash
read -p '请输入一个数字(1-7):' NUM
case $NUM in
1 )
echo "星期一"
;;
2 )
echo "星期二"
;;
3 )
echo "星期三"
;;
4 )
echo "星期四"
;;
5 )
echo "星期五"
;;
6 )
echo "星期六"
;;
7 )
echo "星期日"
;;
* )
echo "你瞎啊"
;;
esac
5.循环
5.1 for循环
for (( 初始化表达式;布尔表达式;步进表达式 ))
do
程序
done
#!/bin/bash
for (( i=0 ;i<=100;i++ ))
do
echo "$i"
done
#!/bin/bash
sum=0
for(( i=0;i<=100;i++))
do
sum=$[$sum+$i]
done
echo $sum
for 变量 in 值1 值2 值3
do
程序
done
或者写成一行
for 变量 in 值1 值2 值3 ; do 程序 ; done
#!/bin/bash
for N in $*
do
echo $N
done
**for循环案例:**在每台机器的根目录下 创建一个当前日期的文件夹 如果文件夹已存在则删除 创建 如果不存在则创建
#!/bin/bash
for hostname in linux01 linux02 linux03 #linux0{1..3} `cat workers`
do
echo "连接$hostname"
ssh $hostname "if [ -e /`date +%Y-%m-%d` ]
then
echo "先删除再创建"
rm -rf /`date +%Y-%m-%d`
mkdir /`date +%Y-%m-%d`
else
echo "直接创建文件夹"
mkdir /`date +%Y-%m-%d`
fi; exit"
echo "退出$hostname"
done
vi workders
linux01
linux02
linux03
猜数字小游戏
$RANDOM生成随机数 0-32767
#!/bin/bash
g=$[ $RANDOM%100+1 ]
for((;;))
do
read -p '请输入您要猜的数字' NUM
if [ $NUM == $g ]
then
echo '您猜对了'
exit;
else
if [ $NUM -gt $g ]
then
echo '您猜大了'
else
echo '您猜小了'
fi
fi
done
5.2 while循环
while [ 条件判断式 ]
do
程序
...
done
#!/bin/bash
num=1
while [ $num -le $1 ]
do
echo "$num"
#num=$[$num+1]
let num++
sleep 1 #睡眠1秒
done
6.数组
定义数组
arr=(1 2 3 4 5 "abc");
为数组元素赋值
arr[0]=100
arr[1]=200
根据指定索引获取元素
${arr[索引]}
获取数组中所有元素
${arr[*]}
${arr[@]}
获取数组的长度
${#arr[*]}
${#arr[@]}
遍历数组
for n in ${arr[*]}
do
echo $n
done
写成一行 for n in ${arr[*]};do echo $n;done
将脚本的输入参数转成一个数组
#!/bin/bash
arr=($@)
for n in ${arr[*]}
do
echo $n
done
7.函数
7.1 系统函数
basename 命令会删掉所有的前缀包括最后一个(‘/’)字符,然后将字符串显示出来。
basename /mysh/for.sh ---> for.sh
basename -s .sh /mysh/for.sh ----->for 不显示后缀
basename /mysh ---> mysh
basename -a /aaa/b.txt /ccc/d.txt -----> b.txt d.txt
dirname 从给定的包含绝对路径的文件名中去除文件名(非目录的部分),然后返回剩下的路径(目录的部分)
dirname /mysh/a.txt ---->/mysh
dirname /mysh/1.txt /aaa/b.txt ----> /mysh /aaa
7.2 自定义函数
function 函数名(){
函数体
return 返回值
}
注意:
function可以省略不写
()可以省略不写 但是函数名和{}之间要有空格
return可以结束函数
return默认返回最后一个命令的状态,也可以给定参数值 范围在 0-255
如果没有return 默认返回最后一个指令的退出状态值 $?
vi fn1.sh
#!/bin/bash
function hello(){
echo "hello"
echo "world"
}
调用方式
在当前shell脚本中调用
hello
在外界的bash中调用
source fn1.sh
hello
function mymkdir(){
echo "正在创建文件夹$1"
mkdir $1;
}
调用
mymkdir aaa
function mymkdir(){
echo "正在创建文件夹$1"
return
echo "老子不创建了"
mkdir $1;
}
#方法遇到return就结束 后面不执行了
function mymkdir(){
echo "正在创建文件夹$1"
mkdir $1
#return 10
}
外界调用
source fn1.sh
mymkdir aaa
创建成功 $? 返回 0
mymkdir 不传参数 创建失败 返回非0
加了return 10之后 无论成功还是失败 都是10
请用户输入信息 如果不输入一直提示 输入后 打印用户输入的信息
#!/bin/bash
function userinput(){
input_var=
output_var=$1
while [ -z $input_var ]
do
read -p "$output_var:" IN
input_var=$IN
done
echo "$input_var"
}
userinput "请输入您的姓名"
8.定时任务
crontab -e
* * * * *
分 时 日 月 周
分:每小时的第几分
时:每天的第几时 0-24
日:每月的几号
月:每年的哪月
周:每周的第几天
* * * * * 代表每分钟
1 * * * * 每小时的01分执行
1 14 * * * 每天的14点01分执行
1 14 1 * * 每月1号的14点01分执行
1 14 2 7 * 每年7月2号的14点01分执行
1 14 * 7 2 每年7月的每周的第2天 14点01分执行
每周二至周五,下午6点 的计划任务
0 18 * * 2-5 (2,3,4,5)
1到10月份,每周二周五,下午6点的计划任务
0 18 * 1-10 2,5
12点和14点,检查apache服务是否启动 每年的1-3月7-9月 周一到周5
0 12,14 * 1-3,7-9 1-5
cat /var/spool/cron/root 查看错误信息
9.高级文本编辑工具
9.1 cut
cut -d 指定切割方式 -f 第几列 文件名
vi cut.txt
001 liuyan 38 nv
002 tangyan 18 nv
003 jinlian 138 nv
004 dalang 8 nan
cut -d " " -f 1 cut.txt 按照" "来切 显示第一列
001
002
003
004
cut -d " " -f 2,3 cut.txt 按照" "来切 显示2 3列
liuyan 38
tangyan 18
jinlian 138
dalang 8
#获取到柳岩的id
cat cut.txt | grep liuyan
001 liuyan 38 nv
cat cut.txt | grep liuyan | cut -d " " -f 1
001
echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/opt/apps/jdk1.8.0_191/bin:/root/bin
选取系统PATH变量值,第2个“:”开始后的所有路径:
echo $PATH | cut -d ":" -f 2-
ifconfig ens33 | grep "inet "| cut -d "i" -f 2-|cut -d " " -f2
9.2 sed
sed是一种流编辑器,它一次处理一行内容。处理时,把当前处理的行存储在临时缓冲区中,称为“模式空间”,接着用sed命令处理缓冲区中的内容,处理完成后,把缓冲区的内容送往屏幕。接着处理下一行,这样不断重复,直到文件末尾。文件内容并没有改变,除非你使用重定向存储输出
sed [选项参数] ‘command’ filename
vi sed.txt
001 liuyan 38 nv
002 tangyan 18 nv
003 jinlian 138 nv
004 dalang 8 nan
#显示的时候在第二行后插入 003 ximenqing 100 nan a 新增,a的后面可以接字串,在下一行出现
sed '2a 003 ximenqing 100 nan' sed.txt
#删除包含001的记录 d 删除
sed '/001/d' sed.txt
查找替换 sed 's/要被取代的字串/新的字串/g' 文件名
#将所有00 替换成 ""
sed 's/nv/女/g' sed.txt
sed 's/0//g' sed.txt
# 删除第二行 并将所有00 替换成"" -e同时执行多个指令
sed -e '2d' -e 's/0//g' sed.txt
#显示时去除空行
sed '/^$/d' sed.txt
#修改文件 -i 删除文件中所有空行
sed -i '/^$/d' sed.txt
#给指定1-3行范围添加注释
sed -i '1,3s/^/#/g' sed.txt
#将所有的注释删除
sed -i 's/^#//g' sed.txt
#在001开头这一行下 加入 002
sed -i '/^001/a\002' sed.txt
#在001开头这一行 上面 加入 0000
sed -i '/^001/i\0000' sed.txt
每一行之间都必须要以反斜杠 \ 来进行新行标记
a :新增, a 的后面可以接字串,而这些字串会在新的一行出现(目前的下一行)
i :插入, i 的后面可以接字串,而这些字串会在新的一行出现(目前的上一行)
9.3 awk
一个强大的文本分析工具,把文件逐行的读入,以空格为默认分隔符将每行切片,切开的部分再进行分析处理。
awk [选项参数] ‘pattern1{action1} pattern2{action2}...’ filename
pattern:表示AWK在数据中查找的内容,就是匹配模式
action:在找到匹配内容时所执行的一系列命令
-F 指定输入文件折分隔符
-v 赋值一个用户定义变量
vi awk.txt
001 liuyan 38 nv
002 tangyan 18 nv
003 jinlian 138 nv
004 dalang 8 nan
#以空格进行切割 查找以001开头的所有行 打印第2列
awk -F " " '/^001/{print $2}' awk.txt
#以空格进行切割 查找以001开头的所有行 打印第2列 第3列 用,分隔
awk -F " " '/^001/{print $2 "," $3}' awk.txt
#在查找的内容之前打印 hello 在结尾打印 world
awk -F " " 'BEGIN{print "hello"}/^001/{print $2 "," $3}END{print "world"}' awk.txt
#给所有用户的年龄+10在输出
awk -F " " -v i=10 '{print $3+i}' awk.txt
内置变量
FILENAME 文件名
NR 已读的记录数 第几行
NF 浏览记录的域的个数(切割后,列的个数) 切割的列数
awk -F " " '{print FILENAME,NR,NF}' awk.txt
#打印所有空行号
awk '/^$/{print NR}' awk.txt
ip
ifconfig ens33 | grep "inet " |awk -F " " '{print "ip地址是:"$2}'
9.4 sort
sort命令是在Linux里非常有用,它将文件进行排序,并将排序结果标准输出。
sort 参数 文件
-n 以数值的方式排序 不指定以字符串形式排序 1 11 111 2 22
-r 逆序
-t 指定分隔符
-k 指定列
vi sort.txt
001 liuyan 38 nv
002 tangyan 18 nv
003 jinlian 138 nv
004 dalang 8 nan
sort -t " " -nk 3 sort.txt
sort -t " " -nrk 3 sort.txt
sort -t " " -nk 3 sort.txt >> 1.txt