shell基础
检查脚本语法错误
bash -n script_name.sh
调试脚本:
bash -x script_name.sh
set -e 或 set +e
-e表示从当前位置开始,如果出现任何错误都将触发exit,退出脚本
+e表示不管出现任何错误都将继续执行脚本
shift 对参数的左移(参数下标增大,右移),参数个数不确定情况下遍历参数进行处理
注释:
单行注释:#
多行注释:
:<<! 被注释的内容,可换行 !
:' 被注释的内容,可换行 '
双引号和单引号区别:
" "输出含变量的值先解析变量的值和命令,保留变量原格式
' '原样输出单引号的内容
` `反引号:可将内容解析为系统命令
exit 1 #退出当前脚本返回一个状态值
nohup 后台执行,通常和&连用
nohup script_name.sh >/dev/null 2>1& & #后台执行脚本,并将错误输出和正确输出定向到/dev/null中(/dev/null表示空值,表示丢弃一切写入其中的数据)
POSIX字符类
[[:upper:]] 表示大写字母[A-Z]
[[:lower:]] 表示小写字母[a-z]
[[:digit:]] 表示阿拉伯数字[0-9]
[[:alnum:]] 大小写字母和数字[0-9a-zA-Z]
[[:space:]] 表示空格或Tab键
[[:alpha:]] 大小写字母[a-zA-Z]
[[:cntrl:]] 表示ctrl键
[[:graph:]]或[:print:] ASCII中33-126之间的字符
[[:xdigit:]] 16进制数字
[[:punct:]] 匹配标点符号
[[:blank:]] 匹配空格或制表符
变量
创建普通变量: name="test" #"="两边不能有空格
创建局部变量: local name="test" #关键字local
引用变量: $name 或 ${name}
变量重新赋值: name="new"
只读变量:
name="test"
readonly name #关键字readonly
删除变量: unset name #不能删除只读变量
字符串变量
单引号:
var='test' #只能原样输出,变量无效,无法转义
双引号:
var="this is ${name}" #变量有效,可以使用转义
参数传递
获取参数值:
$0 代表执行的文件
$1 代表传入的第一个参数
$# 参数总个数
$* 所有向脚本传递的参数,一个整字符串,"$1 $2 $3..."
$@ 同$*,使用引号将每个参数分开输出,"$1" "$2" "$3"
$$ 返回脚本运行的当前进程号
$! 后台运行的最后一个进程ID
$? 显示最后命令的执行状态,0表示无错误,其它表示错误
$- 显示上一个脚本最后一个传入的参数
算数运算
+ - * \ #乘号必须进行转义
加法运算
val=`expr 2 + 1` #数字之间必须使用空格隔开
val=$[2+3] #空格可省略
val=$((2+4)) #空格可省略
关系运算符
-eq: 检测两个数是否相等,相等返回 true。 [ $a -eq $b ] 返回 false。
-ne: 检测两个数是否不相等,不相等返回 true。 [ $a -ne $b ] 返回 true。
-gt: 检测左边的数是否大于右边的,如果是,则返回 true。 [ $a -gt $b ] 返回 false。
-lt: 检测左边的数是否小于右边的,如果是,则返回 true。 [ $a -lt $b ] 返回 true。
-ge: 检测左边的数是否大于等于右边的,如果是,则返回 true。 [ $a -ge $b ] 返回 false。
-le: 检测左边的数是否小于等于右边的,如果是,则返回 true。 [ $a -le $b ] 返回 true。
字符串运算符
=: 检测两个字符串是否相等,相等返回 true。 [ $a = $b ] 返回 false。
!=:检测两个字符串是否相等,不相等返回 true。 [ $a != $b ] 返回 true。
-z:检测字符串长度是否为0,为0返回 true。 [ -z $a ] 返回 false。
-n:检测字符串长度是否为0,不为0返回 true。 [ -n "$a" ] 返回 true。
$: 检测字符串是否为空,不为空返回 true。 [ $a ] 返回 true。
布尔运算符
! :非运算,表达式为 true 则返回 false,否则返回 true。 [ ! false ] 返回 true。
-o:或运算,有一个表达式为 true 则返回 true。 [ $a -lt 20 -o $b -gt 100 ] 返回 true。
-a:与运算,两个表达式都为 true 才返回 true。 [ $a -lt 20 -a $b -gt 100 ] 返回 false。
逻辑运算符
&&:逻辑的 AND [[ $a -lt 100 && $b -gt 100 ]] 返回 false
||:逻辑的 OR [[ $a -lt 100 || $b -gt 100 ]] 返回 true
文件运算符
-b file :检测文件是否是块设备文件,如果是,则返回 true。 [ -b $file ] 返回 false。
-c file :检测文件是否是字符设备文件,如果是,则返回 true。 [ -c $file ] 返回 false。
-d file :检测文件是否是目录,如果是,则返回 true。 [ -d $file ] 返回 false。
-f file :检测文件是否是普通文件(既不是目录,也不是设备文件),如果是,则返回 true。 [ -f $file ] 返回 true。
-g file :检测文件是否设置了 SGID 位,如果是,则返回 true。 [ -g $file ] 返回 false。
-k file :检测文件是否设置了粘着位(Sticky Bit),如果是,则返回 true。 [ -k $file ] 返回 false。
-p file :检测文件是否是有名管道,如果是,则返回 true。 [ -p $file ] 返回 false。
-u file :检测文件是否设置了 SUID 位,如果是,则返回 true。 [ -u $file ] 返回 false。
-r file :检测文件是否可读,如果是,则返回 true。 [ -r $file ] 返回 true。
-w file :检测文件是否可写,如果是,则返回 true。 [ -w $file ] 返回 true。
-x file :检测文件是否可执行,如果是,则返回 true。 [ -x $file ] 返回 true。
-s file :检测文件是否为空(文件大小是否大于0),不为空返回 true。 [ -s $file ] 返回 true。
-e file :检测文件(包括目录)是否存在,如果是,则返回 true。 [ -e $file ] 返回 true。
括号运算
[ ] 运算符两边必须添加空格,字符串和数字比较
[[ ]] 运算符两边必须添加空格,字符串比较加强版
(( )) 运算符两边必须添加空格,数字比较加强版
( ) 用于shell命令及输出,在其中变量赋值不影响当前进程环境影响(会新开一个进程执行其中的命令),括号外的值不受括号里面赋值影响,但可以受里面的值影响
{ } 快速替换,其中的命令由当前进程执行,多命令之间用";"隔开
字符串实例
1. 取长度
str="abcd"
expr length $str # 4
echo ${#str} # 4
expr "$str" : ".*" # 4
2. 查找子串的位置
str="abc"
expr index $str "a" # 1
expr index $str "b" # 2
expr index $str "x" # 0
expr index $str "" # 0
3. 选取字串
str="abcdef"
expr substr "$str" 1 3 #从第一个位置开始取3个字符,abc
expr substr "$str" 2 5 #从第二个位置开始取5个字符,abcdef
expr substr "$str" 4 5 #从第四个位置开始取5个字符,def
expr ${str:2} #从第二个位置开始提取字符串,bcdef
expr ${str:2:3} #从第二个位置开始提取3个字符,bcdef
expr ${str:(-6):5} #从倒数第二个位置向左提取字符串,abcdef
expr ${str:(-4):3} #从倒数第二个位置开始向左提取6个字符,cde
4. 截取子串
str="abbc,def,ghi,abcjkl"
echo ${str#a*c} # 输出,def,ghi,abcjkl 一个井号(#) 表示从左边截取掉最短的匹配 (这里把abbc字串去掉)
echo ${str##a*c} # 输出jkl, 两个井号(##) 表示从左边截取掉最长的匹配 (这里把abbc,def,ghi,abc字串去掉)
echo ${str#"a*c"} # 输出"abbc,def,ghi,abcjkl" 因为str中没有"a*c"子串
echo ${str##"a*c"} # 输出"abbc,def,ghi,abcjkl" 同上
echo ${str#*a*c*} # 空
echo ${str##*a*c*} # 空
echo ${str#d*f) # 输出abbc,def,ghi,abcjkl,
echo ${str#*d*f} # 输出,ghi,abcjkl
echo ${str%a*l} # abbc,def,ghi 一个百分号(%)表示从右边截取最短的匹配
echo ${str%%b*l} # a 两个百分号表示(%%)表示从右边截取最长的匹配
echo ${str%a*c} # abbc,def,ghi,abcjkl
5. 字符串替换
str="apple, tree, apple tree"
echo ${str/apple/APPLE} # 替换第一次出现的apple
echo ${str//apple/APPLE} # 替换所有apple
echo ${str/#apple/APPLE} # 如果字符串str以apple开头,则用APPLE替换它
echo ${str/%apple/APPLE} # 如果字符串str以apple结尾,则用APPLE替换它
6. 比较
[[ "a.txt" == a* ]] # 逻辑真 (pattern matching)
[[ "a.txt" =~ .*\.txt ]] # 逻辑真 (regex matching)
[[ "abc" == "abc" ]] # 逻辑真 (string comparision)
[[ "11" < "2" ]] # 逻辑真 (string comparision), 按ascii值比较
7. 判断读取字符串值
${var} 变量var的值, 与$var相同
${var-DEFAULT} 如果var没有被声明, 那么就以$DEFAULT作为其值
${var:-DEFAULT} 如果var没有被声明, 或者其值为空, 那么就以$DEFAULT作为其值,判断var变量是否没有定义
${var=DEFAULT} 如果var没有被声明, 那么就以$DEFAULT作为其值
${var:=DEFAULT} 如果var没有被声明, 或者其值为空, 那么就以$DEFAULT作为其值 ,判断var变量是否没有定义,并确保变量始终有值
${var+OTHER} 如果var声明了, 那么其值就是$OTHER, 否则就为null字符串
${var:+OTHER} 如果var被设置了, 那么其值就是$OTHER, 否则就为null字符串
${var?ERR_MSG} 如果var没被声明, 那么就打印$ERR_MSG
${var:?ERR_MSG} 如果var没被设置, 那么就打印$ERR_MSG
${!varprefix*} 匹配之前所有以varprefix开头进行声明的变量
${!varprefix@} 匹配之前所有以varprefix开头进行声明的变量
echo
输出,将参数打印到标准输出,参数之间用空格隔开,以换行符结束
参数:
-n 不换行输出(默认换行)
-e 启用反斜杠转义解释
-E 禁用反斜杠转义解释
echo -e "\033[字背景颜色;文字颜色m字符串\033[0m"
\033 等价 \e
多格式用;隔开
字颜色:30—–37 字背景颜色:40—–47
30 40 黑色
31 41 红色
32 42 绿色
33 43 黄色
34 44 蓝色
35 45 紫红色
36 46 青蓝色
37 47 白色
ANSI控制码说明:
\33[0m 关闭所有属性
\33[1m 设置高亮度
\33[3m 斜体
\33[4m 下划线
\33[5m 闪烁
\33[7m 反显
\33[8m 消隐
\33[9m 中划线
\33[30m -- \33[37m 设置前景色
\33[40m -- \33[47m 设置背景色
\33[nA 光标上移n行
\33[nB 光标下移n行
\33[nC 光标右移n行
\33[nD 光标左移n行
\33[y;xH设置光标位置
\33[2J 清屏
\33[K 清除从光标到行尾的内容
\33[s 保存光标位置
\33[u 恢复光标位置
\33[?25l 隐藏光标
\33[?25h 显示光标
printf
输出,不会自动提供换行符
%s 输出字符串
%d 或%i 输出整数
%3.2f 输出浮点数,3表示整数位,2表示小数位
\f 换页
\n 换行
\r 回车
\v 垂直制表符
\b 退格
printf '%s' $(ls /tmp) #输出后面命令的结果
read
从标准输入获取值
参数:
-p 添加提示语句
-n 限制参数个数
-r 屏蔽转义字符,作普通字符处理
-s 静默模式,输入内容不显示在屏幕
-t 指定时间,超出时间后退出read,不读取任何内容
if判断语句
单分支结构:
if [ 判断操作 ] #if后面与"["必须空格
then
[ 操作序列 ]
fi
#if和then同行时
if [ 判断操作 ];then
[ 操作序列 ]
fi
多分支结构:
if [ 判断操作 ]
then
[ 操作序列 ]
elif [ 判断操作 ]
then
[ 操作序列 ]
else
[ 操作序列 ]
fi
其它:
if [ 判断操作 ] && [ 判断操作 ] && [ 判断操作 ]
if [ 判断操作 ] ;then : # ":"表示不作任何操作
for循环语句
结构:
for 变量名 in 取值列表
do
[ 操作序列 ]
done
取值列表:
in 1 2 3 4 5 #取1,2,3,4,5这几个参数
in {1..5} #同上,取1到5的值简写
in {1..100..2} #取1,3,5...97,99值,1-100且步长为2
in $(seq 1 2 100) #seq命令实现1到100且步长为2的取值,同上
C语言风格:
for((表达式1;表达式2;表达式3))
do
[ 操作序列 ]
done
#所有变量可以不用加$前缀,可以使用逻辑运算和四则运算,支持多个表达式之间用","隔开
从文件中获取变量值
for line in `cat file`
do
echo $line
done
while循环语句
结构:
while 测试操作
do
[ 操作序列 ]
done
从文件中获取变量值
while read -r line
do
echo $line
done < filename
case条件语句
结构:
case 变量 in
模式1)
命令序列
;;
模式2)
命令序列
;;
....
*)
默认命令序列
;;
esac
until语句
until与while循环类似
结构:
until
do
command....
done
#首次执行为0状态时,一次也不执行
select选择语句
功能类似于case,用于交互菜单显示
结构:
select 变量 in 列表值
do
command...
break
done
函数
定义:
function func
{
shell command
}
或
func () {
shell command
}
local 在函数内部声明局部变量,覆盖全局变量
数组
shell只支持一维数组
调用随机数函数$RANDOM,返回一个范围在0-32767之间的随机整数
$[RANDOM%100+100]
定义数组:
array_name=(beijing shanghai guangzhou shenzhen) #小括号作边界,使用空格分离
单独定义数组的元素:
array_name[0]="北京"
array_name[1]="上海"
array_name[3]="深圳" #下标可不连续
unset array[1] #清空数组元素
unset array #清空整个数组
获取数组元素:
echo ${array_name[1]}
echo ${array_name[@]} #输出数组所有元素,没有元素的下标省略
echo ${array_name[*]} #输出数组所有元素
获取元素个数:
${#array_name[@]} 或 ${#array_name}
获取单个元素长度:
${#array_name[1]}
参数传递
获取参数值:
$0 代表执行的文件
$1 代表传入的第一个参数
read -a array #从标准输入获取值
##for i in "{array[@]}" #遍历含空格的元素需要使用引号
##for i in "{array[*]}" #使用*时加引号,不分行打印
for i in ${array[@]} #可将@替换为*,遍历数组的所有赋值过的元素,分行打印
do
echo $i
done
特殊用法:
${array[*]:0} #抽取整个数组
${array[*]:4} #取从第4个元素到结束
${array[*]:1:2} #取从1个到第2个元素
${array[*]#z*c} #匹配每个元素删除以"z"开头"c"结束的最长字串
${array[*]/m*x/new} #替换数组中每个元素第一次相匹配的字串
${array[*]//m*x/new} #替换数组每个元素所有匹配的字串
加密shell脚本
安装shc加密工具
#wget http://www.datsi.fi.upm.es/%7Efrosal/sources/shc-3.8.7.tgz
#tar zvxf shc-3.8.7.tgz
#cd shc-3.8.7
#make test && make strings && make install
yum安装
#yum -y install shc
#shc -rvf 文件
shc命令参数:
-e date 指定过期时间
-m message 指定过期时间提示信息
-f script_filename 指定加密的文件名或者路径
-r 可在相同操作系统的不同主机行执行
-v 加密详细过程
#shc -e 03/23/2021 -f test.sh 脚本过期时间设置
#shc -m “hello” -f test.sh 指定到期时间后返回的信息