1 编写脚本
每个脚本的开头都使用"#!",这意味着告诉你的系统这个文件的执行需要指定一个解释器.#!实际上是一个2字节的魔法数字,这是指定一个文件类型的特殊标记, 在#!之后接着是一个路径名.这个路径名指定了一个解释脚本中命令的程序,这个程序可以是shell,程序语言或者是任意一个通用程序.这个指定的程序从头开始解释并且执行脚本中的命令(从#!行下边的一行开始),忽略注释
如:
1 #!/bin/sh
2 #!/bin/bash
3 #!/usr/bin/perl
4 #!/usr/bin/tcl
5 #!/bin/sed -f
6 #!/usr/awk -f
编写完脚本之后,你可以使用sh scriptname,[5]或者bash scriptname来调用它.
(不推荐使用sh <scriptname,因为这禁用了脚本从stdin中读数据的功能.)
更方便的方法是让脚本本身就具有可执行权限,通过chmod命令可以修改.
比如:
chmod 555 scriptname (允许任何人都具有 可读和执行权限)
chmod +rx scriptname (允许任何人都具有 可读和执行权限)
chmod u+rx scriptname (只给脚本的所有者 可读和执行权限)
既然脚本已经具有了可执行权限,现在你可以使用./scriptname.来测试它了.如果这个脚本以一个"#!"行开头,那么脚本将会调用合适的命令解释器来运行.
最后一步,在脚本被测试和debug之后,你可能想把它移动到/usr/local/bin(当然是以root身份) ,来让你的脚本对所有用户都有用.这样用户就可以直接敲脚本名字来运行了
为什么不直接使用scriptname来调用脚本?
如果你当前的目录下($PWD)正好有你想要执行的脚本,为什么它运行不了呢?失败的原因是,出于安全考虑,当前目录并没有被加在用户的$PATH变量中.
因此,在当前目录下调用脚本必须使用./scriptname这种形式. 二元比较操作符,比较变量或者比较数字.注意数字与字符串的区别.
3 比较操作
整数比较
-eq 等于,如:if [ "$a" -eq "$b" ]
-ne 不等于,如:if [ "$a" -ne "$b" ]
-gt 大于,如:if [ "$a" -gt "$b" ]
-ge 大于等于,如:if [ "$a" -ge "$b" ]
-lt 小于,如:if [ "$a" -lt "$b" ]
-le 小于等于,如:if [ "$a" -le "$b" ]
< 小于(需要双括号),如:(("$a" < "$b"))
<= 小于等于(需要双括号),如:(("$a" <= "$b"))
> 大于(需要双括号),如:(("$a" > "$b"))
>= 大于等于(需要双括号),如:(("$a" >= "$b"))
字符串比较
= 等于,如:if [ "$a" = "$b" ]
== 等于,如:if [ "$a" == "$b" ],与=等价
注意:==的功能在[[]]和[]中的行为是不同的,如下:
1 [[ $a == z* ]] # 如果$a以"z"开头(模式匹配)那么将为true
2 [[ $a == "z*" ]] # 如果$a等于z*(字符匹配),那么结果为true
3
4 [ $a == z* ] # File globbing 和word splitting将会发生
5 [ "$a" == "z*" ] # 如果$a等于z*(字符匹配),那么结果为true
一点解释,关于File globbing是一种关于文件的速记法,比如"*.c"就是,再如~也是. 但是file globbing并不是严格的正则表达式,虽然绝大多数情况下结构比较像.
< 小于,在ASCII字母顺序下.如:
if [[ "$a" < "$b" ]]
if [ "$a" \< "$b" ]
注意:在[]结构中"<"需要被转义.
> 大于,在ASCII字母顺序下.如:
if [[ "$a" > "$b" ]]
if [ "$a" \> "$b" ]
注意:在[]结构中">"需要被转义.
-z 字符串为"null".就是长度为0.
-n 字符串不为"null"
注意:
使用-n在[]结构中测试必须要用""把变量引起来.使用一个未被""的字符串来使用! -z 或者就是未用""引用的字符串本身,放到[]结构中
虽然一般情况下可以工作,但这是不安全的.习惯于使用""来测试字符串是一种好习惯
混合比较
-a 逻辑与
exp1 -a exp2 如果exp1和exp2都为true的话,这个表达式将返回true
-o 逻辑或
exp1 -o exp2 如果exp1和exp2中有一个为true的话,那么这个表达式就返回true
这与Bash的比较操作符&&和||很相像.在[[]]中使用它.
1 [[ condition1 && condition2 ]]
-o和-a一般都是和test命令或者是[]一起工作.
1 if [ "$exp1" -a "$exp2" ]
4 变量替换、赋值
$ 变量替换操作符
只有在变量被声明,赋值,unset或exported或者是在变量代表一个signal的时候, 变量才会是以本来的面目出现在脚本里.
变量在被赋值的时候,可能需要使用"=", read状态或者是在循环的头部. 在""中还是会发生变量替换,这被叫做部分引用,或叫弱引用.
而在''中就不会发生变量替换,这叫做全引用,也叫强引用.
注意:$var与${var}的区别,不加{},在某些上下文将引起错误,为了安全,使用${var}
= 赋值操作符(前后都不能有空白)
不要与-eq混淆,那个是test,并不是赋值. 注意,=也可被用来做test操作,这依赖于上下文
Bash变量是不分类型的
不像其他程序语言一样,Bash并不对变量区分"类型".本质上,Bash变量都是字符串. 但是依赖于上下文,Bash也允许比较操作和算术操作.
决定这些的关键因素就是,变量中的值是否只有数字
特殊的变量类型
local variables
这种变量只有在代码块或者是函数中才可见
environmental variables
这种变量将改变用户接口和shell的行为.
在一般的上下文中,每个进程都有自己的环境,就是一组保持进程可能引用的信息的变量.这种情况下,shell于一个一般进程是相同的.
每次当shell启动时,它都将创建自己的环境变量.更新或者添加新的环境变量,将导致shell更新它的环境,同时也会影响所有继承自这个环境的所有子进程(由这个命令
导致的).
注意:分配给环境变量的空间是受限的.创建太多的环境变量将引起空间溢出,这会引起问题.
如果一个脚本设置了环境变量,需要export它,来通知本脚本的环境脚本只能对它产生的子进程export变量.
一个从命令行被调用的脚本export的变量,将不能影响调用这个脚本的那个命令行shell的环境.
positional parameters
就是从命令行中传进来的参数,$0, $1, $2, $3...
$0就是脚本文件的名字,$1是第一个参数,$2为第2个...,以后就需要打括号了,如${10},${11},${12}...
两个值得注意的变量$*和$@,表示所有的位置参数.
引用变量
在一个双引号中直接使用变量名,一般都是没有问题的.它阻止了所有在引号中的特殊字符的重新解释--包括变量名--但是$,`和\除外.保留$,作为特殊字符的意义,是为了能够在双
引号中也能够正常地引用变量("$var").这样在""中可以使用变量所表达的值
使用""来防止单词分割.[4]如果在参数列表中使用双引号,将使得双引号中的参数作为一个参数.即使双引号中的字符串包含多个单词(也就是包含空白部分),也不会变为多个参数
单引号操作总体上和""很像,但不允许引用变量.因为$的特殊含义被关闭了.在''中除了',其他字符都没有特殊的含义了.所以单引号比双引号严格
转义
转义是一种引用单个字符的方法.一个具有特殊含义的字符前边放上一个转义符(\)就告诉shell 这个字符失去了特殊的含义.
值得注意的是,在某些特定的命令和工具中,比如echo和sed,转义符往往会起到相反的效果, 它反倒有可能引发出这个字符特殊的含义.
对于特定的转义符的特殊的含义
在echo和sed中所使用的
\n 意味着新的一行
\r 回车
\t tab键
\v vertical tab(垂直tab),查前边的Ctl-K
\b backspace,查前边的Ctl-H
\a "alert"(如beep或flash)
\0xx 转换成8进制ASCII解码,等价于oxx
5 退出和退出状态
exit命令被用来结束脚本,就像C语言一样.他也会返回一个值来传给父进程,父进程会判断是否可用.
每个命令都会返回一个exit状态(有时候也叫return状态).成功返回0,如果返回一个非0值,通常情况下都会被认为是一个错误码.一个编写良好的UNIX命令,程序,和工具都会返回一个0作为退出码来表示成功,虽然偶尔也会有例外.
同样的,脚本中的函数和脚本本身都会返回退出状态.在脚本或者是脚本函数中执行的最后的命令会决定退出状态.
在脚本中,exit nnn命令将会把nnn退出码传递给shell(nnn必须是10进制数0-255).
当一个脚本以不带参数exit来结束时,脚本的退出状态就由脚本中最后执行命令来决定.
$?读取最后执行命令的退出码.函数返回后,$?给出函数最后执行的那条命令的退出码.这种给函数返回值的方法是Bash的方法.对于脚本来说也一样.总之,一般情况下,0为成功,非0失败W.