Shell 是“壳”的意思,就是“外壳”的“壳”,是相对于“核”而言的(“核”就是“内核”的“核”啦)。
简单来说,Shell 就是一个命令解析器,它接收用户命令,然后调用相应的应用程序。同时它又是一种程序设计语言。作为命令语言,它交互式解释和执行用户输入的命令或者自动地解释和执行预先设定好的一连串的命令;作为程序设计语言,它定义了各种变量和参数,并提供了许多在高阶语言中才具有的控制结构,包括循环和分支。
Shell的具体解释可以看看百度百科,很详细:http://baike.baidu.com/view/849.htm。
下面说正事。
一、一个简单的 Shell 示例
Shell 文件的内容可以是一串命令的列表,把需要的命令依次写上,就是一个简单的 Shell 脚本。例如新建一个 Shell 文件,名字为 moozie.sh,文件内容如下:
#!/bin/bash
cd /var/tmp/
pwd
然后用命令 chmod o+x moozie.sh 给 moozie.sh 这个文件加上执行权限,最后执行命令 ./moozie.sh ,系统就会先跑到 /var/tmp 目录下,然后显示出当前所在目录。
二、关于 Shell 脚本文件的执行方法和脚本文件第一行的 #!/bin/bash
Shell 脚本文件第一行的 #!/bin/bash 并不是注释,虽然这行以 # 开头。这行的作用是声明解释程序的路径,就是由哪个程序来解释当前这个 Shell 脚本文件。现在有两种写法,一种是 #!/bin/sh,一种是 #!/bin/bash。当然也可以不写,但是如果不写的话,执行文件时就需要用 /bin/bash [脚本文件名] 或 /bin/sh [脚本文件名] 来执行脚本文件了。
#!/bin/sh 和 #!/bin/bash 这两种写法的区别如下:
在一般的Linux系统中,sh 一般被设置成 bash 的软链接(可以通过 ll /bin/sh 命令查看一下)。使用 sh 调用执行脚本相当于打开了 bash 的 POSIX 标准模式。也就是说 /bin/sh 和 /bin/bash --posix 这两条命令效果是一样的。在 POSIX 标准模式下,当某行代码出错时,便不继续往下解释。
三、变量、表达式、控制结构
任何语言都有这三个,只是不同语言之间语法上会有所区别。
1、Shell中变量有三种:自定义变量、系统变量、命名返回值变量。
先说自定义变量。自定义变量在声明并赋值(初始化)时,直接 变量名=值 就可以了。但是在引用变量时,需要在变量前加上 $ 符号。变量在赋值的时候,等号两边都不能有空格。每行结尾也没有任何符号,换行就表示一条语句的结束。例如:
#!/bin/bash
var1=hello
var2=bash
#注意,=号两边都不要有空格
echo $var1 $var2
#执行完之后终端上就会显示 hello bash
然后说系统变量。系统变量就是系统中已经定义好的变量,可以直接拿来用。增加或修改系统变量有三种方法:修改 /etc/profile 文件,这样修改的结果对所有用户永久有效;修改用户家目录下的 .bash_profile 文件,这样修改的结果对单用户永久有效;或者可以通过 export 变量名=变量值 命令定义一个系统变量,但是这个定义是临时的,只对当前Shell有效。
系统变量可以直接拿来用,比如:
#!/bin/bash
echo $HOME
echo $USER
#可以显示出当前用户的家目录和当前用户名
常用的系统变量有这么几个:
$n | $1表示第一个参数,$2表示第二个参数,依此类推 |
---|---|
$# | 命令行参数的个数(传递到脚本的参数个数) |
$0 | 当前程序的名称 |
$? | 前一个命令或函数的返回码(显示最后命令的退出状态。0表示没有错误,其他任何值表明有错误。) |
$* | 以"参数1 参数2 ……" 形式保存所有参数(以一个单字符串显示所有向脚本传递的参数。与位置变量不同,此选项参数可超过9个) |
$@ | 以"参数1" "参数2" …… 形式保存所有参数(与$#相同,但是使用时加引号,并在引号中返回每个参数) |
$$ | 本程序的PID(脚本运行的当前进程ID号) |
$! | 上一个命令的PID(后台运行的最后一个进程的进程ID号) |
$- | 显示shell使用的当前选项,与set命令功能相同 |
PATH | 命令搜索路径,以冒号为分隔符。注意与DOS下不同的是,当前目录不在系统路径里 |
---|---|
HOME | 用户home目录的路径名,是cd命令的默认参数 |
COLUMNS | 定义了命令编辑模式下可使用命令行的长度 |
EDITOR | 默认的行编辑器 |
VISUAL | 默认的可视编辑器 |
FCEDIT | 命令fc使用的编辑器 |
HISTFILE | 命令历史文件 |
HISTSIZE | 命令历史文件中最多可包含的命令条数 |
HISTFILESIZE | 命令历史文件中包含的最大行数 |
IFS | 定义SHELL使用的分隔符 |
LOGNAME | 用户登录名 |
指向一个需要SHELL监视其修改时间的文件。当该文件修改后,SHELL将发消息Youhavamail给用户 | |
MAILCHECK | SHELL检查MAIL文件的周期,单位是秒 |
MAILPATH | 功能与MAIL类似。但可以用一组文件,以冒号分隔,每个文件后可跟一个问号和一条发向用户的消息 |
SHELL | SHELL的路径名 |
TERM | 终端类型 |
TMOUT | SHELL自动退出的时间,单位为秒,若设为0则禁止SHELL自动退出 |
PROMPT_COMMAND | 指定在主命令提示符前应执行的命令 |
PS1 | 主命令提示符 |
PS2 | 二级命令提示符,命令执行过程中要求输入数据时用 |
PS3 | select的命令提示符 |
PS4 | 调试命令提示符 |
MANPATH | 寻找手册页的路径,以冒号分隔 |
LD_LIBRARY_PATH | 寻找库的路径,以冒号分隔 |
最后是命令返回值变量。有以下代码:
#!/bin/bash
var1=`date +%y%m%d`
echo $var1
date +%y%m%d
#可以打印两行,内容一样,都是年月日
有时候,我们需要把命令的运行结果捕捉,并赋给某变量,则可以用反引号 `命令` 来实现。如 pos=`pwd`,则 pwd 命令的结果将赋给 pos 变量。
2、表达式和控制结构
表达式有“命令表达式”、“数学表达式”、“字符串表达式”、“文件判断表达式”。控制结构有if、case、for、while。
①命令表达式和 if else,有以下代码:
#!/bin/bash
if mkdir dir1
then
echo ok
else
echo fail
fi
以上代码的意思是,如果能够成功创建目录 dir1,则输出“ok”,否则输出“fail”。then 后面跟条件为真时执行的语句,else 后面跟条件为假时执行的语句。if 结束时,需要有个 fi 标记(就是把 if 翻过来)。
②数学表达式
用来比较两个数的大小关系,可以写符号也可以写单词。具体如下:-gt 是 >,-lt 是 <,-ge 是 >=,-le 是 <=,-eq 是 =,-ne 是 !=。
有以下代码:
#!/bin/bash
var1=8
var2=12
if [ $var1 -gt $var2 ]
then
echo $var1 more than $var2
elif [ $var1 -eq $var2 ]
then
echo $var1 equal $var2
else
echo $var1 less than $var2
fi
需要注意的是,在 if 后面的数学表达式中,中括号前后是要有个空格的。
③字符串表达式:用来判断两个字符串是否相等。只有两个符号,= 和 !=。格式就是 [ $var1 = $var2 ]
④文件表达式文件表达式作用:判断文件是否存在,是否可读、可写、可执行、是否是目录或普通文件等,以及用来比较两个文件的新旧顺序。格式1: [ -d/-f/-e/-r/-w/-x filename ]。
具体为:-d 文件是否存在且是目录,-f 文件是否存在且是文件,-e 是否存在,-r 是否可读,-w 是否可写,-x 是否可执行。
格式2:[ file1 -nt/-ot file2 ]
分别判断:-nt 检查 file1 是否为 file2 新,-ot 检查 file1 是否为 file2 旧。
⑤控制结构
if else 就不说了,上面有。
for 循环有两种风格。一种是 bash 风格的,一种 C 风格的。以下是个bash 风格的 for循环:
#!/bin/bash
for i in A B C D E
do
echo $i
done
#以上代码可以分别打印出A、B、C、D、E。
C 风格 for 循环的语法格式如下:
for((变量=初始值;变量<=n;变量++))
do
语句1
语句2
……
done
需要注意的是,for 循环的语句块开头要用关键词 do,结尾要用 done 包起来。以下是一个 C 风格 for 循环的示例:
#!/bin/bash
for((i=1;i<=100;i++))
sum=0
do
sum=$[ $sum + $i ]
done
echo $sum
#以上代码可以计算1到100的和
case结构的格式如下:
case 变量 in
可能性1)
语句1
语句2
;;
可能性2)
语句1
语句2
;;
*)
语句1
语句2
;;
esac
当一种可能性需要执行的语句结束时,在后面放两个分号 ;; 就可以了。而不是每行后面都放两个分号。*代表以上情况都不是时,类似于 PHP 中的 default。
Shell编程的基础大概就这么多了。