1. 什么是shell
1.1 命令分类
1.内置命令:程序代码内置到bash命令中,
查看命令帮助
help cd
man cd 就会看到所有内置命令的帮助文档。
2.普通命令:程序文件存放在磁盘上 /bin/ls
查看命令帮助
ls --help
man ls
1.2 判断内置命令方法:
[root@web01 ~]# type ls
ls is aliased to `ls --color=auto'
[root@web01 ~]# type cd
cd is a shell builtin
2. shell脚本
一个程序文件,放了大量的linux命令
好的shell脚本,不只是有命令,还有变量,逻辑判断if for case while
Shell的优势在于处理操作系统底层的业务,因为有大量的Linux系统命令
php python go
3. 如何写一个优美的Shell脚本
3.1 统一的脚本存放目录
mkdir -p /server/scripts
3.2 推荐使用vim命令写脚本,支持语法高亮
vim first.sh 规范写法以.sh结尾
3.3 脚本文件的第一行必须写上#!/bin/bash
#! 叫做“幻数” 告诉内核使用/bin/bash来解释我的脚本
# 除了第一行之外都表示后面跟的是注释
echo "hello" # 这里还是注释 echo 'world' 这个命令不生效
3.4 第2行~第n行 加上版权信息
3.5 变量区段
name=hefan 等号左右都不能有空格
3.6 命令语句区段
echo $name 使用变量使用$符号
3.7 执行Shell脚本
第一种方式:(推荐)
sh new.sh或bash new.sh sh是bash的软链接,sh和bash两种方式没有任何区别
第2种:(不推荐使用)
/server/scripts/new.sh 这种全路径执行需要文件有可执行权限,第一种方法则不需要
第3种:
source new.sh 或 . new.sh source和.的功能一模一样
sh 是创建一个新的进程读取脚本执行脚本,和当前的shell窗口进程不资源共享,
例如name变量不会被当前shell窗口继承或知道。
source或. 在当前shell窗口从脚本文件中读取命令执行
应用场景:定时任务的脚本中source /etc/profile
4. 变量区段
4.1 变量的分类
4.1.1.全局变量:在所有进程及其子进程生效,例如对脚本中生效,能在脚本中使用这个变量
export OLDBOY=1 设置环境变量,但是还只是当前Shell窗口生效
env 查看环境变量
永久生效 需要写入环境变量配置文件/etc/profile(常用)
常用变量配置文件
全局:
/etc/profile
/etc/profile.d
/etc/bashrc (推荐)
只对当前用户:
~/.bash_profile
~/.bashrc (推荐)
4.1.2.局部变量:只对当前进程生效,在脚本中不生效
变量名的规范:由字母、数字、下划线组成,必须以字母开头
oldboy
oldboy123
123old 不是变量
_abc 系统用的,我们不用
写法规范:驼峰语法:第一个单词的首字母小写,从第2个单词开始首字母大写
passWord
oldBoyEdu
oneTwoThreeFour
4.1.3.怎么区分全局变量和局部变量:就看有没有用export命令定义
有export就是全局变量,没有export局部变量
[root@web01 scripts]# tail -2 /etc/profile
export OLDBOY=1
oldgirl=1
[root@web01 scripts]# env|grep OLDBOY
OLDBOY=1
[root@web01 scripts]# env|grep oldgirl
4.1.4.特殊变量
位置变量:跟命令行文件名或者参数的所处位置有关
$0 代表执行的脚本名称
echo $0
[root@web01 scripts]# sh arg.sh
arg.sh
[root@web01 scripts]# sh /server/scripts/arg.sh
/server/scripts/arg.sh
$1 $2 $3 ... $n n为任意正整数,$# 表示你传入参数的个数
[root@web01 scripts]# cat arg.sh
#!/bin/bash
echo $0
echo "第一个变量:$1"
echo "第2个变量:$2"
echo "第3个变量:$3"
echo "参数总个数为:$#"
补充:变量之"金庸新著"
$10 和 ${10}
$weekday 和 ${week}day
为了避免歧义,变量左右都加上大括号。
$* 一般表示命令行所有参数,不加引号同$@;如果给$*加上双引号
例如: "$*",则表示将所有的参数视为单个字符串,相当于"$1$2$3"
$@ 一般表示命令行所有参数,不加引号同$*;如果给$@加上双引号
例如: "$@",则表示将所有参数视为不同的独立字符串,相当于"$1" "$2" "$3" "……",这是将参数传递给其他程序的最佳方式,因为他会保留所有内嵌在每个参数里的任何空白。
推荐使用"$@"
[root@web01 ~]# set -- "I am" handsome boy..
[root@web01 ~]# echo $1
I am
[root@web01 ~]# echo $2
handsome
[root@web01 ~]# echo $3
boy..
[root@web01 ~]# echo $*
I am handsome boy..
[root@web01 ~]# echo $@
I am handsome boy..
[root@web01 ~]# for i in $*;do echo $i;done
I
am
handsome
boy..
[root@web01 ~]# for i in $@;do echo $i;done
I
am
handsome
boy..
[root@web01 ~]# for i in "$*";do echo $i;done
I am handsome boy..
[root@web01 ~]# for i in "$@";do echo $i;done
I am
handsome
boy..
状态变量:
$?:判断上一条命令是否执行成功,$?为0表示上一条命令执行成功,$?非零表示上一条命令执行不成功
脚本结尾隐藏执行了exit(等效于exit 0)
因此可以自定义返回值 exit 1
4.2 变量的定义
直接赋值
name=zhangyao
dir=/server/scripts
命令行传参 传递参数 利用位置参数$0 $1 ...
交互式设置参数 read -s 用于密码输入,不明文显示密码 -t 设置超时时间 -p 接提示语句
[root@web01 scripts]# read
oldboy
[root@web01 scripts]# echo $REPLY
oldboy
[root@web01 scripts]# read name
oldboy
[root@web01 scripts]# echo $name
oldboy
[root@web01 scripts]# read passWord
123456
[root@web01 scripts]# echo $passWord
123456
[root@web01 scripts]# unset name # 删除变量
[root@web01 scripts]# echo $name
[root@web01 scripts]# read -p "请输入你的姓名:" name
请输入你的姓名:oldboy
[root@web01 scripts]# read -s -p "请输入你的密码:" passWord
请输入你的密码:[root@web01 scripts]#
[root@web01 scripts]# echo $passWord
12345678
[root@web01 scripts]# read -t 5 -p "请输入你的姓名:" name
请输入你的姓名:
[root@web01 scripts]# vim readname.sh
#!/bin/bash
read -p "请输入你的姓名:" name
read -s -p "请输入你的密码:" passWord
echo
echo "$name $passWord"
4.3 变量的字串
[root@web01 scripts]# name=oldboy
[root@web01 scripts]# echo ${name} # 打印变量
oldboy
[root@web01 scripts]# echo ${name}|wc -L
6
[root@web01 scripts]# echo ${#name} # 获取变量值的长度
6
[root@web01 scripts]# echo $#name # 注意避免歧义
0name
截取字符串
[root@web01 scripts]# echo ${name:1:1} # 变量:截取字符起始编号:截取长度
l
[root@web01 scripts]# echo ${name:1:3}
ldb
[root@web01 scripts]# echo ${name:4:1}
o
删除字符串
[root@web01 scripts]# OLDBOY=abcABC123ABCabc
[root@web01 scripts]# echo $OLDBOY
abcABC123ABCabc
[root@web01 scripts]# echo ${OLDBOY#a*C} # 从左开始匹配,一个#表示最短删除
123ABCabc
[root@web01 scripts]# echo ${OLDBOY##a*C} # 从左开始匹配,2个##表示最长删除
abc
[root@web01 scripts]# echo ${OLDBOY%a*c} # 从右开始匹配,一个%表示最短删除
abcABC123ABC
[root@web01 scripts]# echo ${OLDBOY%%a*c} # 从右开始匹配,2个%%表示最长删除
替换
[root@web01 scripts]# echo ${OLDBOY/abc/ABC}
ABCABC123ABCabc
[root@web01 scripts]# echo ${OLDBOY//abc/ABC}
ABCABC123ABCABC
变量字串的案例--批量改名
[root@web01 test]# touch stu_102999_{1..5}_finished.jpg
[root@web01 test]# ls
stu_102999_1_finished.jpg stu_102999_3_finished.jpg stu_102999_5_finished.jpg
stu_102999_2_finished.jpg stu_102999_4_finished.jpg
[root@web01 test]# mv stu_102999_1_finished.jpg stu_102999_1^Cpg
[root@web01 test]# f=stu_102999_1_finished.jpg
[root@web01 test]# echo ${f/_finished/}
stu_102999_1.jpg
方法1:
for f in `ls *.jpg`;do mv $f `echo ${f%_finished*}.jpg`;done
方法2:
for f in `ls *.jpg`;do mv $f `echo ${f/_finished/}`;done
特殊用法
[root@web01 test]# echo ${var-/server/}
/server/
[root@web01 test]# echo $var
[root@web01 test]# echo ${var=/server/}
/server/
[root@web01 test]# echo $var
/server/
字串和管道的性能对比
[root@web01 ~]# chars=fkfmkfdmgkl
[root@web01 ~]# time for i in $(seq 11111);do count=${#chars};done
real 0m0.126s
user 0m0.123s
sys 0m0.002s
[root@web01 ~]# time for i in $(seq 11111);do count=`echo ${chars}|wc -L`;done
real 0m23.109s
user 0m2.885s
sys 0m3.368s
4.4 变量的数值计算
4.4.1. (())
[root@web01 ~]# ((a=1+1))
[root@web01 ~]# echo $a
2
[root@web01 ~]# b=$((2+2))
[root@web01 ~]# echo $b
4
[root@web01 ~]# echo $((3+4))
7
[root@web01 ~]# i=1
[root@web01 ~]# echo $((i++))
1
[root@web01 ~]# echo $i
2
[root@web01 ~]# echo $((i++))
2
[root@web01 ~]# echo $i
3
[root@web01 ~]# i=1
[root@web01 ~]# echo $((++i))
2
[root@web01 ~]# echo $i
2
[root@web01 ~]# echo $((++i))
3
[root@web01 ~]# echo $i
3
4.4.2.expr
[root@web01 ~]# expr 2+2
2+2
[root@web01 ~]# expr 2 + 2 # 必须有空格
4
[root@web01 ~]# expr a + 1 # 常用的用途:判断其中参数是否为整数
expr: non-numeric argument
[root@web01 ~]# echo $?
2
[root@web01 ~]# expr 2 + 2
4
[root@web01 ~]# echo $? # 通过返回值判断
0
[root@web01 ~]# expr 2.1 + 2 # 不支持小数
expr: non-numeric argument
[root@web01 ~]# expr -2 + 2 # 支持负整数
0
使用场景:
[root@web01 scripts]# vim judge.sh
#!/bin/bash
num1=$1
num2=$2
[ $# -ne 2 ] && echo "Usage:sh $0 num1 num2" && exit 1
expr $num1 + 1 &>/dev/null
[ $? -ne 0 ] && echo "第一个参数不是整数。" && exit 2
expr $num2 + 1 &>/dev/null
[ $? -ne 0 ] && echo "第二个参数不是整数。" && exit 3
echo $(($num1+$num2))
4.4.3. bc 支持小数运算
[root@web01 scripts]# echo 1+2|bc
3
[root@web01 scripts]# echo 1+2.1|bc
3.1
[root@web01 scripts]# bc # 交互方式
bc 1.06.95
Copyright 1991-1994, 1997, 1998, 2000, 2004, 2006 Free Software Foundation, Inc.
This is free software with ABSOLUTELY NO WARRANTY.
For details type `warranty'.
1+2
3
[root@web01 scripts]# echo "scale=2;355/113"|bc # scale指定保留小数位数
3.14
[root@web01 scripts]# echo "scale=6;355/113"|bc
3.141592
[root@web01 scripts]# echo `seq -s '+' 10`=`seq -s '+' 10|bc`
1+2+3+4+5+6+7+8+9+10=55
4.4.4. awk计算数字
[root@zyops ~]# echo "7.7 3.8"|awk '{print ($1-$2)}'
3.9
[root@zyops ~]# echo "358 113"|awk '{print ($1-3)/$2}'
3.14159
[root@zyops ~]# echo "3 9"|awk '{print ($1+3)*$2}'
54
[root@backup scripts]# awk BEGIN'{print 1.2+3.3}'
4.5
5.命令语句区段
5.1 条件判断
5.1.1.语法:[ -f /etc/hosts ] 先敲一对[],然后回退一格,敲2个空格,再回退一格
5.1.2.文件判断
-f 判断文件存在并且为普通文件,则表达式为真
[root@web01 scripts]# [ -f /etc/hosts ] && echo "表达式成立" || echo "表达式不成立"
表达式成立
[root@web01 scripts]# [ -f /etc/host ] && echo "表达式成立" || echo "表达式不成立"
表达式不成立
-d 判断文件存在并且为目录,则表达式为真
[root@web01 scripts]# [ -d /etc/ ] && echo "表达式成立" || echo "表达式不成立"
表达式成立
[root@web01 scripts]# [ -d /etc1/ ] && echo "表达式成立" || echo "表达式不成立"
表达式不成立
5.1.3. 字符串判断
-z 判断字符串为0,则表达式为真 zero 语法要求:字符串左右必须有双引号
[root@web01 scripts]# var=123
[root@web01 scripts]# [ -z "$var" ] && echo "表达式成立" || echo "表达式不成立"
表达式不成立
[root@web01 scripts]# [ -z "$var1" ] && echo "表达式成立" || echo "表达式不成立"
表达式成立
-n 判断字符串非0,则表达式为真
[root@web01 scripts]# [ -n "$var" ] && echo "表达式成立" || echo "表达式不成立"
表达式成立
[root@web01 scripts]# [ -n "$var1" ] && echo "表达式成立" || echo "表达式不成立"
表达式不成立
"串1" = "串2" 相等成立,表达式为真
语法要求:等号两侧也要求有空格。
[root@web01 scripts]# [ "$var" = "123" ] && echo "表达式成立" || echo "表达式不成立"
表达式成立
"串1" != "串2" 不相等成立,表达式为真
[root@web01 scripts]# [ "$var" != "123" ] && echo "表达式成立" || echo "表达式不成立"
表达式不成立
5.1.4. 整数判断
-eq 等于 equal [ $? -eq 0 ]
-ne 不等于 not equal [ $? -ne 0 ]
-gt 大于 greater than
-ge 大于等于 greater equal
-lt 小于 less than
-le 小于等于 less equal
[root@web01 scripts]# [ "1" = "1" ] && echo "表达式成立" || echo "表达式不成立"
表达式成立
表达式成立
# 大于,小于都需要转译
[root@web01 scripts]# [ "1" > "1" ] && echo "表达式成立" || echo "表达式不成立"
表达式不成立
[root@web01 scripts]# [ "1" < "1" ] && echo "表达式成立" || echo "表达式不成立"
表达式不成立
5.1.5. 逻辑操作符
与(-a) 或(-o) 非(!)
[root@web01 scripts]# [ "$var" = "123" -a 1 -eq 1 ] && echo "表达式成立" || echo "表达式不成立"
表达式成立
[root@web01 scripts]# [ "$var" = "123" -a 1 -eq 0 ] && echo "表达式成立" || echo "表达式不成立"
表达式不成立
[root@web01 scripts]# [ "$var" = "123" -o 1 -eq 0 ] && echo "表达式成立" || echo "表达式不成立"
表达式成立
[root@web01 scripts]# [ "$var" = "456" -o 1 -eq 0 ] && echo "表达式成立" || echo "表达式不成立"
表达式不成立
[root@web01 scripts]# [ "$var" = "123" -a 1 -eq 0 ] && echo "表达式成立" || echo "表达式不成立"
表达式不成立
[root@web01 scripts]# [ "$var" = "123" -a ! 1 -eq 0 ] && echo "表达式成立" || echo "表达式不成立"
表达式成立
案例:开发3个shell脚本比较2个整数大小
1. 分别以定义变量,脚本传参以及read读入的方式写3个脚本。
2. 用条件表达式(禁止if语句)进行判断。
3. 将2个整数的比较结果输出到屏幕,出错需要提示。
6. 获取随机数的方法
6.1 系统变量$RANDOM
[root@web01 scripts]# echo $RANDOM
18832
[root@web01 scripts]# echo $RANDOM|md5sum
0828025a694bb190d15dc7880607e810 -
6.2 UUID
[root@web01 scripts]# uuidgen
049ce4d3-ba3e-495f-aae5-73904033d59c
7、shell脚本调试
windows 的换行符是
linux是
,转换命令:dos2unix hello.sh
sh -x 脚本
set -x 指定调试范围
set +x
[root@web01 scripts]# vim arg.sh
#!/bin/bash
echo $0
echo "第一个变量:$1"
set -x
echo "第2个变量:$2"
set +x
echo "第3个变量:$3"
echo "第4个变量:$4"
echo "第5个变量:$5"
echo "第10个变量:${10}"
echo "参数总个数为:$#"
echo $1 $2 $3
echo $*
echo $@
"arg.sh" 29L, 577C written
[root@web01 scripts]# sh arg.sh
arg.sh
第一个变量:
+ echo $'3472542542344270252345217230351207217357274232'
第2个变量:
+ set +x
第3个变量:
第4个变量:
第5个变量:
第10个变量:
参数总个数为:0
参考连接:
http://oldboy.blog.51cto.com/2561410/1867160