1.初步了解:
Shell 就是贝壳的意思,内部是系统/内核, 外部则是用户。对于Shell 就是用于与内核之间的一个媒介。
shell 脚本的意义:
- 如果实现同样的一个功能, 使用Shell脚本会比使用C语言简单快捷
- 脚本中同样也是可以使用 变量、条件、计算、循环等等结构,相对与C语言来说比较简单
- Shell 脚本中 上一个命令的输出可以作为下一个命令的输入
第一个Shell 脚本:
#!/bin/bash # 告诉系统需要使用 /bin/bash 解析器来解析后面的所有命令
echo "Hello" # 相当于C语言中的printf 输出一个字符串到屏幕(终端 )中
注意:
- 第一行必须是 #!/bin/bash 为了告诉系统用使用bash 解析器来解析命令
- 除了使用/bin/bash 以外还可以使用 awk / sed
- Shell 脚本并不是什么复杂的程序, 在使用之前不需要提前编译程序
- 文件后缀 C语言的后缀.c 而Shell 脚本的后缀是 .sh
2.变量
Shell 脚本是一种弱类型语言,在脚本当中使用变量不需要也无法指定变量的“类型”。缺省状态下,Shell 脚本的变量都是字符串,即一连串的单词列表。
定义与赋值:
myname="Michael Jackson"
注意:
- 等号两边是不允许出现空格的
- 双引号用来强调, 内部是一个完整的个体 (中间出现空格), 不然会被误会为一个命令
3.变量引用:
$ echo $myname
变量的种类
Shell 脚本中有这么几种变量:
A 普通的用户自定义变量,比如上面的 myname。
B 系统预定义好的环境变量,比如 PATH。
C 命令行变量,比如$#、$*等。
1,$# :代表命令行参数个数
2,$* :代表所有的参数
3,$@ :同上
4,$n :第 n 个参数
Shell 脚本中还有几个跟命令行变量形式很类似的特殊变量,他们是:
1,$? :代表最后一个命令执行之后的返回值
2,$$ :代表当前 Shell 的进程号 PID
4.特殊符号们:
引号:
双引号 “ ”
双引号的作用是将一些“单词”括起来形成单个的“值”。
在此变量的定义中如果没有双引号将会报错,因为这个字符串有两个单词,第二个单词会被认为是一个命令,但显然不对,因为 Jackson 不是命令而只是 myname 的一部分
单引号 ‘ ’
在单引号中所有的字符串都是字符串本意, 不能起到 引用、 命令的效果
反引号(抑音符) ` `
在双引号中可以直接解析一个命令
竖杠|(管道)
可以用来连接另个命令 , 把上一个命令的输出信息写入都管道文件中, 并把管道文件中的数据传递给下一个命令作为他的输入。
$ ls -l | wc // ls -l的输出被转移都 wc当成他的输入
大于号> 和小于号
每一个进程在刚开始运行的时候,系统都会为他们默认地打开了三个文件,他们分别是
标准输入、标准输出、标准出错,其文件描述符和对应设备关系如下图所示
这三个标准文件对应两个硬件设备:标准输入是键盘,标准输出和标准出错是显示器(是
的,显示器设备被打开了两次,第一次打开为行缓冲类型的标准输出,第二次打开为不缓冲
类型的标准出错)。绝大多数的 Shell 命令,默认的输入输出都是这三个文件
把ls命令所输出的内容 从 1描述符 重定向输出到 a.txt 中
ls 1> a.txt
ls -l abc.c > a.txt // 假设abc.c文件不存在, 那么输出到 a.txt的内容为空
ls -l abc.c 2> a.txt // 假设abc.c文件不存在, ls 则执行出错, 因此他的输出则是标准出错文件
// 所以 把原本需要输出都 2 号文件的内容 重定向为 a.txt
5.字符串处理:
1,计算一个字符串的字符个数:
:~$ str="Hello Gz2075"
:~$ echo "${#str}"
通配符:
* 代表任意长度的任意字符
? 代表一个长度的任意字符
[a-z] 代表一个长度 , 在a-z至之间的一个字符
[az] 代表一个长度,只能匹配 a 或者 z
[^az] 代表一个长度,不在 a 或 z 之内的字符(a和z以外的字符)
% 从右向左尽可能少的匹配字符
%% 从右向左尽可能多的匹配字符 (贪婪匹配)
# 从左向右尽可能少的匹配字符
## 从左向右尽可能多的匹配字符 (贪婪匹配)
"删除"(返回)字符串左边部分:
#!/bin/bash
str=HelloGz2075hellogz2069asdf2075ljklkj2075HelloEven
tet="${str#*2075}" #从左往右尽可能少 匹配
abc="${str##*2075}" #从左往右尽可能多 匹配
echo $str #HelloGz2075hellogz2069asdf2075ljklkj2075HelloEven
echo $abc #HelloEven
echo $tet #hellogz2069asdf2075ljklkj2075HelloEven
"删除"(返回)字符串右边部分:
#!/bin/bash
str=HelloGz2075hellogz2069asdf2075ljklkj2075HelloEven
tet="${str%2075*}" #从右往左尽可能少 匹配
abc="${str%%2075*}" #从右往左尽可能多 匹配
echo $tet #HelloGz2075hellogz2069asdf2075ljklkj
echo $abc #HelloGz
测试语句
专门用来实现所谓的测试语句,测试语句可以测试很多不同的情形
语句 | 含义 | 说明 | |
test -e file | 判断文件file是否存在 | 存在返回0,否则返回1 | |
test -r file | 判断文件file是否可读 | 可读返回0,否则返回1 | |
test -w file | 判断文件file是否可写 | 可写返回0,否则返回1 | |
文件 | test -x file | 判断文件file是否可执行 | 可执行返回0,否则返回1 |
test -d file | 判断文件file是否是目录 | 是目录返回0,否则返回1 | |
test -f file | 判断文件file是否是普通文件 | 是普通文件返回0,否则返回1 | |
test -s file | 判断文件file是否非空 | 非空返回0,否则返回1 | |
test s1 = s2 | 判断字符串s1和s2是否相同 | 相同返回0,否则返回1 | |
test s1 != s2 | 判断字符串s1和s2是否不同 | 不同返回0,否则返回1 | |
test s1 < s2 | 判断字符串s1是否小于s2 | s1小于s2返回0,否则返回1 | |
字符串 | test s1 > s2 | 判断字符串s1是否大于s2 | s1大于s2返回0,否则返回1 |
test -n s | 判断字符串s长度是否为非0 | s长度为非0返回0,否则返回1 | |
test -z s | 判断字符串s长度是否为0 | s长度为0返回0,否则返回1 | |
test n1 -eq n2 | 判断数值n1是否等于n2 | n1等于n2返回0,否则返回1 | |
test n1 -ne n2 | 判断数值n1是否不等于n2 | n1不等于n2返回0,否则返回1 | |
test n1 -gt n2 | 判断数值n1是否大于n2 | n1大于n2返回0,否则返回1 | |
数值 | test n1 -ge n2 | 判断数值n1是否大于等于n2 | n1大于等于n2返回0,否则返回1 |
test n1 -lt n2 | 判断数值n1是否小于n2 | n1小于n2返回0,否则返回1 | |
test n1 -le n2 | 判断数值n1是否小于等于n2 | n1小于等于n2返回0,否则返回1 | |
eq | equal | 等于 | |
ne | not equal | 不等于 | |
gt | greater than | 大于 | |
ge | greater than equal | 大于等于 | |
lt | less than | 小于 |
6.脚本语法单元
if 语句语法:
if 判断条件
then
XXXXXXX
elif 判断条件
then
aaa
fi
注意:
- 每一个 if 语句都以 fi作为结束标记
- if语句后面的条件必须为真时(0), then 后面的语句才会被执行
- if 判断条件; 与 if 判断条件 效果是一致的 ,(分号有没有都可以)
#!/bin/bash
if test $# -ne 1 #判断 命令行输入的参数个数是否正确
then
echo "参数错误~~"
fi
if test -e $1 # 判断文件是否存在
then
cat $1
else
echo "文件不存在!!"
fi
case语句 (类似于C与语言的SWITCH CASE)
语法:
case 变量 in
1) 11111 ;;
2) 22222 ;;
3) 33333 ;;
*) 44444 ;;
esac
例子, 用户输入数字脚本响应的输出 数据
#!/bin/bash
read num # 从标准输入中读取一个数据 存放到 num
case $num in
1) echo "用户输入为 1 " ;; # 注意 ;; 相当于C语言中的break
2) echo "用户输入为 2 " ;;
3) echo "用户输入为 3 " ;;
4) echo "用户输入为 4 " ;;
*) echo "用户输入为 其他 " ;;
esac # 注意结束标记跟 if 一样 返过来写 case
注意:
- num 是一个字符串,因此 1 ,2,3,4 都是字符串的形式
- 整个 case 结构必须 esac 作为结束
7.循环控制:
使用while实现输出1 到100
#!/bin/bash
declare -i n=1 # 在定义变量 n 前面加上 declare -i 表示该变量为数值而不是字符串
while [ $n -le 100 ] # -le判断 %n是否小于100
do
echo $n
n=$n+1
done
使用until 实现输出1 到100 (直到某个条件满足才退出循环)
#!/bin/bash
declare -i n=1 # 在定义变量 n 前面加上 declare -i 表示该变量为数值而不是字符串
until [ $n -gt 100 ] # -le判断 %n是否小于100
do
echo $n
n=$n+1
done
for循环语句:
#!/bin/bash
files=`ls` # 在当前目录下执行 ls,将所有的文件名保存在变量 files 中
for a in $files # 循环地将 files 里面的每个单词赋给 a,赋完则退出循环
do
if [ -f $a ] # 如果文件$a 是一个普通文件,那么就计算他的行数
then
wc -l $a # 直接通过 wc 在终端打印输出 文件的行数和 文件名
fi
done
8.函数
语法:
#!/bin/bash
func() # 不需要填写形参 函数的定义中,括号里面不能写任何东西
{
for a in $*
do
echo $a
done
return 99 # 默认返回值类型为 字符串类型
}
func Hello GZ2075 Even # 调用 func 函数 并传递参数 Hello GZ2075 Even
echo "return value = $?" # 打印输出 上一个命令的返回值 func
trap 捕获信号
trap "" INT
上面语句的含义是:当脚本收到信号 SIGINT 时,忽略该信号
#!/bin/bash
do_something()
{
echo "exit byebye!!"
}
echo $$ # 打印当前脚本文件的进程号
trap do_something EXIT # 设一个脚本正常退出的处理函数
trap do_something INT QUIT HUP # 设置捕获信号 INT QUIT HUP 并执行响应函数
# 信的 前缀 SIG 必须忽略
sleep 5 # 随眠过程中并不会响应信号