Shell脚本的回顾

对新的编程语言学习的一些感想

最近重新温习了一下Shell,系统的学习了一遍。发现其实以前对Shell的理解与认知仅仅只是停留在马上用的层面。仔细想了想,任何一种编程语言或者是其他新知识的学习,个人觉得应该离不开几个要素。这样才能在自己学习中,建立对新知识一个系统的认知。

  1. 了解Shell的基本历史,可以很快速的浏览一遍,有个大概轮廓或印象。
  2. Shell编程中的变量、流程、语法、函数(重点)
  3. 掌握知识,并运用自如->大量的练习

重点其实是在第二点,掌握一门编程语言最基本的四个点,其实基本能上手了。至于熟练程度,那都是靠不停的使用堆砌起来的。掌握了系统的学习过程,遇到问题->解决问题,才能知其然。

shell历史

Shell目前是一种统称,UNIX系统上有多种Shell。目前各种Linux发行版标配的Shell都是bash。Shell脚本不需要编译,属于解释执行的,一行一行读取并执行这些命令。类似的解释执行的还有JS脚本、HTML等,都是一行一行顺序执行的。

已进入Linux的黑窗,敲一行命令,就刚好能执行。其实就是因为系统在启动的时候,就已经运行了一个shell解释器,等着用户的输入再来解释执行。

执行脚本

1
2
3
4
#! /bin/sh
#这是注释
cd ..
ls

#表示注释,#!表示该脚本指定的某解释器进行解释执行
编写好以后,得运行一遍吧。在Linux操作系统中,启动shell脚本有4种方式。

1
2
3
4
source ./test.sh
. ./test.sh
sh test.sh
./test.sh

以上4种方式的区别在于,source跟.是属于shell的内建命令,也就是说用这两种方式启动,是不会产生新进程的。用sh命令,则会产生在当前进程上产生一个子进程来运行该脚本。提示:权限不够。表示该新建的文件没有执行权,需要执行chmod +x test.sh,为test.sh添加执行权。

基本语法

Shell中的变量主要有两种。

变量

环境变量:以从父进程传给子进程。env命令可以直接打印查看当前系统的环境变量。
本地变量:只存在于当前Shell进程,格式是:VARNAME=value(等号之间没有空格)。可使用export命令可以把本地变量导出为环境变量。export VARNAME=value

命令代换

用一个自定义的变量名承载系统命令。例如:DATE=`date`,echo $DATE则可以实现直接敲命令date的效果。

转义字符

有些特殊意义的字符,如果只想取它字面值,可以\进行转义,用于去除紧跟其后的单个字符的特殊意义。

单引号

字符串的界定符,单引号中的内容会原样输出。
echo ‘$SHELL’则会输出$SHELL

双引号

字符串的界定符,被双引号用括住的内容,将被视为单一字串。它防止通配符扩展,但允许变量扩展。
echo “$PATH”则会输出环境变量PATH的值。作为一种好的Shell编程习惯,应该总是把变量的取值放在双引号之中。

流程

命令test或 [] 可以测试一个条件是否成立,0表示成功,1表示失败。可以用特殊变量$?读出。

分支

if/then/elif/else/fi

在Shell中用if、then、elif、else、fi这几条命令实现分支控制。

1
2
3
if [ -f ~/.bashrc ]; then
. ~/.bashrc
fi

1
2
3
4
5
6
7
8
9
10
11
#! /bin/sh
echo "Is it morning? Please answer yes or no."
read YES_OR_NO
if [ "$YES_OR_NO" = "yes" ]; then
echo "Good morning!"
elif [ "$YES_OR_NO" = "no" ]; then
echo "Good afternoon!"
else
echo "Sorry, $YES_OR_NO not recognized. Enter yes or no."
fi

read命令的作用是等待用户输入一行字符串,将该字符串存到一个Shell变量中。

case/esac

Shell脚本的case可以匹配字符串和Wildcard,每个匹配分支可以有若干条命令,末尾必须以;;结束,执行时找到第一个匹配的分支并执行相应的命令,然后直接跳到esac之后。

1
2
3
4
5
6
7
8
9
10
11
12
#! /bin/sh
echo "Is it morning? Please answer yes or no."
read YES_OR_NO
case "$YES_OR_NO" in
yes|y|Yes|YES)
echo "Good Morning!";;
[nN]*)
echo "Good Afternoon!";;
*)
echo "Sorry, $YES_OR_NO not recognized. Enter yes or no.";;
esac

循环

for/do/done

Shell脚本的for循环结构,类似于某些编程语言的foreach循环。也就是遍历。

1
2
3
4
5
#! /bin/sh
for FRUIT in apple banana pear; do
echo "I like $FRUIT"
done

while/do/done

1
2
3
4
5
6
7
8
#! /bin/sh
echo "Enter password:"
read TRY
while [ "$TRY" != "secret" ]; do
echo "Sorry, try again"
read TRY
done

函数

Shell中的函数,在其定义中是没有返回值也没有参数列表。

1
2
3
4
5
6
#! /bin/sh
foo(){ echo "Function foo is called";}
echo "-=start=-"
foo
echo "-=end=-"

位置参数和特殊变量

1
2
3
4
5
6
7
$0 相当于C语言main函数的argv[0]
$1、$2... 这些称为位置参数(Positional Parameter),相当于C语言main函数的argv[1]、argv[2]...
$# 相当于C语言main函数的argc - 1,注意这里的#后面不表示注释
$@ 表示参数列表"$1" "$2" ...,例如可以用在for循环中的in后面。
$* 表示参数列表"$1" "$2" ...,同上
$? 上一条命令的Exit Status
$$ 当前进程号

Shell脚本调试方法

1
2
3
-n 读一遍脚本中的命令但不执行,用于检查脚本中的语法错误。
-v 一边执行脚本,一边将执行过的脚本命令打印到标准错误输出。
-x 提供跟踪执行信息,将执行的每一条命令和结果依次打印出来。

$ sh -x ./test.sh

正则表达式

正则表达式分为Basic正则和Extended正则,也就是基本正则和扩展正则。Linux操作系统中,grep命令,默认使用的是基本正则。egrep命令使用的是扩展正则。

基本语法-字符类

1
2
3
4
. 匹配任意一个字符 abc.可以匹配abcd、abc9等
[] 匹配括号中的任意一个字符 [abc]可以匹配ad、bd、cd
- 在[]括号内表示字符范围 [0-9a-fA-F]可以匹配一位十六进制数字
^ 位于[]括号的开头,匹配除括号内字符之外的任意一个字符 [^xy]可以匹配a1、b1,不匹配x1、y1

基本语法-数量限定符

1
2
3
4
5
6
7
? 紧跟它前面的单元,匹配零次或一次 [0-9]?\.[0-9]匹配0.0、2.3、.5等,.号是特殊字符,用\转义一下。
+ 紧跟它前面的单元,匹配一次或多次 [a-zA-z0-9_.-]+@[a-zA-z0-9_.-]+\.[a-zA-z0-9_.-]匹配email地址
* 紧跟它前面的单元,匹配零次或多次 [0-9][0-9]*匹配至少一位数字
{N} 大括号,精确匹配N次 [0-9][0-9]{2}匹配从100到999的整数
{N,} 匹配至少N次 [0-9][0-9]{2,}匹配三位以上的整数
{,M} 匹配最多M次 [0-9]{,1}相当于[0-9]?
{N,M} 至少N次,最多M次 ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$匹配IP地址

基本语法-位置限定符

1
2
^ 匹配行首的位置
$ 匹配行末的位置

基本语法-其他特殊定符

1
2
3
\ 转义字符,普通字符转义为特殊字符,特殊字符转义为普通字符
() 将正则表达式的一部分括起来组成一个单元,可以对整个单元使用数量限定符 ([0-9]{1,3}\.){3}[0-9]{1-3}匹配IP地址
| 连接两个子表达式,表示或的关系 n(o|either)匹配no或neither
谢谢你请我吃糖果

--------- 本文结束,感谢您的审阅 ---------
0%