shell编程
什么是shell?
Shell 是一个用 C 语言编写的程序,它是用户使用 Linux 的桥梁。Shell 既是一种命令语言,又是一种程序设计语言。
Shell 是指一种应用程序,这个应用程序提供了一个界面,用户通过这个界面访问操作系统内核的服务。
变量:
声明变量:export name="value"
declare -i ab
声明整数型变量
""
:包含的变量会被解释
''
:包含的变量会当做字符串解释
``
:反引号中的内容会作为系统命令,并执行其内容,可以替换输出一个变量
declare
命令用来声明shell变量:
参数说明:
- +/- "-“可用来指定变量的属性,”+"则是取消变量所设的属性。
- -f 仅显示函数。
- r 将变量设置为只读。
- x 指定的变量会成为环境变量,可供shell以外的程序来使用。
- i [设置值]可以是数值,字符串或运算式。
# declare -i ef //声明整数型变量
# ef=1 //变量赋值(整数值)
# echo $ef //显示变量内容
# declare +i ef //取消变量属性
# declare -r ab //设置变量为只读
# unset variable_name //删除变量
#! 是一个约定的标记,它告诉系统这个脚本需要什么解释器来执行,即使用哪一种 Shell。
变量名外面的花括号是可选的,加不加都行,加花括号是为了帮助解释器识别变量的边界
获取字符串的长度
string="abcd"
echo ${#string} #输出 4
Shell 数组
bash支持一维数组(不支持多维数组),并且没有限定数组的大小。
类似于 C 语言,数组元素的下标由 0 开始编号。获取数组中的元素要利用下标,下标可以是整数或算术表达式,其值应大于或等于 0。
定义数组
在 Shell 中,用括号来表示数组,数组元素用"空格"符号分割开。定义数组的一般形式为:
数组名=(值1 值2 ... 值n)
例如:
array_name=(value0 value1 value2 value3)
或者
array_name=(
value0
value1
value2
value3
)
还可以单独定义数组的各个分量:
array_name[0]=value0
array_name[1]=value1
array_name[n]=valuen
可以不使用连续的下标,而且下标的范围没有限制。
读取数组
读取数组元素值的一般格式是:
${数组名[下标]}
例如:
valuen=${array_name[n]}
使用 @ 符号可以获取数组中的所有元素,例如:
echo ${array_name[@]}
获取数组的长度
获取数组长度的方法与获取字符串长度的方法相同,例如:
# 取得数组元素的个数
length=${#array_name[@]}
# 或者
length=${#array_name[*]}
# 取得数组单个元素的长度
lengthn=${#array_name[n]}
多行注释
多行注释还可以使用以下格式:
:<<EOF
注释内容...
注释内容...
注释内容...
EOF
EOF 也可以使用其他符号:
:<<'
注释内容...
注释内容...
注释内容...
'
:<<!
注释内容...
注释内容...
注释内容...
!
特殊变量:
$1,$2,…:对应调用第1,第2等参数
$0:命令本身
$*:以一个单字符串显示所有向脚本传递的参数。如"$*"用「"」括起来的情况、以"$1 $2 … $n"的形式输出所有参数。
$@:与$*相同,但是使用时加引号,并在引号中返回每个参数。如"$@"用「"」括起来的情况、以"$1" "$2" … "$n" 的形式输出所有参数。
$#:传递给脚本的参数的个数
$?:显示最后命令的退出状态。0表示没有错误,其他任何值表明有错误。
$*
与$@
区别:
- 相同点:都是引用所有参数。
- 不同点:只有在双引号中体现出来。假设在脚本运行时写了三个参数 1、2、3,,则 " * " 等价于 “1 2 3”(传递了一个参数),而 “@” 等价于 “1” “2” “3”(传递了三个参数)。
基本运算
- 数学运算
#!/bin/bash
val=`expr 2 + 2`
echo "两数之和为 : $val"
两点注意:
- 表达式和运算符之间要有空格,例如 2+2 是不对的,必须写成 2 + 2,这与我们熟悉的大多数编程语言不一样。
- 完整的表达式要被反引号 `` 包含
[root@master ~]# echo "today is `date` "
today is 2022年 02月 05日 星期六 12:41:11 CST
- 判断文件的行数:
[root@master ~]# wc -l mysql.sh
17 mysql.sh
[root@master ~]# wc -l mysql.sh |cut -d' ' -f1
17
[root@master ~]# cat linecount.sh
#!/bin/bash
linecount="$(wc -l $1|cut -d' ' -f1)"
echo "This file have ${linecount} lines"
- 计算/etc/passwd文件中的第10个用户和第20用户的 ID之和:
[root@master ~]# cat /etc/passwd|head -10|tail -1
operator:x:11:0:operator:/root:/sbin/nologin
#用户名:密码占位符:uid:gid:用户描述信息:家目录:登录指定shell
[root@master ~]# cat /etc/passwd|head -10|tail -1|cut -d: -f3
11
[root@master shell]# cat id_sum.sh
#!/bin/bash
userid1=$(cat /etc/passwd|head -10|tail -1|cut -d: -f3)
userid2=$(cat /etc/passwd|head -10|tail -1|cut -d: -f3)
userid_sum=$[$userid1 + $userid2]
echo $userid_sum
- 统计文件空行
[root@master shell]# cat blank_count.sh
#!/bin/bash
blank1=$(grep "^$" $1 | wc -l)
blank2=$(grep "^$" $1 | wc -l)
allblank=$[$blank1 + $blank2]
echo "The sum space line: $allblank"
[root@master shell]# bash blank_count.sh /etc/httpd/conf/httpd.conf.bak ~/mysql.sh
The sum space line: 74
数值比较
-gt
:大于(greater than)
-ge
:大于等于
-eq
:等于
-ne
:不等于
-lt
:小于(less than)
-le
:小于等于
- 数字比大小
[root@master shell]# cat diff.sh
#!/bin/bash
read -p "请输入两个正整数" num1 num2
if [ $num1 -gt $num2 ];then
echo "$num1 > $num2"
elif [ $num1 -lt $num2 ];then
echo "$num1 < $num2"
else
echo "$num1 = $num2"
fi
[root@master shell]# bash diff.sh
请输入两个正整数2 4
2 < 4
文件测试
简单的存在性测试:
-e FILE :文件存在性测试,存在为真,否则为假
存在性及类型测试:
-b FLIE:是否存在且为块设备文件;
-c FILE:是否存在且为字符设备文件;
-d FILE:是否存在且为目录文件;
-f FILE:是否存在且为普通文件;
-h FILE 或 -L FILE : 存在且为符号链接文件;
-p FIEL :是否存在且为命名管道文件;
-S FILE:是否存在且为套接文件;
文件权限测试:
-r FILE:是否存在且可读
-w FILE:是否存在且可写
-x FILE:是否存在可执行
文件特殊权限测试:
-g FILE:是否存在且拥有sgid权限;
-u FILE:是否存在且拥有suid权限;
-k FILE:是否存在且拥有sticky权限;
文件大小测试:
-s FILE:是否存在且非空
文件是否打开:
- fd:fd表示文件描述符是否已经打开且与某终端相关
-N FILE:文件自动上一次读取之后是否被修改过;
-O FILE:当前用户是否为文件的属主;
-G FILE:当前有效用户是否为文件数组;
双目测试:
FILE1 -ef FILE2 :FILE1与FILE2是否指向同一个设备上的相同inode
FILE1 -nt FILE2:FILE1是否新于FILE2
FILE1 -ot FILE2:FILE1是否旧于FILE2
file="/var/www/runoob/test.sh"
if [ -r $file ]
then
echo "文件可读"
else
echo "文件不可读"
fi
if [ -w $file ]
then
echo "文件可写"
else
echo "文件不可写"
fi
if [ -x $file ]
then
echo "文件可执行"
else
echo "文件不可执行"
fi
if [ -f $file ]
then
echo "文件为普通文件"
else
echo "文件为特殊文件"
fi
if [ -d $file ]
then
echo "文件是个目录"
else
echo "文件不是个目录"
fi
if [ -s $file ]
then
echo "文件不为空"
else
echo "文件为空"
fi
if [ -e $file ]
then
echo "文件存在"
else
echo "文件不存在"
fi
组合测试
逻辑运算符:
&&代表的意思是当前一个命令执行成功时会继续执行后续的命令,当前一个命令执行失败的时候不会执行后续的命令
||代表的意思是当前一个命令执行成功时不会继续执行后续的命令,当前一个命令执行失败的时候会执行后续的命令
第一种方式:
COMMAND1 && COMMAND2
COMMAND1 || COMMAND2
! COMMAND
第二种方式:
EXPRESSION1 -a EXPRESSION2
EXPRESSION1 -o EXPRESSION2
! EXPRESSION
Note:必须使用测试命令进行
printf命令
printf “%-10s %-8s %-4s**\n**” 姓名 性别 体重kg
printf “%-10s %-8s %-4.2f**\n**” 郭靖 男 66.1234
printf “%-10s %-8s %-4.2f**\n**” 杨过 男 48.6543
printf “%-10s %-8s %-4.2f**\n**” 郭芙 女 47.9876
执行脚本,输出结果如下所示:
姓名 性别 体重kg
郭靖 男 66.12
杨过 男 48.65
郭芙 女 47.99
%s %c %d %f 都是格式替代符,%s 输出一个字符串,%d 整型输出,%c 输出一个字符,%f 输出实数,以小数形式输出。
%-10s 指一个宽度为 10 个字符(- 表示左对齐,没有则表示右对齐),任何字符都会被显示在 10 个字符宽的字符内,如果不足则自动以空格填充,超过也会将内容全部显示出来。
%-4.2f 指格式化为小数,其中 .2 指保留2位小数。
shell流程控制
选择执行
if 判断条件;then
条件为真的分支代码
elif 判断条件;then
条件为真的分支代码
else
条件为假的分支代码
fi
- 判断用户是否存在,如果不存在就添加用户,并设置密码和用户相同
[root@master shell]# cat user.sh
#!/bin/bash
if [ $# -lt 1 ];then
echo "at least one argument"
exit 1
fi
if id $1 &> /dev/null;then
echo "$1 exists"
exit 0
else
useradd $1
[ $? -eq 0 ] && echo "$1"|passwd --stdin $1 &>/dev/null
echo "user $1 have been created"
fi
- 判断流程
[root@master shell]# ps -ef |grep -c mysql
1
[root@master shell]# ps -ef|grep mysql
root 2818 1741 0 19:46 pts/1 00:00:00 grep --color=auto mysql
if [ $(ps -ef | grep -c "ssh") -gt 1 ]; then echo "false"; fi
for循环
语法:
for var in item1 item2 ... itemN
do
command1
command2
...
commandN
done
写成一行:
for var in item1 item2 ... itemN; do command1; command2… done;
- 循环输出
for loop in 1 2 3 4 5
do
echo "The value is: $loop"
done
输出结果:
The value is: 1
The value is: 2
The value is: 3
The value is: 4
The value is: 5
----------------------
#!/bin/bash
for str in This is a string
do
echo $str
done
输出结果:
This
is
a
string
while语句
语法:
while condition
do
command
done
实例:
#!/bin/bash
int=1
while(( $int<=5 ))
do
echo $int
let "int++"
done
命令:let
let 命令是 BASH 中用于计算的工具,用于执行一个或多个表达式,变量计算中不需要加上 $ 来表示变量。如果表达式中包含了空格或其他特殊字符,则必须引起来。
while循环可用于读取键盘信息:
echo '按下 <CTRL-D> 退出'
echo -n '输入你最喜欢的语言: '
while read FILM
do
echo "$FILM 是最好的语言!"
done
无限循环
无限循环语法格式:
while :
do
command
done
或者
while true
do
command
done
或者
for (( ; ; ))
until 循环
until 循环执行一系列命令直至条件为 true 时停止。
until 循环与 while 循环在处理方式上刚好相反。
一般 while 循环优于 until 循环,但在某些时候—也只是极少数情况下,until 循环更加有用。
until 语法格式:
until condition
do
command
done
condition 一般为条件表达式,如果返回值为 false,则继续执行循环体内的语句,否则跳出循环。
以下实例我们使用 until 命令来输出 0 ~ 9 的数字:
#!/bin/bash
a=0
until [ ! $a -lt 10 ]
do
echo $a
a=`expr $a + 1`
done
case … esac
case … esac 为多选择语句,与其他语言中的 switch … case 语句类似,是一种多分支选择结构,每个 case 分支用右圆括号开始,用两个分号 ;; 表示 break,即执行结束,跳出整个 case … esac 语句,esac(就是 case 反过来)作为结束标记。
可以用 case 语句匹配一个值与一个模式,如果匹配成功,执行相匹配的命令。
case … esac 语法格式如下:
case 值 in
模式1)
command1
command2
...
commandN
;;
模式2)
command1
command2
...
commandN
;;
esac
实例:
echo '输入 1 到 4 之间的数字:'
echo '你输入的数字为:'
read aNum
case $aNum in
1) echo '你选择了 1'
;;
2) echo '你选择了 2'
;;
3) echo '你选择了 3'
;;
4) echo '你选择了 4'
;;
*) echo '你没有输入 1 到 4 之间的数字'
;;
esac
break命令
break命令允许跳出所有循环(终止执行后面的所有循环)。
下面的例子中,脚本进入死循环直至用户输入数字大于5。
#!/bin/bash
while :
do
echo -n "输入 1 到 5 之间的数字:"
read aNum
case $aNum in
1|2|3|4|5) echo "你输入的数字为 $aNum!"
;;
*) echo "你输入的数字不是 1 到 5 之间的! 游戏结束"
break
;;
esac
done
continue
continue命令与break命令类似,只有一点差别,它不会跳出所有循环,仅仅跳出当前循环。
#!/bin/bash
while :
do
echo -n "输入 1 到 5 之间的数字: "
read aNum
case $aNum in
1|2|3|4|5) echo "你输入的数字为 $aNum!"
;;
*) echo "你输入的数字不是 1 到 5 之间的!"
continue
echo "游戏结束"
;;
esac
done
shell函数
demoFun(){
echo "这是我的第一个 shell 函数!"
}
echo "-----函数开始执行-----"
demoFun
echo "-----函数执行完毕-----"