Shell学习
初识shell
什么是shell
shell程序:接口,提供了用户和内核交互桥梁。
shell语言: 命令语言,编程语言。解释性语言(编译型语言) 。用于编写shell脚本。
shell解释器: sh(unix默认) bash(linux默认)
shell基本格式
1 | !/bin/bash |
shell脚本执行
指定解释器执行
sh 脚本名称
bash 脚本名称
1 | [hadoop@hadoop01 test]$ sh shell01.sh |
内部命令
source
或.
脚本名称.
:使Shell读入指定的Shell程序文件. 并依次执行文件中的所有语句(脚本调用另外脚本)source
:用于重新执行刚修改的初始化文件,使之立即生效,而不必注销并重新登录1
2
3
4[hadoop@hadoop01 test]$ . shell01.sh
aa
[hadoop@hadoop01 test]$ source shell01.sh
aa
通过路径执行
需要有执行的权限
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18[hadoop@hadoop01 test]$ ll
total 4
-rw-rw-r-- 1 hadoop hadoop 21 Aug 5 01:58 shell01.sh
# 修改权限 chmod 755 a.sh
[hadoop@hadoop01 test]$ chmod u+x shell01.sh
[hadoop@hadoop01 test]$ ll
total 4
-rwxrw-r-- 1 hadoop hadoop 21 Aug 5 01:58 shell01.sh
# 相对路径
[hadoop@hadoop01 test]$ ./shell01.sh
aa
# 绝对路径
[hadoop@hadoop01 test]$ /home/hadoop/test/shell01.sh
aa
shell注释
单行注释:shell中以
#号开头
表示多行注释:
:<<! 多行注释 !
1
2
3
4
5
6
7
8
9
10
11
12[hadoop@hadoop01 test]$ cat shell01.sh
#!/bin/bash
echo aa
# echo bb
:<<!
echo '1'
echo '2'
echo '3'
!
[hadoop@hadoop01 test]$ ./shell01.sh
aa
变量
Shell中按照变量的作用域和生命周期,Shell变量可分为四大类:
永久环境变量:需要修改配置文件,变量永久生效。
/etc/profile
:所有用户有效~/.bash_profile
: 当前用户永久有效 ,立即生效需要source /etc/profile
临时环境变量:使用
expor
t命令行声明即可,变量在shell脚本进程结束后仍然有效,但在关闭当前shell会话后失效。全局变量:在脚本中定义,仅在当前Shell脚本中有效,其他Shell脚本进程不能访本
作用域从定义的位置开始,到脚本结束或被显示删除的地方为止。
注意,全局变量既可以在Shell函数内定义,也可以在shell函数外定义
因为shell函数内定义的变量默认为global,且作用域从“函数被调用时执行变量定义的地方”开始,到脚本结束或被显示删除的地方为止。
局部变量。在shell脚本中函数内显示使用local关键字定义的变量。其作用域局限于函数内。同名local变量会屏蔽global变量。
系统的内置变量:
查看 :
set
;取出:
echo $USER
,echo $PATH
1
2
3
4
5
6
7
8# 查看内置变量
[hadoop@hadoop01 ~]$ set
BASH=/bin/bash
....
# 使用$取出
[hadoop@hadoop01 ~]$ echo $BASH
/bin/bash
自定义变量
变量名=值
:规范:
=
号附近不能用空格- 命名只能使用英文字母,数字和下划线,首个字符不能以数字开头。变量名推荐使用大写字母
- 变量名不能使用标点符号。
- 变量名不能使用bash里的关键字(可用help命令查看保留关键字)。
- 单引号: 内部不能取值,(会脱意)
- 双引号: 内部可以取值,,可以出现转义字符
例子
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23[hadoop@hadoop01 ~]$ A='this is a'
[hadoop@hadoop01 ~]$ echo $A
this is a
[hadoop@hadoop01 ~]$ B='this is b'
[hadoop@hadoop01 ~]$ echo $B
this is b
# 双引号: 内部可以取值
[hadoop@hadoop01 ~]$ echo "$A"
this is a
# 单引号: 内部不能取值
[hadoop@hadoop01 ~]$ echo '$A'
$A
# 已定义的变量,可以被重新定义
[hadoop@hadoop01 ~]$ your_name="tom"
[hadoop@hadoop01 ~]$ echo $your_name
tom
[hadoop@hadoop01 ~]$ your_name="alibaba"
[hadoop@hadoop01 ~]$ echo $your_name
alibaba使用变量的时候才加美元符(
$
),第二次赋值的时候不能写$
只读变量:使用 readonly 命令可以将变量定义为只读变量,只读变量的值不能被改变。
1
2
3
4
5
6
7
8
9
10
11
12[hadoop@hadoop01 ~]$ myUrl="https://www.google.com"
# 设置只读变量
[hadoop@hadoop01 ~]$ readonly myUrl
[hadoop@hadoop01 ~]$ myUrl="https://www.runoob.com"
-bash: myUrl: readonly variable
# 定义的时候设置只读变量(不可删除和修改)
# readonly 变量名=变量值
[hadoop@hadoop01 ~]$ readonly site='https://baidu.com'
[hadoop@hadoop01 ~]$ site='https://www.baidu.com'
-bash: site: readonly variable删除变量 :使用 unset 命令可以删除变量。语法:
unset variable_name
变量被删除后不能再次使用。unset 命令不能删除只读变量。
1
2
3
4
5
6
7
8
9
10
11
12
13[hadoop@hadoop01 ~]$ readonly site='https://baidu.com'
# 不能删除只读变量。
[hadoop@hadoop01 ~]$ unset site
-bash: unset: site: cannot unset: readonly variable
# 变量被删除后不能再次使用
[hadoop@hadoop01 ~]$ echo $A
this is a
[hadoop@hadoop01 ~]$ unset A
[hadoop@hadoop01 ~]$ echo $A
[hadoop@hadoop01 ~]$
变量案例
使用解释器和路径执行则会开启新的Shell进程来执行指定的脚本,这样的话,父进程中的变量在子进程中就无法访问,子进程也无法访问父进程内容
使用解释器和路径的方式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44# a.sh
[hadoop@hadoop01 test]$ cat a.sh
#!/bin/bash
A='A in a.sh'
echo $A
# 创建子进程读取b.sh
/home/hadoop/test/b.sh
# 使用内部命令执行,b.sh内容读到父进程
#. /home/hadoop/test/b.sh
echo $B
# b.sh
[hadoop@hadoop01 test]$ cat b.sh
#!/bin/bash
B='B in b.sh'
echo $B
echo $A
# 路径执行或使用解析器开启新的Shell进程
# a.sh中只有A变量 b.sh中只有B变量
# b.sh不能访父空间的非export中的变量
[hadoop@hadoop01 test]$ bash a.sh # ./a.sh 效果相同
A in a.sh
B in b.sh # b.sh脚本输出B的结果
#空行 b.sh输出A的结果
#空行 a.sh输出B的结果
[hadoop@hadoop01 test]$ bash b.sh # ./b.sh 效果相同
B in b.sh
#空行 b.sh中只有B变量没有A变量
#------使用一个终端运行:结果有问题--------------------
# 使用内部命令 这里会用同一个空间运行,运行后就会有A或者B,会产生下面的效果
# 实际上需要开二个终端分别运行a.sh 和 b.sh,实际只有二行
[hadoop@hadoop01 test]$ . a.sh # source a.sh 效果相同
A in a.sh
B in b.sh
B in b.sh
# 实际上需要开二个终端分别运行a.sh 和 b.sh,实际只有一行
[hadoop@hadoop01 test]$ . b.sh # source b.sh 效果相同
B in b.sh
A in a.sh
[hadoop@hadoop01 test]$使用内部命令的方式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38[hadoop@hadoop01 test]$ pwd
/home/hadoop/test
[hadoop@hadoop01 test]$ cat a.sh
#!/bin/bash
A='A in a.sh'
echo $A
# 使用内部命令执行,b.sh内容读到父进程
. /home/hadoop/test/b.sh
echo $B
[hadoop@hadoop01 test]$ cat b.sh
#!/bin/bash
B='B in b.sh'
echo $B
echo $A
# 解释器的方式: b.sh在a.sh空间中运行
[hadoop@hadoop01 test]$ bash a.sh # ./a.sh
A in a.sh
B in b.sh
A in a.sh
B in b.sh
# b.sh中只有B变量
[hadoop@hadoop01 test]$ bash b.sh # ./b.sh
B in b.sh
#------使用一个终端运行:结果有问题--------------------
# 内部命令: a.sh和b.sh 都在当前进程中运行
[hadoop@hadoop01 test]$ source a.sh # . a.sh
A in a.sh
B in b.sh
A in a.sh
B in b.sh
# 需要重新开终端测试,
# 否则a.sh和b.sh 都在当前进程中运行,A和B变量都能被二个脚本访问,实际只有B这一行
[hadoop@hadoop01 test]$ source b.sh # . b.sh
B in b.sh
A in a.sh使用内部命令执行总结:
- a.sh 直接调用b.sh ,会让b.sh 在a.sh 所在的子空间中执行
- 子进程空间只能访问父进程中用export定义的变量
- 一个shell进程无法将自己定义的变量提升到父进程空间中去
- source 或 “.”执行脚本的时候会让脚本在调用者所在的shell进程的空间中执行
export命令
使用export命令我们申明的是临时环境变量,在当前shell会话中,所有的shell实例都可以访问由export命令申明的临时环境变量。
因为当前shell会话中的所有shell实例,都是当前shell会话的子进程,所以可以与父进程一同访问环境变量。
hell变量可分为四大类:
- 永久环境变量
- 临时环境变量
- 全局变量
- 局部变量
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20[hadoop@hadoop01 test]$ cat c.sh
#!/bin/bash
name='Lily'
# 让d.sh在c.sh空间运行
bash d.sh
[hadoop@hadoop01 test]$ cat d.sh
#!/bin/bash
echo $name
# 不使用export命令,取不到结果
[hadoop@hadoop01 test]$ bash c.sh
# 使用export命令后,d.sh可以读到dinginess的临时变量
[hadoop@hadoop01 test]$ cat c.sh
#!/bin/bash
export name='tom'
#name='Lily'
# 让d.sh在c.sh空间运行
bash d.sh
[hadoop@hadoop01 test]$ bash c.sh
tom
反引号赋值
先将反引号内结果运算完毕,然后赋值,也可以用
$()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15# 反引号赋值
[hadoop@hadoop01 test]$ DATE1=`date`
[hadoop@hadoop01 test]$ echo $DATE1
Wed Aug 5 04:10:19 CST 2020
# $()赋值
[hadoop@hadoop01 test]$ DATE2=$(date)
[hadoop@hadoop01 test]$ echo $DATE2
Wed Aug 5 04:10:53 CST 2020
# 输出家目录下的文件
[hadoop@hadoop01 test]$ B=`ll ~/`
[hadoop@hadoop01 test]$
[hadoop@hadoop01 test]$ echo $B
total 404020 drwxrwxr-x. 3 hadoop hadoop 26 Aug 5 03:40 apps drwxrwxr-x 3 hadoop hadoop 24 Jul 31 00:06 data -rw-rw-r--. 1 hadoop hadoop 218720521 Jul 16 14:40 hadoop-2.7.7.tar.gz -rw-rw-r--. 1 hadoop hadoop 194990602 Jun 4 2019 jdk-8u211-linux-x64.tar.gz drwxrwxr-x 2 hadoop hadoop 72 Aug 5 04:01 test
${}取变量其他技巧
${var:-aa}
:没有值时输出aa${var:+aa}
:有值时输出aa${var:=aa}
:没有值时赋值为aa${var:?aa}
:检测变量是否有值,var为空或者被删除(unset),message会送到标准输出,出现在shell脚本中,脚本会停止运行
1 | [hadoop@hadoop01 test]$ A='百度' |
特殊变量
脚本执行的时候需要传递参数,参数用空格隔开,尽量使用${}
特殊变量列表
变量表达式 含义 $0 当前脚本的文件名 $n 传递给脚本或函数的参数。n 是一个数字,表示第几个参数。例如,第一个参数是$1,第二个参数是$2。 $# 传递给脚本或函数的参数个数。 $* 传递给脚本或函数的所有参数。 $@ 传递给脚本或函数的所有参数。被双引号(“ “)包含时,与 $* 稍有不同,下面将会讲到。 $? 上个命令的退出状态,或函数的返回值。 $$
当前 Shell 进程 ID。对于 Shell 脚本,就是这些脚本所在的进程 ID。 例子
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19[hadoop@hadoop01 test]$ cat e.sh
#!/bin/bash
echo $? # 上个命令的退出状态
echo $$ # 脚本所在的进程 ID
echo $0 # 脚本名
echo $1 # 第一个参数
echo $2 # 第二个参数
echo $# # 参数个数
echo $* # 传递给脚本或函数的所有参数
echo $@ # 传递给脚本或函数的所有参数
[hadoop@hadoop01 test]$ bash e.sh a1 a2 a3
0
2544
e.sh
a1
a2
3
a1 a2 a3
a1 a2 a3
$*和$@的区别
echo的每一句输出默认会换行
$* 和 $@ 都表示传递给函数或脚本的所有参数
没有双引号
" "
: 二者都以$1 $2 ... $n
的形式组成参数列表有引号的时候:
$*
将所有的参数作为一个整体 ,以"$1 $2 ... $n"
的形式组成一个整串$@
将所有的参数分开,以"$1" "$2" ... "$n"
的形式组成一个参数列表
1 | [hadoop@hadoop01 test]$ cat f.sh |
运算符
算术运算符:
- 在四则运算以及逻辑运算。必须保证运算符与算数之间有空格。
- 四则运算也只能借助:
let
,expr
,双括号(())
等命令完成。
expr
运算:
二边要加空格,只能计算整数
+
:加1
2
3
4
5
6
7
8
9
10
11# expr和运算符二边要加空格
[hadoop@hadoop01 ~]$ A= expr 2 + 3
5
[hadoop@hadoop01 ~]$ A=expr 2 + 3
-bash: 2: command not found
[hadoop@hadoop01 ~]$ A= expr 2+3
2+3
[hadoop@hadoop01 ~]$ A= expr 2+ 3
expr: syntax error
[hadoop@hadoop01 ~]$ A= expr 2 +3
expr: syntax error-
:减1
2
3
4
5
6
7# expr和运算符二边要加空格
[hadoop@hadoop01 ~]$ A= expr 2 - 3
-1
[hadoop@hadoop01 ~]$ A=expr 2 - 3
-bash: 2: command not found
[hadoop@hadoop01 ~]$ A= expr 2-3
2-3\*
:乘号需要转义1
2
3
4
5[hadoop@hadoop01 ~]$ A= expr 2 * 3
expr: syntax error
# 需要转义
[hadoop@hadoop01 ~]$ A= expr 2 \* 3
6/
:没有小数点,类似取模1
2
3
4[hadoop@hadoop01 ~]$ A= expr 4 / 2
2
[hadoop@hadoop01 ~]$ A= expr 3 / 2
1()
括号运算需要转义使用转移符号括起来
1
2
3[hadoop@hadoop01 ~]$ A=`expr \( 2 + 3 \) \* 4`
[hadoop@hadoop01 ~]$ echo $A
20
双括号
语法:表达式的空格要求不严格,双括号可以换成中括号[]
((表达式1,表达式2…))
特点:
- 在双括号结构中,所有表达式可以像c语言一样,如:a++,b–等。
- 在双括号结构中,所有变量可以不加入:“$”符号前缀。
- 双括号可以进行逻辑运算,四则运算
- 双括号结构 扩展了for,while,if条件测试运算
- 支持多个表达式运算,各个表达式之间用“,”分开
- 如果双括号带:$,将获得表达式值,赋值给左边变量。
四则运算
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49[hadoop@hadoop01 ~]$ a=1
[hadoop@hadoop01 ~]$ b=2
[hadoop@hadoop01 ~]$ c=3
# 双括号结构中,所有变量可以不加入:“$”符号前缀
[hadoop@hadoop01 ~]$ (($a=$a+1))
-bash: ((: 1=1+1: attempted assignment to non-variable (error token is "=1+1")
[hadoop@hadoop01 ~]$ (($a=a+1))
-bash: ((: 1=a+1: attempted assignment to non-variable (error token is "=a+1")
# 加法
[hadoop@hadoop01 ~]$ ((a=a+1))
2
[hadoop@hadoop01 ~]$ ((a=c-a))
[hadoop@hadoop01 ~]$ echo $a
2
# 减法
[hadoop@hadoop01 ~]$ ((a=c-a))
[hadoop@hadoop01 ~]$ echo $a
1
# 乘法 : 结构内的空格不固定
[hadoop@hadoop01 ~]$ ((d=2 *c))
[hadoop@hadoop01 ~]$ echo $d
6
# 除法:取模
[hadoop@hadoop01 ~]$ ((e=4/2))
[hadoop@hadoop01 ~]$ echo $e
2
[hadoop@hadoop01 ~]$ ((e=3/2))
[hadoop@hadoop01 ~]$ echo $e
1
# 自增
[hadoop@hadoop01 ~]$ ((b=++b))
[hadoop@hadoop01 ~]$ echo $b
3
# 自减
[hadoop@hadoop01 ~]$ ((--b))
[hadoop@hadoop01 ~]$ echo $b
2
# 双括号带:$,将获得表达式值,赋值给左边变量。
[hadoop@hadoop01 ~]$ e=$((1+10))
[hadoop@hadoop01 ~]$ echo $e
11
# 带括号运算
[hadoop@hadoop01 ~]$ f=$(((1+2)*4))
[hadoop@hadoop01 ~]$ echo $f
12
# 双括号可以换成中括号`[]`
[hadoop@hadoop01 ~]$ f=$[(1+2)*3]
[hadoop@hadoop01 ~]$ echo $f
9逻辑运算
1
2
3
4
5
6
7
8[hadoop@hadoop01 ~]$ a=1
[hadoop@hadoop01 ~]$ b="ab"
[hadoop@hadoop01 ~]$ echo $((a>1?8:9))
9
[hadoop@hadoop01 ~]$ ((b!="a"))&& echo "err2";
err2
[hadoop@hadoop01 ~]$ ((a<2))&& echo "ok";
ok扩展流程控制语句(逻辑关系式)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27# for运算
[hadoop@hadoop01 ~]$ num=10
[hadoop@hadoop01 ~]$ total=0
[hadoop@hadoop01 ~]$ for((i=0;i<num;i++));
> do
> ((total+=i))
> done
[hadoop@hadoop01 ~]$ echo $total
45
# while运算
[hadoop@hadoop01 ~]$ total=0
[hadoop@hadoop01 ~]$ i=0
[hadoop@hadoop01 ~]$ echo nuum
nuum
[hadoop@hadoop01 ~]$ echo $num
10
[hadoop@hadoop01 ~]$ while((i<=num))
> do
> ((total+=i,i++))
> done
# if 运算
[hadoop@hadoop01 ~]$ if((total=45))
> then
> echo "OK"
> fi
OK1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22!/bin/sh
num=100;
total=0;
for((i=0;i<=num;i++));
do
((total+=i));
done
echo $total;
total=0;
i=0;
while((i<=num));
do
((total+=i,i++));
done
echo $total;
=5050));then
echo "ok";
fi
let命令
当表达式中含有 Shell 特殊字符(例如 |)时,需要用双引号" "
或者单引号' '
将表达式包围起来。let后的表达式不能使用空格
1 | # 语法格式为: |
给变量 i 加 8:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15[c.biancheng.net]$ i=2
# let i+=8 等同于 ((i+=8)),但后者效率更高。
[c.biancheng.net]$ let i+=8
[c.biancheng.net]$ echo $i
10
# let运算不能带空格
[hadoop@hadoop01 ~]$ let i+ 5
-bash: let: i+: syntax error: operand expected (error token is "+")
[hadoop@hadoop01 ~]$ let sum= \(1+3\)*2
-bash: let: sum=: syntax error: operand expected (error token is "=")
# let带括号运算需要转义
[hadoop@hadoop01 ~]$ let sum=\(1+3\)*2
[hadoop@hadoop01 ~]$ echo $sum
8let 后面可以跟多个表达式。
1
2
3
4[c.biancheng.net]$ a=10 b=35
[c.biancheng.net]$ let a+=6 c=a+b #多个表达式以空格为分隔
[c.biancheng.net]$ echo $a $c
16 51
1 |
A=expr \( 2 + 3 \) \* 4
echo $A
A=$((2+3)): 不用管空格
A=$[2+3]: 不用管空格
let A=2:
let D=A+C :省略$,不可以使用空格
let D=A++
()
—>$((A+B))
:可以不写$符号()
—>$[A+B]
:()
—>let C=A+B
:
小数运算bc
算数运算符
1
2
3
4
5
6
7
8[hadoop@hadoop01 ~]$ res= expr 2.3 + 3.5
expr: non-integer argument
[hadoop@hadoop01 ~]$ let res=2.3+3.5
-bash: let: res=2.3+3.5: syntax error: invalid arithmetic operator (error token is ".3+3.5")
[hadoop@hadoop01 ~]$ echo $[2.3+3.5]
-bash: 2.3+3.5: syntax error: invalid arithmetic operator (error token is ".3+3.5")
[hadoop@hadoop01 ~]$ echo $((2.3+3.5))
-bash: 2.3+3.5: syntax error: invalid arithmetic operator (error token is ".3+3.5")bc命令
[hadoop@hadoop01 ~]$ sudo yum install -y bc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23[hadoop@hadoop01 ~]$ echo "2.3+3.5"
2.3+3.5
# 浮点数运算
[hadoop@hadoop01 ~]$ echo "2.3+3.5" | bc
5.8
# 比较运算: 非0为真(true 为1 ,false 为0)
[hadoop@hadoop01 ~]$ echo "1<2" | bc
1
# 设置小数点位数: (3.33 保留二位小数)
[hadoop@hadoop01 ~]$ echo "scale=2;10/3" | bc
3.33
# 设置进制: (将二进制111转为10进制 7)
[hadoop@hadoop01 ~]$ echo "ibase=2;111" | bc
7
# 进制转换: (将10进制的10转为8进制 12)
[hadoop@hadoop01 ~]$ echo "ibase=10;obase=8;10" | bc
12
# 高级运算:(计算3的3次幂)
[hadoop@hadoop01 ~]$ echo "3^3" | bc
27
# (4的开方)
[hadoop@hadoop01 ~]$ echo "sqrt(4)" | bc
2
三元运算符
[ condition ] && 真 || 假
,[ ]
表达式内两侧必须有空格
1 | # 1>=2 ? true : false |
关系运算符
运算符 | 等同运算符 | 说明 |
---|---|---|
-eq | = | 检测二个数相等 ,相等返回true |
-nq | != | 检测二个数不相等,不相等返回true |
-ge | >= | 检测左边大于等于右边,是true |
-gt | > | 检测左边大于右边,是true |
-le | <= | 检测左边小于等于右边,是true |
-lt | < | 检测左边小于右边,是true |
布尔运算法
运算符 | 等同运算符 | 说明 |
---|---|---|
-a | && | 与运算,二者为true,返回true |
-o | || | 或运算,有一个表达式为true,则返回true; |
! | ! | 非运算,表达式为true则返回false,否则返回true; |
1 | # 1>=2 |
脚本实例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
###本脚本主要写于20190905
###本脚本主要用于逻辑运算符
if [ ! $1 ]
then
echo "==============该脚本运行格式: sh $0 变量1 变量2"
break
else
if [ ! $2 ]
then
echo "==============该脚本运行格式: sh $0 变量1 变量2"
break
else
echo "请输入你想要输入的数据:"
read file
####获取输入的变量无法判断输入的类型,导致输入字符串类型会导致脚本报错
###非运算
if [ $1 != $2 ]
then
echo "变量a不等于变量b"
else
echo "变量a等于变量b"
fi
###与运算
if [ $1 -lt $file -a $2 -gt $file ]
then
echo "变量a小于$file且变量b大于$file结果为真"
else
echo "变量a小于$file且变量b大于$file结果为假"
fi
###或运算
if [ $1 -lt $file -o $2 -gt $file ]
then
echo "变量a小于$file或者变量b大于$file结果为真"
else
echo "变量a小于$file或者变量b大于$file结果为假"
fi
fi
fi
字符串运算符
运算符 | 说明 | 举例 |
---|---|---|
= | 检测两个字符串是否相等,相等返回 true。 | [ $a = $b ] 返回 false。 |
!= | 检测两个字符串是否相等,不相等返回 true。 | [ $a != $b ] 返回 true。 |
-z | 检测字符串长度是否为0,为0返回 true。测试字符串是否是空串 | [ -z $a ] 返回 false。 |
-n | 检测字符串长度是否为0,不为0返回 true。 | [ -n $a ] 返回 true。 |
str | 检测字符串是否为空,不为空返回 true。存在为真 | [ $a ] 返回 true。 |
1 | [hadoop@hadoop01 ~]$ str1='abc' |
[]
,[[]]
,{}
,''
,""
区别
引用的概念。所谓引用是指将字符串用某种符号括起来,以防止特殊字符被解析为其他意思。而在Shell中一共有4种引用符,分别是双引号、单引号、反引号和转义符。双引号又叫“部分引用”或“弱引用”,可以引用除$符、反引号、转义符之外的所有字符;单引号又叫“全引用”或“强引用”,可以引用所有字符;反引号则会将反引号括起的内容解释为系统命令。
[]
:单中括号是比较基本的变量计算及数值比较的方法,一般情况下已经足够使用;- 单括号是对一段比较长的命令进行合并,单括号中的命令用-0或-a来进行衔接;
[[]]
:双中括号是扩展的数值比较方法,里面的数值计算也相对来说复杂些。推荐大家平常工作中使用单中括号即可,满足日常的工作,不做运维开发的话,双括号方面涉及不多。双中括号就复杂多了,一半而言,涉及变量引用的话 ,双括号可以在数值计算中引用
单引号与双引号的最大不同在于双引号仍然可以保有变量的内容,但单引号内仅能是一般字符 ,而不会有特殊符号。
单引号比较笨一点,它不会将引号内的内容像变量一类的进行转换,举个例子:
用echo显示变量:
1
2
3
4[hadoop@hadoop01 ~]$ echo "$JAVA_HOME"
/usr/local/jdk1.8.0_211
[hadoop@hadoop01 ~]$ echo '$JAVA_HOME'
$JAVA_HOME
双引号:部分引用是指用双引号括起来的引用,特殊字符会被解析为特殊意义,如上面的“JAVA_HOME”,
- 打印该变量的时候,需要将它们用双引号括起来
单引号: 全部引用是指用单引号括起来的引用。单引号中的任何字符都只当作是普通字符(除了单引号本身,也就是说单引号中间无法再包含单引号,即便用转义符转义单引号也不行)
所有在单引号中的字符都只能代表其作为字符的字面意义。
全引用括起的字符串中还含有单引号会出现问题
1
2
3
4
5
6
7
8
9
10
11# 因为Shell无法区分哪个单引号是引用的结束符,就像下面显示的一样:
[root@localhost ~]# echo 'it's a dog'
>
# 解决这个问题,可以采取如下两种方式:
# 1. 转义单引号
[root@localhost ~]# echo 'it'\''s a dog'
it's a dog
# 2.使用双引号
[root@localhost ~]# echo "it's a dog"
it's a dog
无引号:把内容输出出来,但可能不会将含有空格的字符串视为一个整体输出,
- 如果内容中有命令,变量等,会先把变量,命令解析结果,然后再输出最终内容来,
- 如果字符串中带有空格等特殊字符,则不能完整的输出,需要改加双引号
花括号:
以逗号分割的文件列表进行拓展,
touch {a,b}.txt
结果为a.txt b.txt。对大括号中以点点(..)分割的顺序文件列表起拓展作用,如:
touch {a..d}.txt
结果为a.txt b.txt c.txt d.txt代码块:大括号内的命令不会新开一个子shell运行,即脚本余下部分仍可使用括号内变量
括号内的命令间用分号隔开,最后一个也必须有分号。
1
2
3
4${var:-string}
${var:+string}
${var:=string}
${var:?string}
文件运算符
运算符 | 说明 |
---|---|
-d | 检测是否是目录,是true |
-f | 检测是否为普通文件,不是目录和设备文件,是true |
-e | 检测是否存在,是true |
-s | 检测文件是否不为空,(文件大小是否大于0),不为空true |
-r | 是否可读,是true |
-w | 是否可写,是true |
-x | 是否可执行,是true |
-b | 是否为块设备文件,是true |
-c | 是否是字符设备文件 |
1 | [hadoop@hadoop01 test]$ touch a.txt |
流程控制语句
if
read -p
相当于Scanner,读取控制台的数据1
2
3
4
5
6
7
8
9
10if [condition];
then
语句1;
elif[ condition ];
then
语句2;
...
else
语句3;
fi例子
1
2
3
4
5
6
7
8
9
10
11
12
read -p "请输入成绩:" SCORE
if [ $SCORE -gt 80 ]
then echo "优秀"
elif [ $SCORE -gt 60 ]
then echo "良好"
else echo "一般般"
fi
[hadoop@hadoop01 test]$ . score.sh
请输入成绩:61
良好
case
1 | case $1 in |
1 | !/bin/bash |
while
1 | while [expression]/((expression)) |
1 | !/bin/bash |
for
列表是一组值(数字,字符串)组成的序列,每个值通过空格分隔,每循环一次,就将列表中的下一个值赋给变量
第一种:
1
2
3
4
5
6
7
8
9
10for 变量 in 列表
do
command
......
done
[hadoop@hadoop01 test]$ for N in 1 2 3;do echo $N ;done
1
2
3第二种:
1
2
3
4[hadoop@hadoop01 test]$ for N in {1..3};do echo $N ; done
1
2
3第三种
1
2
3
4[hadoop@hadoop01 test]$ for((i=0;i<=2;i++));do echo "welcome $i times"; done
welcome 0 times
welcome 1 times
welcome 2 times例子
1
2
3
4
5
6
7
8//求一百的和
#!/bin/bash
result=0
for((i=1;i<=100;i++))
do
let result=result+i
done
echo $result
until
- expression 一般为条件表达式,如果返回false 则继续执行循环体,否则跳出循环,为true是跳出循环,和while相反
1 | until expression((条件))/[] |
1 | !/bin/bash |
九九乘法表
1 | !/bin/bash |
数组
- 使用小括号 ,数组内的元素用空格隔开
1 | 1. 数组的声明 |
1 | [hadoop@hadoop01 test]$ arr=(aa bb cc) |
数组的遍历
- 数组可能不是连续的,不能使用for(;;)的形式遍历
1 | 1. for元素循环遍历 |
函数
一段代码块
声明:
函数名(){command...}
调用:函数名
例子
1
2
3
4
5
6
7
8
9
10[hadoop@hadoop01 test]$ cat fun.sh
#!/bin/bash
hello(){
echo "hahaha..."
}
# 调用函数
hello
#运行脚本
[hadoop@hadoop01 test]$ sh fun.sh
hahaha...
带参数和返回值
1 | [hadoop@hadoop01 test]$ cat fun2.sh |
跨脚本调用函数
1 | [hadoop@hadoop01 test]$ cat other.sh |
集群自动安装软件
集群自动安装jdk
- 软件一致: httpd / yum
- 集群自动安装:
- 一台机器自动安装(写脚本 autoInstall.sh)
- 批量发送自动安装的脚本,自动执行脚本(send.sh)
准备:
传输文件到http服务器
1
2mkdir /var/www/html/jdk
cp jdk-8u73-linux-x64.tar.gz /var/www/html/jdk/编写自动下载安装的脚本
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15# autoInstall.sh
#!/bin/bash
#下载jdk
wget http://192.168.2.101/jdk/jdk-8u73-linux-x64.tar.gz
#解压jdk
tar -zxvf jdk8.tar.gz -C /opt/
#配置环境变量
cat >> /etc/profile << EOF
export JAVA_HOME=/opt/jdk8
export PATH=$PATH:$JAVA_HOME/bin/
EOF
# 执行/etc/profile
source /etc/profile
# 测试
java -version自动配置免密
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
201.先安装expect
yum -y install expect
2.执行命令并匹配
expect -c "
spawn ssh-keygen
expect {
\"(/root/.ssh/id_rsa):\" {send \"\r\";exp_continue;}
\"(empty for no passphrase):\" {send \"\r\";exp_continue;}
\"passphrase again:\" {send \"\r\";exp_continue;}
}
"
3.发送免密登录脚本
expect -c "
spawn ssh-copy-id ip
expect {
\"connecting (yes/no)?\" {send \"yes\r\";exp_continue;}
\"password:\" {send \"centos\r\";exp_continue;}
}
"具体例子
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# by liuxg
# 2019.05.15
# passwdless.sh
# echo
# -n 不要在最后自动换行
# -e 处理特殊字符
# expect是一个免费的编程工具语言,用来实现自动和交互式任务进行通信,而无需人的干预。
# expect是不断发展的,随着时间的流逝,其功能越来越强大,已经成为系统管理员的的一个强大助手。
# expect需要Tcl编程语言的支持,要在系统上运行expect必须首先安装Tcl。(源码安装要注意)
yum install expect -y #安装expect
echo "按enter键3次即可"
# 生成秘钥(按enter键3次即可生成)
ssh-keygen -t rsa
SERVERS="172.18.74.167 172.18.74.169 172.18.74.172 172.18.74.173 172.18.74.174" #需要配置的主机名
#SERVERS="m1 m2 m3 n1 n2"
PASSWORD=123456 #需要配置的主机登录密码
#将本机生成的公钥复制到其他机子上
#如果(yes/no)则自动选择yes继续下一步
#如果password:怎自动将PASSWORD写在后面继续下一步
auto_ssh_copy_id(){
expect -c "set timeout -1;
spawn ssh-copy-id $1;
expect {
*(yes/no)* {send -- yes\r;exp_continue;}
*password:* {send -- $2\r;exp_continue;}
eof {exit 0;}
}";
}
ssh_copy_id_to_all(){
for SERVER in $SERVERS #遍历要发送到各个主机的ip
do
auto_ssh_copy_id $SERVER $PASSWORD
done
}
ssh_copy_id_to_all
passwordless.sh
发送安装和免密脚本并调用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19# send.sh
#!/bin/bash
SERVICES=(192.168.2.101 192.168.2.102)
for SERVICE in ${SERVICES[*]}
do
scp -r /root/autoInstall.sh root@$SERVICE:~/
ssh root@$SERVICE sh autoInstall.sh
echo "$SERVICE..."
done
#自动发送远程调用如果不能自动退出
标准输入 0
标准输出 1
错误输出 2
/dev/null "黑洞" 只写文件
ssh root@$SERVICE > /dev/null 2>&1 <<EOF
sh autoInstall.sh
EOF
自动安装MySQL
安装mysql脚本:
1 |
|
手动第一次登陆,然后改掉密码
1
2[root@hadoop bin]# mysql -uroot -pZjVIWvOGD18bT7oX
mysql> set PASSWORD=PASSWORD('root');现在就可以写脚本链接mysql进行操作了
1
2
3
4
5
6
7
8[root@hadoop bin]# vi initMysql.sh
#!/bin/bash
mysql -uroot -proot << EOF
GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY 'root' WITH GRANT OPTION;
FLUSH PRIVILEGES;
use mysql;
select host, user, password from user;
EOF