Unix/Linux上常见的Shell脚本解释器有bash、sh、csh、ksh等,习惯上把它们称作为一种Shell。我们常说有多少中Shell,其实说的是Shell脚本解释器。
几种常见的Shell
**sh**
sh是由Steve Bourne 开发,是 Bourne shell 的缩写,sh是Unix标准默认的shell。
**bash**
bash是由Brian Fox 和 Chet Ramey共同完成。是Bourne Again Shell的缩写。bash是linux标准默认的shell。
**ash**
ash是由Kenneth Almquist编写,linux中占用资源最少的一个小shell,它只包含24个内部命令,因而使用起来不是很方便。
**csh**
csh是linux中比较大的内核,它由以William Joy为代表的共计47位作者编成,共有52个内部命令。该shell其实是指向/bin/tcsh这样的一个shell,就是说csh就是tcsh。
**ksh**
ksh是由Eric Gisin编写,是Korn Shell的缩写,共有42条内部命令。该shell最大的优点是几乎和商业发行版的ksh完全兼容。这样就可以在不用花钱购买商业版本的情况下尝试商业版本的性能了。
**注意:bash是linux默认的shell,它基于Bourne Shell,吸收了csh和ksh的一些特性,bash完全兼容sh,就是说,用sh写的脚本可以不加修改的在bash中执行**
第一个Shll脚本
$ vi hello.sh #新建一个shell脚本(扩展名为sh的文件,sh代表shell,扩展名并不影响脚本的执行。也可以直接起名hello)
在脚本中输入以下代码:
#!/bin/bash # "#!"是一个约定的标记,它告诉系统需要使用哪一种Shell。
echo "Hello World!"
运行Shell的方法:
$ chmod +x hello.sh #给文件增加执行权限
$ ./hello.sh #执行脚本
$ /bin/sh hello.sh #以sh运行脚本
$ /bin/bash hello.sh #以bash运行脚本
示例:
编写一个脚本,从标准输入(stdin)中读取名字,显示在标准输出(stdout)上。
#/bin/bash
echo "What's your name?"
read NAME
echo "Hello ${NAME}"
Shell 变量
定义变量
变量名与等号直接不能有空格。变量明的命名规则需遵循如下规则:
1、首个字符必须为字母(a-z,A-Z)
2、中间不能有空格,可以使用下划线(_)
3、不能使用标点符号
4、不能使用shell里的关键字(在命令行中使用help命令可以查看保留关键字)
变量定义示例:
variableName="value"
使用变量
使用一个定义过的变量,只要在变量名前面加 $ 符号就可以了。如:
variableName="value"
echo $variableName
有些时候需要在变量名外面加上花括号帮助解释器识别变量的边界,如下面这种情况
SKILL=Ada
echo "I'm a good at ${SKILL}Script"
重新定义变量
已经定义的变量,可以被重新定义,如:
#!/bin/bash
NAME=Dave
echo "Hello ${NAME}"
NAME=Abby
echo "Hello ${NAME}"
只读变量
使用readonly命令可以将变量定义为只读变量,只读变量的值不能改变
#!/bin/bash
NAME=Dave
readonly NAME
NAME=Abby
运行脚本会报错,结果如下
./test.sh: line 5: NAME: readonly variable
删除变量
使用unset命令删除变量,变量被删除后不能再次使用;unset命令不能删除只读变量
示例:
#!/bin/bash
NAME=Dave
unset NAME
echo $NAME
运行结果不会有任何输出。
Shell特殊变量
实例
#!/bin/bash
echo "File Name : $0"
echo "First Parameter : $1"
echo "Second Parameter : $2"
echo "All Parameter : $@"
echo "All Parameter : $*"
echo "Total Number of Parameter $#"
echo "The Shell of the process ID is : $$"
运行结果:
$ ./test.sh Dave Abby
File Name : ./test.sh
First Parameter : Dave
Second Parameter : Abby
All Parameter : Dave Abby
All Parameter : Dave Abby
Total Number of Parameter 2
The Shell of the process ID is : 2752
$ echo $? #查看./test.sh 退出状态。一般情况下成功返回0,失败返回-1
0
* $* 和 $@的区别 *
很多关于shell的书本上写当它们被双引号包含时,$* 会将所有参数作为一个整体,而$@ 会将各个参数分开。但是,我在运行脚本的时候并没有发现这样的情况。我是在Ubuntu1204版本中测试的。代码如下
#!/bin/bash
echo '$*='"$*"
echo '$@='"$@"
Shell替换
命令替换
命令替换是指shell可以先执行命令,将输出结果暂时保存,在适当的地方输出
示例:
#!/bin/bash
DATE=`date`
echo "$DATE"
变量替换
示例:
#!/bin/bash
echo ${var:-"Hello"}
echo $var
echo ${var:="Hello"}
echo $var
echo ${var:+"World"}
echo $var
unset var
echo ${var:?"message"}
echo $var
结果如下:
$ ./test.sh
Hello
Hello
Hello
World
Hello
./test.sh: line 13: var: message
Shell的if语句
if语句通过关系运算符判断表达式的真假来决定执行那个分支,Shell有三种if语句。
- if … fi 语句
- if … else … fi 语句
- if … elif … else … fi 语句
if … fi 语句的语法
if [ expression ]
then
Statement(s) to be executed if expression is true
fi
**注意:**expression 和方括号之间必须有空格。否则会有语法错误
示例:
#!/bin/bash
a=10
b=20
if [ $a == $b ]
then
echo "a == b"
fi
if [ $a != $b ]
then
echo "a! = b"
fi
if … else … fi语句语法
if [ expression ]
then
Statement(s) to be executed if expression is true
else
Statement(s) to be executed if expression is not true
fi
示例:
#!/bin/bash
a=10
b=20
if [ $a == $b ]
then
echo "a == b"
else
echo "a! = b"
fi
if … elif …fi语句语法
if [ expression 1 ]
then
Statement(s) to be executed if expression 1 is true
elif [ expression 2 ]
then
Statement(s) to be executed if expression 2 is true
elif [ expression 3 ]
then
Statement(s) to be executed if expression 3 is true
else
Statements to be executed if expression not is true
fi
示例:
#!/bin/bash
a=10
b=20
if [ $a == $b ]
then
echo "a == b"
elif [ $a -gt $b ]
then
echo "a > b"
elif [ $a -lt $b ]
then
echo "a < b"
else
echo "None of the condition met"
fi
Shell运算符
bash支持很多运算符。包括算数运算符、关系运算符、布尔运算符、字符串运算符和文件测试运算符。
算数运算符
bash中实现数学运算的语法。
- 使用expr表达式计算工具。 expr 1 + 2
-
[1+2]−
((1+2))
使用expr时需要注意:
- 表达式和运算符之间要有空格。
- 完整的表达式要被“包含起来。
示例:
#!/bin/bash
a=10
b=20
echo "`expr $a + $b`"
echo $[$a+$b]
echo $((a+b))
echo "`expr $a - $b`"
echo "`expr $a \* $b`" # *号需要转义
echo $[$a*$b]
echo "`expr $a / $b`"
echo "`expr $a % $b`"
关系运算符
关系运算符只支持数字,不支持字符串。除非字符串的值是数字。
示例:
#!/bin/bash
a=10
b=20
if [ $a -eq $b ]
then
echo "a == b"
else
echo "a != b"
fi
if [ $a -ne $b ]
then
echo "a != b"
else
echo "a == b"
fi
if [ $a -gt $b ]
then
echo "a > b"
else
echo "a < b"
fi
if [ $a -lt $b ]
then
echo "a < b"
else
echo "a > b"
fi
if [ $a -ge $b ];then echo "a > b";else echo "a < b";fi
if [ $a -le $b ];then echo "a < b";else echo "a > b";fi
布尔运算符
示例:
#!/bin/bash
a=10
b=20
if [ ! $a -eq $b ]; then echo "a == b"; else echo "a != b"; fi
if [ $a -lt $b -o $b -gt $a ]; then echo "a < b"; else echo "a > b"; fi
if [ $a -le $b -a $b -ge $a ]; then echo "a < b"; else echo "a > b"; fi
字符串运算符
示例:
#!/bin/bash
a="abc"
b="edf"
if [ ! $a = $b ]; then echo "a != b"; else echo "a == b"; fi
if [ $a != $b ]; then echo "a != b"; else echo "a == b"; fi
if [ -z $a -o -z $b ]; then echo "string length is zero";
else echo "string length isn't zero"; fi
if [ -n $a -a -n $b ]; then echo "string length isn't zero"
else echo "string length is zero"; fi
if [ $a -a $b ];then echo "string length isn't zero"
else echo "string length is zero"; fi
文件测试运算符
文件测试运算符用于检测文件的各种属性
示例:
#!/bin/bash
if [ -b $1 ]; then echo "$1 is block file"; else echo "$1 is not block file"; fi
if [ -c $1 ]; then echo "$1 is str file"; else echo "$1 is not str file"; fi
if [ -d $1 ]; then echo "$1 is directory file"; else echo "$1 is not directory file"; fi
if [ -f $1 ]; then echo "$1 is ordinary file"; else echo "$1 is not ordinary file"; fi
if [ -g $1 ]; then echo "$1 SGID bit"; else echo "$1 not SGID bit"; fi
if [ -k $1 ]; then echo "$1 Sticky bit"; else echo "$1 not Sticky not bit"; fi
if [ -p $1 ]; then echo "$1 is pipe file"; else echo "$1 is not pipe file"; fi
if [ -u $1 ]; then echo "$1 SUID bit"; else echo "$1 not SUID bit"; fi
if [ -r $1 ]; then echo "$1 is read file"; else echo "$1 is not read file"; fi
if [ -w $1 ]; then echo "$1 is write file"; else echo "$1 is not write file"; fi
if [ -x $1 ]; then echo "$1 is execute file"; else echo "$1 is not execute file"; fi
if [ -s $1 ]; then echo "$1 is not zero"; else echo "$1 is zero"; fi
if [ -e $1 ]; then echo "$1 exists"; else echo "$1 not exists"; fi
运行结果:
$ ./test.sh test.sh
test.sh is not block file
test.sh is not str file
test.sh is not directory file
test.sh is ordinary file
test.sh not SGID bit
test.sh not Sticky not bit
test.sh is not pipe file
test.sh not SUID bit
test.sh is read file
test.sh is write file
test.sh is execute file
test.sh is not zero
test.sh exists
[关于SUID,SGID,Sticky的属性请参考:](http://blog.csdn.net/u011641885/article/details/45442053)
Shell中的字符串
Shell中的字符串可以用双引号,也可以用单引号,也可以不用引号。如:
#!/bin/bash
a="abc"
b=edf
c='hij'
echo $a
echo $b
echo $c
双引号与单引号的区别
单引号:单引号中的任何字符都会原样输出,单引号中的变量是无效的。单引号中不能出现单引号。
双引号:双引号中可以有变量,双引号中可以出现转义字符。
示例:
#!/bin/bash
a="abc"
b=edf
echo "$a"
echo '$b'
运行结果如下:
$ ./test1.sh
abc
$b
字符串的几种用法
#!/bin/bash
a="abc"
b=edf
echo "$a"$b""
####**字符串的拼接** 输出结果是abcedf
#!/bin/bash
a="abc"
echo ${#a}
####**获取字符串的长度**
#!/bin/bash
a="abc"
echo ${a:1:2}
####**提取字符串** 输入结果是bc
#!/bin/bash
a="hello world"
echo `expr index "$a" d` ##在字符串$a中查找字符d第一次出现的位子
####**查找字符或字符串**
Shell中的数组
定义数组
bash支持一维数组(不支持多维数组),并且没有限定数组的大小。数组元素的下标由0开始编号,获取数组中的元素要利用下标,下标跨越式整数或者算术表达式,其值应大于或等于0。
数组的定义有3种方式
array_name=("hello" "world") #各元素直接用空格隔开
array_name=(
hello
world) #各元素直接用换行隔开
c[0]=hello
c[2]=world #单独定义数组的各个元素
数组的读取格式
读取数组元素值的一般格式是:
${array_name[index]}
示例:
#!/bin/bash
a=("hello" "world")
b=(
hello
world)
c[0]=hello
c[2]=world
echo "${a[0]} ${a[1]}"
echo "${b[0]} ${b[1]}"
echo "${c[0]} ${c[2]}"
echo "${a[@]}" # @代表取得数组中所有元素
echo "${b[*]}" # *代表取得数组中的所有元素
echo "${#a[@]}" # 获取数组的元素个数
echo "${#a[0]}" # 获取数组中单个元素的长度
Shell的分支语句
case … esac 是shell的分支语句,匹配一个值或一个模式,如果匹配成功,执行相匹配的命令,case语句格式如下:
case 值 in
模式1)
command1
command2
command3
;;
模式2)
command1
command2
command3
;;
*)
command1
command2
command3
;;
esac
case工作方式如上所示。取值后面必须为关键字 in ,每一模式必须以右括号结束。取值可以为变量或者常数。匹配发现取值符合某一模式后,其间所有命令开始执行直至 ;;。(;; 类似C语言的break)
取值将检测匹配的每一个模式,一旦模式匹配,则执行完匹配模式相应的内容后不再继续其他模式。如果无一匹配模式,使用 * 捕获该值。在执行相对应内容。
示例:
#!/bin/bash
case $1 in
-f)
echo "FILE"
;;
-d)
echo "DIR"
;;
*)
echo "NONE"
;;
esac
运行结果:
$ ./test.sh -f
FILE
$ ./test.sh -d
DIR
$ ./test.sh
NONE
$ ./test.sh -g
NONE
Shell循环
Shell中for循环
for循环一般格式为
for 变量 in 列表
do
command1
command2
.....
done
列表是一组值(数组、字符串等)组成的序列,每个值通过空格分隔。每循环一次,就将列表中的下一个值赋给变量。
#!/bin/bash
for loop in `date`
do
echo $loop
done
运行结果:
$ ./test.sh
六
5月
2
22:32:38
HKT
2015
Shell中while循环
while循环格式
while command
do
Statement(s) to be executed if command is true
done
##command 一般为测试条件,当条件为真是执行
示例:
#!/bin/bash
while read VALUE
do
echo $VALUE
done
##从标准输入中读取字符串
Shell中until循环
until循环的格式为:
until command
do
Statement(s) to be executed if command is false
done
command 一般为条件表达式,如果返回值为false,则继续执行循环语句,否则跳出循环。
示例:
#!/bin/bash
a=0
until [ ! $a -lt 10 ]
do
echo $a
a=$[$a+1]
done
#输出数字0-9
循环中可以使用break关键字和continue关键字跳出循环,用法如C程序中的break和continue
Shell中的函数
Shell函数的定义格式如下:
function function_name (){
list of commands
[return value]
}
##关键字function 可以省略。
##函数的返回值只能是整数,一般用来表示函数是否执行成功。
##如果不加return语句,会将最后一条命令运行结果作为返回值。
##使用 $? 接收函数返回值
函数的参数:
调用函数的时可以向其传递参数。在函数体内部。通过
n的像是获取参数的值(和获取脚本的参数一样)。当n的值大于等于10时,获取参数需要加大括号,如
{11}。
函数的调用:
函数的调用只需要给出函数名即可。不需要加括号。
如果希望直接从终端调用函数,可以将函数定义在主目录下的 .profile 文件中。这样每次登陆后,在命令提示符后面输入函数名字就可以直接调用
函数可以使用unset命令删除
unset .f function_name #一定要在函数名前加上 .f 参数
示例:
#!/bin/bash
fun (){
echo $1
echo $2
return $(($1+$2))
}
fun $1 $2 #调用函数,传递参数,这里的$1和$2是shell脚本的参数
ret=$? #接收函数的返回值
echo "The sum of two numbers is $ret"
运行结果:
./test1.sh 10 20
10
20
The sum of two numbers is 30
Shell文件包含
shll可以将外部脚本的内容合并到当前脚本
shell中包含脚本格式
. filename
或者
source filename
注意:被包含的脚本不需要执行权限也可以执行。
示例:
#!/bin/bash
. ./test1.sh $1 $2
echo test1.sh run over
执行结果:
$ ll test1.sh
-rw-rw-r-- 1 gino gino 121 5月 2 23:46 test1.sh
$ cat test1.sh
#!/bin/bash
fun (){
echo $1
echo $2
return $(($1+$2))
}
fun $1 $2
ret=$?
echo "The sum of two numbers is $ret"
$ ./test2.sh 1 2
1
2
The sum of two numbers is 3
test1.sh run