第一节 Shell编程概述
Shell是一个命令行解释器,它接收应用程序或用户的命令,然后调用操作系统内核。
Shell 这个词还代表一个功能强大的编程语言,易编写、易调试、灵活性强。
一、Linux系统提供的Shell解析器
我们编写的Shell脚本程序,需要经过Shell解析器的解析,才能交给Linux系统执行。
[root@hadoop101 ~]$ cat /etc/shells
/bin/sh
/bin/bash
/sbin/nologin
/usr/bin/sh
/usr/bin/bash
/usr/sbin/nologin
/bin/tcsh
/bin/csh
其中最常用的是 /bin/sh 和 /bin/bash
二、bash 和 sh 的关系
[root@rich workspace]# ll /bin
lrwxrwxrwx. 1 root root 7 7月 1 09:03 /bin -> usr/bin
[root@apple w]# ll /usr/bin | grep bash$
-rwxr-xr-x. 1 root root 964544 4月 11 2018 bash
lrwxrwxrwx. 1 root root 4 7月 9 16:42 sh -> bash
sh是bash的软链接
三、Linux默认的解析器是bash
[root@apple w]# echo $SHELL
/bin/bash
四、Shell语法总述
第二节 Shell编程HelloWorld
一、创建脚本文件
通常以.sh作为扩展名,这不是一个语法层面的要求,而仅仅是大众的习惯。
二、脚本内容
第一行指定当前脚本的解析器:
#!/bin/bash
实现具体功能:
echo "hello world"
在Shell脚本中,#开头的都是注释。#!/bin/bash相当于就是注释形式出现的一个固定格式,告诉Linux系统,当前使用/bin/bash解析器。
三、Shell脚本的运行方式
命令名 | 在当前进程运行脚本 | 新建子进程运行脚本 |
---|---|---|
source | √ | |
. | √ | |
sh | √ | |
bash | √ | |
chmod +x后直接运行 | √ |
其中“.”是source的另一种写法。在当前进程中发布的全局变量可以在当前进程的其他脚本中继续沿用,也可以在子进程中使用;但是子进程export发布的变量仅限于子进程内部使用。
从上面进程树中能够看到,sh、bash、直接运行这三种方式确实是开辟新的子进程运行脚本中的代码。
顺便一提:当我们给一个脚本文件通过chmod命令+x操作附加了执行权限,此时需要通过路径访问到这个文件,否则就必须加入PATH环境变量。不以路径的形式调用,就会看到下面错误提示:
此时要么把脚本文件所在目录添加到PATH环境变量中,要么通过路径访问和调用:
第三节 变量
一、系统预定义变量
常用系统变量包括:USER、HOME、PWD、SHELL等,可以使用echo命令输出它们的值。
二、使用set命令查看
ABRT_DEBUG_LOG=/dev/null
BASH=/bin/bash
BASHOPTS=checkwinsize:cmdhist:expand_aliases:extglob:extquote:force_fignore:histappend:interactive_comments:login_shell:progcomp:promptvars:sourcepath
BASH_ALIASES=()
BASH_ARGC=()
BASH_ARGV=()
……
三、自定义变量
1、基本语法
①定义变量
USER_NAME=tom
②引用变量
#!/bin/bash
# 声明变量的同时赋值
MR_FENG=shuai
# 打印变量,使用$变量名方式来引用
echo $MR_FENG
# 引用未声明、未初始化的变量,没有任何打印的内容。没有报错
echo $GOOD
③撤销变量
unset 变量名
[root@apple ~]# USER_NAME=tom
[root@apple ~]# echo $USER_NAME
tom
[root@apple ~]# unset USER_NAME
[root@apple ~]# echo $USER_NAME
④声明常量
[root@apple ~]# readonly COMPANY_NAME=atguigu
[root@apple ~]# echo $COMPANY_NAME
atguigu
[root@apple ~]# COMPANY_NAME=uuu
-bash: COMPANY_NAME: 只读变量
[root@apple ~]# unset COMPANY_NAME
-bash: unset: COMPANY_NAME: 无法反设定: 只读 variable
不能修改,不能撤销
2、语法规则
- 变量名称可以由字母、数字和下划线组成,但是不能以数字开头,环境变量名建议大写。
- 等号两侧不能有空格,衍生规则就是变量声明时必须初始化
- 在bash中,变量默认类型都是字符串类型,无法直接进行数值运算。
- 变量的值如果有空格,需要使用双引号或单引号括起来。
- 变量名区分大小写。
四、特殊变量
1、$n
n是数字,$0代表当前脚本名称。从1开始表示脚本接收到的第n个参数。从{10}开始数字需要使用{}括起来。
[root@hadoop101 datas]$ touch parameter.sh
[root@hadoop101 datas]$ vim parameter.sh
#!/bin/bash
echo "$0 $1 $2"
[root@hadoop101 datas]$ chmod 777 parameter.sh
[root@hadoop101 datas]$ ./parameter.sh cls xz
./parameter.sh cls xz
2、$#
返回输入参数的个数
[root@hadoop101 datas]$ vim parameter.sh
#!/bin/bash
echo "$0 $1 $2"
echo $#
[root@hadoop101 datas]$ chmod 777 parameter.sh
[root@hadoop101 datas]$ ./parameter.sh cls xz
parameter.sh cls xz
2
3、*和@
都能够返回全部参数,只有在循环中且放在引号中能够体现出它们的区别。
[root@hadoop101 datas]$ vim parameter.sh
#!/bin/bash
echo "$0 $1 $2"
echo $#
echo $*
echo $@
[root@hadoop101 datas]$ bash parameter.sh 1 2 3
parameter.sh 1 2
3
1 2 3
1 2 3
4、$?
返回上一条命令的执行结果。
- 条件判断语句
- 返回 0 表示 true
- 返回 1 表示 false
- 普通语句
- 返回 0 表示成功
- 返回非 0 数表示失败
#!/bin/bash
# 把运行当前脚本的程序名称输出
echo "运行当脚本的程序名称:$0"
# 把运行当前脚本时传入的参数输出
echo "第一个参数:$1"
echo "第二个参数:$2"
echo "第三个参数:$3"
# 数字是两位数时需要带上大括号
echo "第十个参数:${10}"
# 输出传入参数的个数
echo "传入参数的个数:$#"
# 输出全部参数
echo "全部参数列表:$*"
echo "全部参数列表:$@"
# 输出上一条命令执行的结果
echo "上一步操作:$?"
# 人为制造一个问题
shuai
echo "上一步操作:$?"
第四节 表达式
((表达式))或[表达式]
[root@apple ~]# echo $((20+30))
50
[root@apple ~]# echo $(((15+15)*2))
60
[root@apple ~]# echo $[(15+15)*2]
60
第五节 条件判断
一、基本语法
- 写法1:test condition
- 写法2:[ condition ]
- 注意 condition 前后有空格
- 空字符串视为 false,非空字符串视为 true
二、常用条件判断
数据类型 | 写法 | 单词 | 含义 |
---|---|---|---|
数值 | -lt | less than | 小于 |
数值 | -le | less equal | 小于等于 |
数值 | -eq | equal | 等于 |
数值 | -gt | greater than | 大于 |
数值 | -ge | greater equal | 大于等于 |
数值 | -ne | not equal | 不等于 |
文件 | -r | read | 判断当前用户是否可以读取该文件 |
文件 | -w | write | 判断当前用户是否可以修改该文件 |
文件 | -x | execute | 判断当前用户对该文件是否有执行权限 |
文件 | -f | file | 判断当前文件是否存在并且是一个常规的文件 |
文件 | -e | existence | 判断文件是否存在 |
文件 | -d | directory | 判断是否存在并且是一个目录 |
可以参考下面例子:
#!/bin/bash
# 条件判断表达式写法一:test
test 10 -gt 7
# 使用$?获取上一条语句的执行结果
echo $?
# 使用$?获取上一条语句的执行结果
test 10 -lt 7
echo $?
# 条件判断表达式写法二:[ condition ]
# 注意:condition两边要有空格
[ 10 -gt 7 ]
echo $?
[ 10 -lt 7 ]
echo $?
# 专门针对文件进行操作的运算符
[ -e aaa.txt ]
echo $?
[ -r aaa.txt ]
echo $?
[ -w aaa.txt ]
echo $?
第六节 流程控制
一、三目运算符
[root@apple w]# [ 10 -gt 5 ] && echo "10大于5" || echo "10小于5"
10大于5
二、if判断
注意:if后面有空格
1、单if
if [ 10 -gt 5 ]
then
echo "10大于5"
fi
2、if…else
if [ 10 -gt 5 ]
then
echo "10大于5"
else
echo "10小于5"
fi
3、if…elif
if [ 10 -gt 5 ]
then
echo "10大于5"
elif [ 10 -lt 5 ]
then
echo "10小于5"
else
echo "10等于5"
fi
三、case判断
AGE=10
case $AGE in
"10")
echo 10
;;
"20")
echo 20
;;
*)
echo other
;;
esac
四、for循环
1、常规用法
for (( i=1;i<=10;i++ ))
do
echo $i
done
2、遍历外部数据
#!/bin/bash
# 如果需要使用外部数据,则需要把外部数据赋值给一个变量,不能在for语句中直接使用
len=$1
for (( i=1;i<=len;i++ ))
# 从do关键字开始是循环体开始
do
echo $i;
# 到done关键字为止是循环体结束
done
3、for…in循环
for i in $*
do
echo $i
done
没有引号的时候*和@一样,加了引号就有区别
for i in "$*"
do
echo $i
done
上面代码输出的结果是:
[root@apple w]# sh shell42.sh a b c d e
a b c d e
“*”没有换行,“@”有换行
五、while循环
s=0
i=1
while [ $i -le 100 ]
do
s=$[$s+$i]
i=$[$i+1]
done
echo $s
第七节 函数
一、概述
Shell 编程中的函数和我们以前熟悉的函数最大的区别是:Shell 编程中要求函数的返回值只能是整数。并且只能通过 $? 方式获得。可以显示加:return 返回,如果不加,将以最后一条命令运行结果,作为返回值。return 后跟数值 n(0-255)。
# 声明函数
function sum() {
echo $[$1+$2]
}
# 调用函数
sum 10 20
在函数体中,$1、$2等是对函数参数的引用。
#!/bin/bash
# 声明函数
function sum(){
# 使用$1、$2引用函数传入的参数
return $[$1+$2]
}
# 调用函数,传入参数
sum 10 20
# 使用$?获取函数执行结果
echo "sum 10 20执行的结果是$?"
二、获取外部数据
1、获取参数
这个前面说过了,通过$1、2等方式获取函数参数,{10}开始需要使用大括号。
2、使用read读取用户输入
read命令有两个常用参数
- -t用于指定输入等待时间,单位是秒
- -p用于指定提示文字
read -t 10 -p "please enter:" NAME
echo $NAME
三、小练习
#!/bin/bash
# 用户输入数字,判断是否为7的倍数
# 初步读取用户是否继续玩的指令
read -t 10 -p "请输入数字指令(0代表继续玩,1代表退出):" code
# 循环询问用户是否继续玩
while [ $code -ne 1 ]
# 进入循环体
do
# 读取用户输入的数据
read -t 10 -p "请输入数字:" number
# 判断数据是否为7的整数倍
if [ $[$number%7] -eq 0 ]
# 进入if分支
then
# 执行if分支
echo "$number是7的倍数。"
# 进入else分支
else
# 执行else分支
echo "$number不是7的倍数。"
# 结束if结构
fi
# 再次读取用户是否继续玩的指令
read -t 10 -p "请输入数字指令(0代表继续玩,1代表退出):" code
# 结束循环体
done