执行方式
方式1: ./脚本名
方式2: bash 脚本名
方式3: source 脚本名
三种执行方式的区别:
1. ./脚本名的方式需要加执行权限(chmod u+x 脚本名) 其他两种不需要执行权限。
2. ./脚本名 和 bash 脚本名 方式执行时,是在后台打开了一个新的终端(我们看不到)来执行脚本,并将执行结果返回给当前终端,而source 脚本名 方式执行,是在当前终端执行的。
shell中的变量
2.1 特点
shell中使用变量时无需提前定义,直接使用即可。
shell中的变量默认都是字符串类型,就没有整型、浮点型、字符型之分
shell中的变量也要符合标识符的命名规范:
1.数字、字母、下划线组成
2.不能以数字开头
3.不能和shell的关键字冲突(ls cd pwd..)
shell中使用变量时,一般情况下变量名都大写
shell中每条指令结束不需要加分号
2.2 引用变量的值
$变量名 或者 ${变量名}
注意千万不能写成 $(变量名) 这种写法是命令置换符。
清空变量的值:unset 变量名 。
shell中的位置变量
位置变量相当于C语言中 main函数的参数
$0
$0 脚本名 三种不同的执行方式 $0 也不一样
./方式执行 ---》 ./脚本名
bash方式执行 --》 脚本名
source方式执行 --》 bash
$1~$9
表示执行脚本时 在命令行传的 第1到第9个参数
如果参数超过9个 就需要用 ${} 了
如 ${10} ${27}
$@ 表示执行脚本时 在命令行传的 所有参数
$* 表示执行脚本时 在命令行传的 所有参数
$# 表示执行脚本时 在命令行传的所有参数的个数
$$ 执行脚本的进程号
$? 表示上一条命令是否执行成功 (成功 0 失败 1)
变量的作用域
shell中如果没有特殊说明,变量的作用域都是全局的
如果想声明局部使用 需要使用关键字 local 来声明
例:
#!/bin/bash
#shell中的函数定义
function my_func(){
VALUE1=hello
local VALUE2=beijing
}
my_func #shell中的函数调用
echo $VALUE1 #hello
echo $VALUE2 #空的
只读变量
使用 readonly 关键修饰的变量都是只读变量
命令置换符
作用:将命令执行的结果赋值给变量
`` 注意:这个不是单引号 这是反引号 键盘上1前面的按键
$()
例:
#!/bin/bash
LIST1=`ls`
echo $LIST1
LIST2=$(pwd)
echo $LIST2
字符串的截取
STRING1=www.hqyj.com //从前往后是0开始,从后往前是1开始。
STRING2=${STRING1:4} #从STRING1第4位开始 一直截取到最后。
echo $STRING2 #hqyj.com
STRING3=${STRING1:4:4} #从STRING1第4位开始 截取4个字符
echo $STRING3 #hqyj
STRING4=${STRING1:0-6} #从STRING1的倒数第6位 一直截取到最后
echo $STRING4 #yj.com
STRING5=${STRING1:0-6:2} #从STRING1的倒数第6位 截取2个字符
echo $STRING5 #yj
shell中的数组
shell只有一维数组,shell中的数组用 () 来表示
shell中的数组也是无需提前定义 直接使用即可
shell中的数组也是没有类型的 默认都是 字符串
ARR1=(aa bb 'cc' "hello" 123 world) #完全初始化
ARR2=([0]="hello" [3]="world") #不完全初始化 没有初始化的元素就是空的
#给数组元素重新赋值
ARR1[0]="hqyj"
#获取数组元素的值
# ${数组名[下标]}
echo ${ARR1[0]} #hqyj
echo ${ARR1[3]} #hello
#获取数组中所有元素
${数组名[@]}
${数组名[*]}
echo ${ARR1[@]}
echo ${ARR1[*]}
#获取数组元素的个数
${#数组名[@]}
${#数组名[*]}
echo ${#ARR1[@]} #6
echo ${#ARR1[*]} #6
echo ${#ARR2[*]} #2
#数组元素的追加
ARR3=(aa bb cc)
echo ${ARR3[@]}
ARR3=(${ARR3[@]} "hello" 'world') #在后面追加
echo ${ARR3[@]}
ARR3=(beijing ${ARR3[@]}) #在前面追加
echo ${ARR3[@]}
shell中的输入和输出
9.1 输出--echo
echo有些类似于 printf
例:
V1=hello
echo $V1 #echo是自带换行符的
echo "hello world"
V2=beijing
echo $V1 $V2 #可以一行输出多个值
#默认是不识别转义字符 \n 的 如果需要识别 得加 -e 选项
echo -e "hello\nworld"
9.2 输入--read
(1)read用法类似于scanf,多个变量输入时 用空格分隔,如果要输入的值 本身就有空格,建议写多行 read 指令分别获取,否则有冲突;
(2)如果要输入数组 需要加 -a 选项,输入时要用空格分隔数组元素;
(3)-p 可以加描述字段,用作输入的提示,用“ ”标注内容;
(4)read -n 5 表示最多只获取5个字符,输入5个字符时,即使不敲回车,也会继续向下执行;
(5)read -t 5 表示等待输入时只等待 5秒,超过5秒没输入,程序就会继续向下执行
(6)read -s 表示取消输入的回显;
(7)多个选项可以同时使用,但是要注意:选项和参数必须成对存在。
shell中的运算符
shell本身使用来批量执行命令的,而不是用来做复杂运算的,如果需要用到复杂的运算,可以使用shell调用C程序来处理问题,但是在一些场景下,简单的运算如果也调用C程序处理的话,程序会冗长,比如:计数变量的自增。这时,就可以用shell中的运算符来做一些简单的运算。
shell中如果不使用运算符直接运算,那么默认都会将变量按字符串处理。
shell中的运算符:
(1) (()) 推荐使用
(2) $[]
(3) let
(4) expr
上述四种运算符的执行效率从上到下是依次递减的,其中expr可以做字符串处理。
10.1 (())
格式:((表达式))、((表达式1, 表达式2, ..., 表达式n))
当有多个表达式的时候,每个表达式都会参与运算,而最后一个表达式的结果,就是整个(())运算符表达式的结果。
注意事项:
1.在(())内部,引用变量值的时候,$ 可加可不加;
2.在(())内部,运算符前后的空格,可加可不加;
3.如果想获取表达式的结果 需要在整个(())前加 $ 如 $((A+B));
4.可以做复杂的运算,如 ++ -- for循环;
for((i=0; i<100; i++))
10.2 $[]
格式:$[表达式]、$[表达式1, 表达式2, ..., 表达式n]
当有多个表达式的时候,每个表达式都会参与运算,最后一个表达式的结果,就是整个$[]运算符表达式的结果。
注意事项:
1.在$[]内部,引用变量值的时候,$ 可加可不加;
2.在$[]内部,运算符前后的空格,可加可不加;
3.$[]表达式的结果必须要有变量来接,或者输出到终端,否则报错
$[A+B] 这种格式是错误的 RET=$[A+B] echo $[A+B]
4.不可以做复杂的运算
$[i=0; i<100; i++] #错误的写法
10.3 expr
1.在expr内部,引用变量值的时候,$ 必须加;
2.在expr内部,运算符前后的空格,必须加;
3.expr会自动将运算的结果输出到终端;
4.如果想获取expr的结果,需要用命令置换符 $() 或者 `` ; ret= `expr $v1 + 1`
5.expr不能做复杂的运算 如 ++ -- ,但是可以用其他的写法实现++的功能,如 V1=`expr $V1 + 1`;
expr在做乘法运算的时候 * 要写成 \*。
10.4 let
格式:let 表达式1 表达式2 ... 表达式n //中间用空格分隔
注意:let必须是完整的算术表达式,即有等号两边
如 let C=A+B 正确
C=`let A+B` 错误
1.let会运算所有的表达式:
VAR1=30
VAR2=25
let RET=VAR1+VAR2
RET=RET+VAR1
echo $RET #85
2.let中变量可以加$,也可以不加$;
3.运算符的前后不能加空格。
shell中的 if 语句(很重要)
11.1 格式
if [ 表达式1 ] #注意:if和[ 之间 [] 和表达式之间 都必须有空格
then
分支1
elif [ 表达式2 ] #注意:elif和[ 之间 [] 和表达式之间 都必须有空格
then
分支2
else #else后面没有 then
其他分支
fi
11.2 对字符串的判断
其作用类似于strcmp。
注意:对字符串的判断最好加双引号,否则如果参与判断的字符串中有空格就会报错。
-z 判断字符串是否为空
-n 判断字符串是否为非空
==或者= 判断字符串是否相等
!= 判断字符串是否不相等
\> 判断字符串的大小 大于
\< 判断字符串的大小 小于
拓展:
< 和 << 输入重定向符
> 和 >> 输出重定向符
> 覆盖写
>> 追加写
11.3 将字符串按整型方式判断
-gt 大于 great than
-lt 小于(小写的L) less than
-eq 等于 equal
-ge 大于等于 great equal
-le 小于等于 less equal
-ne 不等于 not equal
逻辑运算
-a 逻辑与 and
-o 逻辑或 or
! 逻辑非
关于逻辑运算符,以逻辑与为例:
在两个 [] 之间要用 &&
在一个 [] 内部要用 -a
11.4 判断文件的类型
-e 判断文件是否存在 存在为真 不存在为假
-s 判断文件是否存在,并判断文件是否为非空,非空为真 空为假
-b 判断文件是否存在,并且判断文件是否为块设备文件
-S (大写)判断文件是否存在,并且判断文件是否为套接字文件
-p 判断文件是否存在,并且判断文件是否为管道文件
-f 判断文件是否存在,并且判断文件是否为普通文件
-L (大写)判断文件是否存在,并且判断文件是否为软链接文件
-c 判断文件是否存在,并且判断文件是否为字符设备文件
-d 判断文件是否存在,并且判断文件是否为目录文件
注意:
判断类型时,要先判断链接文件,
因为链接文件的类型既算是链接文件 也算是他链接的文件的类型
所以先判断链接文件,是因为其他文件不会被识别成链接文件
11.5 判断文件的权限
-r 判断文件是否存在,并判断是否有读权限
-w 判断文件是否存在,并判断是否有写权限
-x 判断文件是否存在,并判断是否有执行权限
注意:哪个用户来执行脚本,判断的就是改基本对哪个用户的权限
11.6 判断文件的时间戳
时间戳:文件最后一次修改的时间
-nt 判断前面文件的时间戳是否比后面的新 new than
-ot 判断前面文件的时间戳是否比后面的旧 old than
注意:离当前时间越近,时间戳越新。
11.7 判断文件的inode号
使用 stat 文件名 可以查看文件的inode号
只有硬链接文件 inode 号才相同
-ef 相同为真 不同为假
case..in 语句
12.1 格式
case $变量 in
选项1)
分支1
;;
选项2)
分支2
;;
选项n)
分支n
;;
*)
其他分支
;;
esac
注意:
case 后面一定是变量的值 也就是说变量名前一定有 $
选项中的值就是case后面的变量所有可能的结果
* 选项表示其他分支,相当于switch..case中的 default分支
12.2 关于case的选项
选项是支持通配符的
[0-9] 通配0-9中的任意一个字符
[a-zA-Z] 通配 a-zA-Z中的任意一个字符 注意 涉及本地语序 LC_ALL=C
"aa"|"bb"|"cc" 通过 aa 或者 bb 或者 cc
[abc] 通配[abc]中的任意一个字符
[^abc] 通配除了[abc]中的任意一个字符。。。这种写法逻辑是反的,基本不使用。
shell中的while循环
13.1 格式
while 表达式
do
循环体
done
其中表达式的用法和 if 的表达式一样
shell中的for循环
14.1 C风格的for循环
for ((i=0;i<10;i++))
do
循环体
done
14.2 shell中特有的for循环
格式:
for 变量 in 单词列表
do
循环体
done
关于单词列表:
1.单词列表中的单词用空格分隔;
2.如果单词列表是连续的,可以使用 {start..end} 如 {1..100}
也可以使用序列来处理 `seq 1 100` #遍历 [1, 100];
也可以按照一定的间隔取数据 `seq 1 2 100` #从1开始取数据 步长为2 到100结束;
3.单词列表也可以是命令的结果(只要命令的结果是以空格分隔的就可以);
4.省略 in 的 for循环,这时 变量的值来自于执行脚本时 命令行的参数。
shell中的select..in语句
select 变量 in 单词列表
do
语句
done
select 会将单词列表生成一个可供用户选择的列表:
1) aa
2) bb
3) cc
#? //用户选择的时候,要通过编号来选择,不能通过单词来选择
其中 #? 是可以改的 修改环境变量 PS3
如 export PS3="input>>"
实际使用的时候 select..in 语句一般都和 case..in 语句连用,例:
select i in `ls`
do
#配合着case..in 可以根据用户的选择不同 走不同的逻辑
case $i in
"01test.sh")
echo "1111"
;;
"02test.sh")
echo "2222"
;;
*)
echo "other"
;;
esac
break
done
shell中的break和continue
break 结束本层的循环
break n n是一个数字 结束n层循环
continue 结束本层的本地循环
continue n n是一个数字 结束n层的本次循环
shell中的函数
17.1 定义函数的格式
#注意 function可以不写 但是建议写上 方便阅读
function 函数名(){
函数体
}
1.shell中的函数是没有参数列表的
2.shell中的函数也没有返回值类型
3.shell中定义函数使用function关键字,function是可以不写的
4.shell中函数名也是标识符 要符合命名规范
5.shell中的函数如果没有被调用 是不会执行的
17.2 函数的调用
没有参数:函数名
有参数的:函数名 参数1 参数2 ... 用空格分隔
17.3 函数内部使用参数
函数内部通过位置变量来使用参数
$0 脚本名
$1~$9 调用函数时给函数传递的第1个到第9个参数
${10} 9个以上的 必须要用 {}
如果是将命令行的所有参数传递给函数
$@ $* "$@" 将命令行的参数原封不动的逐个的作为参数传递给函数
"$*" 将命令行的参数作为一个整体传递给函数
在函数内部使用 $1 获取到的就是命令行的所有参数
17.4 函数的返回值
1.shell中的变量默认都是全局的,函数内部定义的变量在任何位置都可以访问,只要不加local关键字修饰的就可以,所以可以使用全局变量来处理函数的返回值;
2.可以使用return的方式来返回函数运行的结果,但是要注意,使用return返回的值 只能是 [0,255] 范围内的函数的调用处可以使用 $? 来接收函数的返回值
3.可以使用echo来返回函数运行的结果,在调用处调用函数需要用命令置换符 `` $(),而且要注意 函数中所有的echo都会当做返回值处理,所以这种写法一般只允许函数中有一个 echo。