一起学Shell Script

作为程序员, 你一定见过许多bin目录, 那么你看的懂里面写的代码吗?(一些配置, 启动, 停止等等), 那些就是Shell Script.
或许 你会使用一些常用的linux下的命令(cd pwd mv cp scp 等等)以及vim的常用操作(yy $ p h j k l)等等. 但是Shell Script可能有点不熟悉, 因为日常用的不多嘛. 正常的, 但, 不会写, 至少大致可以看得懂吧. 所以 或许这篇文章可以带你了解一点Shell脚本.

先看一个例子

一个简单的脚本文件 Hello.sh

1
2
3
4
shell
#!/bin/bash
#This is my first shell program
echo "Hello Shell Script!"

解释下咯

1
2
3
4
5
6
7
8
> 1. 第一行#!表示shell类型, 必须放在首行. 考虑可移植性, 要确认该路径在别的服务器和环境是否存在.
> 声明的shell后面不要添加任何内容, 后面的内容会当 做参数传给shell.
>
> 2. 第二行#表示shell里面的注释, 养成良好的注释习惯, 如说明脚本功能, 作者, 修改历史等等.
>
> 3. 第三行开始就是脚本的主体内容.
>
> 4. 如果你想让你的脚本像操作系统命令一样使用, 那就将脚本的存放路径追加到path路径中去即可.

执行脚本的方式

  1. sh hello.sh
  1. source hello.sh
  1. 如果要直接执行 ./hello.sh, 需要改文件的权限
chmod +x hello.sh

Shell变量

Shell变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Shell变量无需事先声明, 不严格区分类型, 默认是字符串类型, 未定义是为空串, 可以分为三类.
1. 环境变量, 全局有效.
2. 局部变量, 当前shell有效.
3. 特殊变量, 当前shell有效.

Shell变量名字, 首字符必须为字母, 变量名和值用等号连接, 变量名=值, 中间不可以有空格,
使用$变量名 或者 ${变量名} 获取变量值.
单引号里面都是普通字符, 双引号里面会保留变量的特性,用值替换,
declare声明指定类型的变量.
1. declare -a 定义数组变量.
2. declare -A 定义map变量.
3. declare -f 仅仅使用函数名.
4. declare -i 定义数值类型的变量.
5. declare -r 定义只读类型变量.
6. declare -x 将变量设置为环境变量.

Shell数组定义.

1
2
3
4
5
6
7
8
9
10
1. 定义 arr1=("a" "bb" "ccc")
2. 取值 ${arr1[1]}, ${arr1[2]}, ${arr1[3]}
3. 获取 ${#arr1}获取数组长度
4. unset arr1[1]删除第一个元素.
5. 切片 ${arr1:1:2} 获取12个元素.


1. sh -x hello.sh可以看运行时一些出错情况.

2. $((3+6)) 算术运算要用 ((计算过程))

linux中的判断表达式:

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
>判断是否是一个文件 [ -f /etc ] && echo true || echo false
>
>判断文件是否存在 [ -e /etc/hello.txt ] && echo true || echo false
>
>判断是否是一个目录 [ -d /etc ] && echo true || echo false
>
>判断文件是否可读 [ -r /etc ] && echo true || echo false
>
>判断两个值是否相等: [ 3 -eq $var1] && echo true || echo false
>
>以及判断不等 -ne, 小于: lt, 小于等于: le, 大于: gt, 大于等于: ge.
>
>以及还有:
>
>字符串比较运算符 (请注意引号的使用,这是防止空格扰乱代码的好方法)
>
>-z string 如果 string长度为零,则为真 [ -z "$myvar" ]
>
>-n string 如果 string长度非零,则为真 [ -n "$myvar" ]
>
>string1= string2 如果 string1与 string2相同,则为真 [ "$myvar" = "one two three" ]
>
>string1!= string2 如果 string1与 string2不同,则为真 [ "$myvar" != "one two three" ]



> 小练习:
>
> a = 2
>
> echo $(a=5; echo a) #这里会输出5, 但是()表示命令替换, 并不改变原来的值.
>
> echo $a # 这里仍然输出的是2. 值不改变.



> 小练习:
>
> $(( 3 + (5 > 3))) #输出结果为4
>
> [[ ! -z $f && -f $f ]] && echo true #判断文件$f是否为空, 若不为空再判断该$f是否一个普通文件, 若成立, 则输出true.

脚本中特殊的符号

1
2
3
4
5
6
7
8
9
shell
echo "script name: " $0
echo "param count: " $#
echo "param list: " $@
echo "first param: " $1
echo "second param: " $2

read -p "请输入名字:" name
echo $name " 你好"

if 条件控制

1
2
3
4
5
6
7
8
9
10
11
12
13
shell
read -p "please input a num: " num
if [[ ! -z $num && $num -lt 10 ]]; then
echo "$num is less than 10 "
elif [[ ! -z $num && $num -lt 20 ]]; then
echo "$num is less than 20 "
elif [[ ! -z $num && $num -lt 30 ]]; then
echo "$num is less than 30 "
elif [[ ! -z $num && $num -lt 40 ]]; then
echo "$num is less than 40 "
else
echo "$num is more than 40"
fi

while循环

1
2
3
4
5
6
7
8
9
10
11
12
13
shell
read -p "please input a string: " str
count=1
while [ "$str" != "quit" ]
do
echo $str
if [ $count -ge 5 ]; then
echo $count
break
fi
((count ++))
read -p "please input a string: " str
done

for循环

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
shell
for((i=1; i<=10; i++))
do
echo $i
done

for num in {1..10}
do
echo $num
done

for num in {a..z}
do
echo $num
done

for file in /etc/*.conf
do
echo $file
done

for in循环

1
2
3
4
5
shell
for s in "hello" "world" "nihao"
do
echo $s
done

seq 10 :可以生成一个序列:从1到10.

字符串常用操作

1
2
3
4
5
6
7
8
9
10
> a=hello
> 1. 字符串值 ${a}
> 2. 长度 ${#a}
> 3. 指定位置 ${a:0:2}
> 4. 从指定位置到结尾 ${a:2}
> 5. 删除从前往后指定字符, 最短匹配 ${a#h*l} #lo
> 6. 删除从前往后指定字符串, 最长匹配 ${a##h*l} #o
> 7. 如果 用%代替#则表示从后向前.
> 8. 搜索并且局部替换 ${var/search/replacement} ${a/l/a} #healo
> 9. 搜索并且全局替换 {var//seach/replacement} ${a//l/a} #heaao
1
2
3
shell
var=/home/daejong/hello.sh
echo ${var%/*} # 最短匹配, 从后向前删除, 结果为 /home/daejong, 既可以获取到文件所在文件夹的路径.
1
> 检测字符串是否为空, 或者是否是未设置.  然后根据判断设置Default值以及ERR_MSG等. 一般使用不多具体的使用时再查. 这里不多介绍.

函数的使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
shell
function fun()
{
echo "params list: "
for p in $@
do
echo $p
done

echo "first params: " $1
return 100
}

fun p1 p2 p3 p4 p5 p6
echo "fun return: " $?
1
2
3
4
5
6
7
8
9
> $@可以获取所有与参数的一个列表
>
> $1 $2 $3 获取对应的第几个参数
>
> $? 获取返回值
>
> $# 获取参数个数
>
> shift 2表示将$@的前两个参数删除, 即将参数列表向右offset两个位置.
1
2
3
4
shell
获取函数的返回值:
ret=`fun $@` # 将脚本的参数全部传给函数, 并且获取函数的返回值. 注意这里用的反单引号.
ret=$(fun $@) # 也可以用变量替换的方式调用函数并且获取函数的返回值.
1
2
3
4
5
> 注意: 在函数里面定义的变量和在脚本中定义的变量都是全局的.
>
> 但是如果你想函数中的变量不想被外部脚本访问, 就在变量前面添加 local 关键字即可.
>
> echo ${var:-"hello"} # 如果var这个变量为空, 则输出hello, 否则就输出var.

环境变量

  • 通过env或者set命令, 查看当前shell的环境变量
  • 通过${VAR}或者$VAR访问环境变量
  • 通过export命令设置环境变量., 如export JAVA_HOME=VALUE

配置文件

全局配置文件和用户级配置文件, 注意用户级配置文件可以覆盖全局配置文件.

初始化bash环境

1
2
3

1. 全局配置文件: /etc/profile, 会去加载 `/etc/profile.d/*.sh/etc/locale.conf`
2. 用户配置文件: ~/.bash_profile, 会去加载 ~/bashrc/etc/bashrc