shell脚本

来源:互联网 发布:java游戏高级编程 编辑:程序博客网 时间:2024/06/07 05:28

1. shell脚本使用

考虑到Shell脚本的命令限制和效率问题,下列情况一般不使用Shell:

l  资源密集型的任务,尤其在需要考虑效率时(比如,排序,hash等等)。

l  需要处理大任务的数学操作,尤其是浮点运算,精确运算,或者复杂的算术运算(这种情况一般使用C++FORTRAN来处理)。

l  有跨平台(操作系统)移植需求(一般使用CJava)。

l  复杂的应用,在必须使用结构化编程的时候(需要变量的类型检查,函数原型,等等)。

l  对于影响系统全局性的关键任务应用。

l  对于安全有很高要求的任务,比如你需要一个健壮的系统来防止入侵、破解、恶意破坏等等。

l  项目由连串的依赖的各个部分组成。

l  需要大规模的文件操作。

l  需要多维数组的支持。

l  需要数据结构的支持,比如链表或数等数据结构。

l  需要产生或操作图形化界面GUI

l  需要直接操作系统硬件。

l  需要 I/Osocket 接口。

l  需要使用库或者遗留下来的老代码的接口。

l  私人的、闭源的应用(shell脚本把代码就放在文本文件中,全世界都能看到)。

 

2. Shell编程

打开文本编辑器,新建一个文件,扩展名为shsh代表shell),扩展名并不影响脚本执行,见名知意就好,如果你用phpshell脚本,扩展名就用php好了。
输入一些代码:

#!/bin/bash

echo"Hello World !"

#!是一个约定的标记,它告诉系统这个脚本需要什么解释器来执行,即使用哪一种Shellecho命令用于向窗口输出文本。
运行Shell脚本有两种方法。

作为可执行程序

将上面的代码保存为test.sh,并 cd 到相应目录:

chmod +x./test.sh  #使脚本具有执行权限

./test.sh  #执行脚本

注意,一定要写成./test.sh,而不是test.sh运行其它二进制的程序也一样,直接写test.shlinux系统会去PATH里寻找有没有叫test.sh的,而只有/bin, /sbin,/usr/bin/usr/sbin等在PATH里,你的当前目录通常不在PATH里,所以写成test.sh是会找不到命令的,要用./test.sh告诉系统说,就在当前目录找。
通过这种方式运行bash脚本,第一行一定要写对,好让系统查找到正确的解释器。
这里的"系统",其实就是shell这个应用程序(想象一下WindowsExplorer),但我故意写成系统,是方便理解,既然这个系统就是指shell,那么一个使用/bin/sh作为解释器的脚本是不是可以省去第一行呢?是的。

作为解释器参数

这种运行方式是,直接运行解释器,其参数就是shell脚本的文件名,如:

/bin/shtest.sh

/bin/phptest.php

这种方式运行的脚本,不需要在第一行指定解释器信息,写了也没用。

 

再看一个例子。下面的脚本使用 read 命令从stdin获取输入并赋值给 PERSON变量,最后在 stdout 上输出:

#!/bin/bash

 

# Author : mozhiyan

# Copyright (c) http://see.xidian.edu.cn/cpp/linux/

# Script follows here:

 

echo"What is your name?"

read PERSON

echo"Hello, $PERSON"

运行脚本:

chmod +x ./test.sh

$./test.sh

What is your name?

mozhiyan

Hello, mozhiyan

$

2.1 shell变量

2.1.1 定义变量

定义变量时,变量名不加美元符号($),如:

variableName="value"

注意,变量名和等号之间不能有空格,这可能和你熟悉的所有编程语言都不一样。同时,变量名的命名须遵循如下规则:

l  首个字符必须为字母(a-zA-Z)。

l  中间不能有空格,可以使用下划线(_)。

l  不能使用标点符号。

l  不能使用bash里的关键字(可用help命令查看保留关键字)。


变量定义举例:

myUrl="http://see.xidian.edu.cn/cpp/linux/"

myNum=100

2.1.2 使用变量

使用一个定义过的变量,只要在变量名前面加美元符号($)即可,如:

your_name="mozhiyan"

echo $your_name

echo ${your_name}

变量名外面的花括号是可选的,加不加都行,加花括号是为了帮助解释器识别变量的边界,比如下面这种情况:

for skillinAda Coffe Action Java

do

   echo "I am good at${skill}Script"

done

如果不给skill变量加花括号,写成echo "I am good at $skillScript",解释器就会把$skillScript当成一个变量(其值为空),代码执行结果就不是我们期望的样子了。

推荐给所有变量加上花括号,这是个好的编程习惯。

2.1.3 重新定义变量

已定义的变量,可以被重新定义,如:

myUrl="http://see.xidian.edu.cn/cpp/linux/"

echo ${myUrl}

 

myUrl="http://see.xidian.edu.cn/cpp/shell/"

echo ${myUrl}

这样写是合法的,但注意,第二次赋值的时候不能写$myUrl="http://see.xidian.edu.cn/cpp/shell/",使用变量的时候才加美元符($)。

只读变量

使用 readonly 命令可以将变量定义为只读变量,只读变量的值不能被改变。

下面的例子尝试更改只读变量,结果报错:

#!/bin/bash

 

myUrl="http://see.xidian.edu.cn/cpp/shell/"

readonly myUrl

myUrl="http://see.xidian.edu.cn/cpp/danpianji/"

运行脚本,结果如下:

/bin/sh:NAME: This variable is read only.

2.1.4 删除变量

使用 unset 命令可以删除变量。语法:

unset variable_name

变量被删除后不能再次使用;unset命令不能删除只读变量。

举个例子:

#!/bin/sh

 

myUrl="http://see.xidian.edu.cn/cpp/u/xitong/"

unset myUrl

echo $myUrl

上面的脚本没有任何输出。

2.1.5 变量类型

运行shell时,会同时存在三种变量:

1) 局部变量

局部变量在脚本或命令中定义,仅在当前shell实例中有效,其他shell启动的程序不能访问局部变量。

2) 环境变量

所有的程序,包括shell启动的程序,都能访问环境变量,有些程序需要环境变量来保证其正常运行。必要的时候shell脚本也可以定义环境变量。

3) shell变量

shell变量是由shell程序设置的特殊变量。shell变量中有一部分是环境变量,有一部分是局部变量,这些变量保证了shell的正常运行

2.2特殊变量

前面已经讲到,变量名只能包含数字、字母和下划线,因为某些包含其他字符的变量有特殊含义,这样的变量被称为特殊变量。

例如,$表示当前Shell进程的ID,即pid,看下面的代码:

$echo $$

运行结果

29949

 

 

特殊变量列表

变量

含义

$0

当前脚本的文件名

$n

传递给脚本或函数的参数。n是一个数字,表示第几个参数。例如,第一个参数是$1,第二个参数是$2。

$#

传递给脚本或函数的参数个数。

$*

传递给脚本或函数的所有参数。

$@

传递给脚本或函数的所有参数。被双引号("  ")包含时,与 $* 稍有不同,下面将会讲到。

$?

上个命令的退出状态,或函数的返回值。

$$

当前Shell进程ID。对于 Shell 脚本,就是这些脚本所在的进程ID。

 

2.2.1 命令行参数

运行脚本时传递给脚本的参数称为命令行参数。命令行参数用 $n表示,例如,$1表示第一个参数,$2表示第二个参数,依次类推。

请看下面的脚本:

#!/bin/bash

 

echo "File Name: $0"

echo "First Parameter : $1"

echo "First Parameter : $2"

echo "Quoted Values: $@"

echo "Quoted Values: $*"

echo "Total Number of Parameters : $#"

运行结果:

$./test.shZara Ali

File Name: ./test.sh

FirstParameter : Zara

SecondParameter : Ali

QuotedValues: Zara Ali

QuotedValues: Zara Ali

TotalNumber of Parameters : 2

2.2.2 $* $@的区别

$* $@都表示传递给函数或脚本的所有参数,不被双引号(" ")包含时,都以"$1" "$2" "$n"的形式输出所有参数。

但是当它们被双引号(" ")包含时,"$*"会将所有的参数作为一个整体,以"$1 $2 $n"的形式输出所有参数;"$@"会将各个参数分开,以"$1""$2" "$n"的形式输出所有参数。

下面的例子可以清楚的看到 $* $@的区别:

#!/bin/bash

echo "\$*="$*

echo "\"\$*\"=""$*"

 

echo "\$@="$@

echo "\"\$@\"=""$@"

 

echo "print each param from \$*"

for varin$*

do

   echo "$var"

done

 

echo "print each param from \$@"

for varin$@

do

   echo "$var"

done

 

echo "print each param from \"\$*\""

for varin"$*"

do

   echo "$var"

done

 

echo "print each param from \"\$@\""

for varin"$@"

do

   echo "$var"

done

执行 ./test.sh"a" "b" "c" "d",看到下面的结果:

$*=  a b c d

"$*"=a b c d

$@=  a b c d

"$@"=a b c d

printeach param from $*

a

b

c

d

printeach param from $@

a

b

c

d

printeach param from "$*"

a b c d

printeach param from "$@"

a

b

c

d

2.2.3 退出状态

$?可以获取上一个命令的退出状态。所谓退出状态,就是上一个命令执行后的返回结果。
退出状态是一个数字,一般情况下,大部分命令执行成功会返回 0,失败返回 1
不过,也有一些命令返回其他值,表示不同类型的错误。

下面例子中,命令成功执行:

$./test.shZara Ali

File Name: ./test.sh

FirstParameter : Zara

SecondParameter : Ali

QuotedValues: Zara Ali

QuotedValues: Zara Ali

TotalNumber of Parameters : 2

$echo $?

0

$


$?也可以表示函数的返回值,后续将会讲解。

 

2.3替换

如果表达式中包含特殊字符,Shell将会进行替换。例如,在双引号中使用变量就是一种替换,转义字符也是一种替换。

举个例子:

#!/bin/bash

 

a=10

echo-e "Value of a is $a \n"

运行结果:

Value of a is 10

这里 -e表示对转义字符进行替换。如果不使用 -e选项,将会原样输出:

Value of a is 10\n


下面的转义字符都可以用在 echo中:

转义字符

含义

\\

反斜杠

\a

警报,响铃

\b

退格(删除键)

\f

换页(FF),将当前位置移到下页开头

\n

换行

\r

回车

\t

水平制表符(tab键) 

\v

垂直制表符

可以使用echo命令的 -E选项禁止转义,默认也是不转义的;使用 -n选项可以禁止插入换行符。

2.3.1 命令替换

命令替换是指Shell可以先执行命令,将输出结果暂时保存,在适当的地方输出。

命令替换的语法:

`command`

注意是反引号,不是单引号,这个键位于 Esc键下方。

下面的例子中,将命令执行结果保存在变量中:

#!/bin/bash

 

DATE=`date`

echo "Date is $DATE"

 

USERS=`who|wc -l`

echo "Logged in user are $USERS"

 

UP=`date;uptime`

echo "Uptime is $UP"

运行结果:

Date isThu Jul  2 03:59:57 MST 2009

Logged inuser are 1

Uptime isThu Jul  2 03:59:57 MST 2009

03:59:57up 20 days, 14:03,  1 user,  load avg: 0.13, 0.07, 0.15

2.3.2 变量替换

变量替换可以根据变量的状态(是否为空、是否定义等)来改变它的值

可以使用的变量替换形式:

形式

说明

${var}

变量本来的值

${var:-word}

如果变量 var 为空或已被删除(unset),那么返回 word,但不改变 var的值。

${var:=word}

如果变量 var 为空或已被删除(unset),那么返回 word,并将 var的值设置为 word

${var:?message}

如果变量 var 为空或已被删除(unset),那么将消息 message送到标准错误输出,可以用来检测变量 var 是否可以被正常赋值。
若此替换出现在Shell脚本中,那么脚本将停止运行。

${var:+word}

如果变量 var 被定义,那么返回 word,但不改变 var的值。


请看下面的例子:

#!/bin/bash

 

echo${var:-"Variable is not set"}

echo"1 - Value of var is ${var}"

 

echo${var:="Variable is not set"}

echo"2 - Value of var is ${var}"

 

unset var

echo${var:+"This is default value"}

echo"3 - Value of var is $var"

 

var="Prefix"

echo${var:+"This is default value"}

echo"4 - Value of var is $var"

 

echo${var:?"Print this message"}

echo"5 - Value of var is ${var}"

运行结果:

纯文本复制

Variable is not set

1 - Value of var is

Variable is not set

2 - Value of var is Variable is notset

 

3 - Value of var is

This is default value

4 - Value of var is Prefix

Prefix

5 - Value of var is Prefix

2.4 Shell运算符

Bash支持很多运算符,包括算数运算符、关系运算符、布尔运算符、字符串运算符和文件测试运算符。

原生bash不支持简单的数学运算,但是可以通过其他命令来实现,例如 awk exprexpr最常用。

expr
是一款表达式计算工具,使用它能完成表达式的求值操作。

例如,两个数相加:

#!/bin/bash

 

val=`expr2+ 2`

echo "Total value : $val"

运行脚本输出:

Total value : 4

两点注意:

表达式和运算符之间要有空格,例如 2+2是不对的,必须写成 2 + 2,这与我们熟悉的大多数编程语言不一样。

完整的表达式要被 ` ` 包含,注意这个字符不是常用的单引号,在 Esc 键下边。

2.4.1 算术运算符

先来看一个使用算术运算符的例子:

#!/bin/sh

 

a=10

b=20

val=`expr$a+ $b`

echo "a + b : $val"

 

val=`expr$a- $b`

echo "a - b : $val"

 

val=`expr$a\* $b`

echo "a * b : $val"

 

val=`expr$b/ $a`

echo "b / a : $val"

 

val=`expr$b% $a`

echo "b % a : $val"

 

if[ $a == $b ]

then

  echo "a is equalto b"

fi

 

if[ $a != $b ]

then

  echo "a is notequal to b"

fi

运行结果:

a + b : 30

a - b : -10

a * b : 200

b / a : 2

b % a : 0

a is not equal to b

注意:

乘号(*)前边必须加反斜杠(\)才能实现乘法运算;

if...then...fi 是条件语句,后续将会讲解。

 

算术运算符列表

运算符

说明

举例

+

加法

`expr $a + $b` 结果为 30

-

减法

`expr $a - $b` 结果为 10

*

乘法

`expr $a \* $b` 结果为  200

/

除法

`expr $b / $a` 结果为 2

%

取余

`expr $b % $a` 结果为 0

=

赋值

a=$b 将把变量 b的值赋给 a

==

相等。用于比较两个数字,相同则返回 true

[ $a == $b ] 返回 false

!=

不相等。用于比较两个数字,不相同则返回 true

[ $a != $b ] 返回 true


注意:条件表达式要放在方括号之间,并且要有空格,例如 [$a==$b]是错误的,必须写成 [ $a == $b ]

2.4.2 关系运算符

关系运算符只支持数字,不支持字符串,除非字符串的值是数字。

先来看一个关系运算符的例子:

#!/bin/sh

 

a=10

b=20

if[ $a -eq $b ]

then

  echo "$a -eq $b :a is equal to b"

else

  echo "$a -eq $b: ais not equal to b"

fi

 

if[ $a -ne $b ]

then

  echo "$a -ne $b: ais not equal to b"

else

  echo "$a -ne $b :a is equal to b"

fi

 

if[ $a -gt $b ]

then

  echo "$a -gt $b: ais greater than b"

else

  echo "$a -gt $b: ais not greater than b"

fi

 

if[ $a -lt $b ]

then

  echo "$a -lt $b: ais less than b"

else

  echo "$a -lt $b: ais not less than b"

fi

 

if[ $a -ge $b ]

then

  echo "$a -ge $b: ais greater or  equal to b"

else

  echo "$a -ge $b: ais not greater or equal to b"

fi

 

if[ $a -le $b ]

then

   echo"$a -le $b: a is lessor equal to b"

else

  echo "$a -le $b: ais not less or equal to b"

fi

运行结果:

10 -eq 20: a is not equal to b

10 -ne 20: a is not equal to b

10 -gt 20: a is not greater than b

10 -lt 20: a is less than b

10 -ge 20: a is not greater or equal to b

10 -le 20: a is less or  equal to b

 

关系运算符列表

运算符

说明

举例

-eq

检测两个数是否相等,相等返回 true

[ $a -eq $b ] 返回 true

-ne

检测两个数是否相等,不相等返回 true

[ $a -ne $b ] 返回 true

-gt

检测左边的数是否大于右边的,如果是,则返回 true

[ $a -gt $b ] 返回 false

-lt

检测左边的数是否小于右边的,如果是,则返回 true

[ $a -lt $b ] 返回 true

-ge

检测左边的数是否大等于右边的,如果是,则返回 true

[ $a -ge $b ] 返回 false

-le

检测左边的数是否小于等于右边的,如果是,则返回 true

[ $a -le $b ] 返回 true

2.4.3 布尔运算符

先来看一个布尔运算符的例子:

#!/bin/sh

 

a=10

b=20

 

if[ $a != $b ]

then

  echo "$a != $b : ais not equal to b"

else

  echo "$a != $b: ais equal to b"

fi

 

if[ $a -lt 100 -a$b -gt 15 ]

then

  echo "$a -lt 100-a $b -gt 15 : returns true"

else

  echo "$a -lt 100-a $b -gt 15 : returns false"

fi

 

if[ $a -lt 100 -o$b -gt 100 ]

then

  echo "$a -lt 100-o $b -gt 100 : returns true"

else

  echo "$a -lt 100-o $b -gt 100 : returns false"

fi

 

if[ $a -lt 5 -o$b -gt 100 ]

then

  echo "$a -lt 100-o $b -gt 100 : returns true"

else

  echo "$a -lt 100-o $b -gt 100 : returns false"

fi

运行结果:

10 != 20 : a is not equal to b

10 -lt 100 -a 20 -gt 15 : returns true

10 -lt 100 -o 20 -gt 100 : returns true

10 -lt 5 -o 20 -gt 100 : returns false

 

布尔运算符列表

运算符

说明

举例

!

非运算,表达式为 true 则返回 false,否则返回 true

[ ! false ] 返回 true

-o

或运算,有一个表达式为 true则返回 true

[ $a -lt 20 -o $b -gt 100 ] 返回 true

-a

与运算,两个表达式都为 true才返回 true

[ $a -lt 20 -a $b -gt 100 ] 返回 false

2.4.4 字符串运算符

先来看一个例子:

#!/bin/sh

 

a="abc"

b="efg"

 

if[ $a = $b ]

then

  echo "$a = $b : ais equal to b"

else

  echo "$a = $b: ais not equal to b"

fi

 

if[ $a != $b ]

then

  echo "$a != $b : ais not equal to b"

else

  echo "$a != $b: ais equal to b"

fi

 

if[ -z $a ]

then

  echo "-z $a :string length is zero"

else

  echo "-z $a :string length is not zero"

fi

 

if[ -n $a ]

then

  echo "-n $a :string length is not zero"

else

  echo "-n $a :string length is zero"

fi

 

if[ $a ]

then

  echo "$a : stringis not empty"

else

  echo "$a : stringis empty"

fi

运行结果:

abc = efg: a is not equal to b

abc != efg : a is not equal to b

-z abc : string length is not zero

-n abc : string length is not zero

abc : string is not empty

 

字符串运算符列表

运算符

说明

举例

=

检测两个字符串是否相等,相等返回 true

[ $a = $b ] 返回 false

!=

检测两个字符串是否相等,不相等返回 true

[ $a != $b ] 返回 true

-z

检测字符串长度是否为0,为0返回 true

[ -z $a ] 返回 false

-n

检测字符串长度是否为0,不为0返回 true

[ -z $a ] 返回 true

str

检测字符串是否为空,不为空返回 true

[ $a ] 返回 true

2.4.5 文件测试运算符

文件测试运算符用于检测Unix文件的各种属性。

例如,变量 file表示文件“/var/www/tutorialspoint/unix/test.sh”,它的大小为100字节,具有 rwx 权限。下面的代码,将检测该文件的各种属性:

#!/bin/sh

 

file="/var/www/tutorialspoint/unix/test.sh"

 

if[ -r $file ]

then

  echo "File hasread access"

else

  echo "File doesnot have read access"

fi

 

if[ -w $file ]

then

  echo "File haswrite permission"

else

  echo "File doesnot have write permission"

fi

 

if[ -x $file ]

then

  echo "File hasexecute permission"

else

  echo "File doesnot have execute permission"

fi

 

if[ -f $file ]

then

  echo "File is anordinary file"

else

  echo "This issepcial file"

fi

 

if[ -d $file ]

then

  echo "File is a directory"

else

  echo "This is nota directory"

fi

 

if[ -s $file ]

then

  echo "File size iszero"

else

  echo "File size isnot zero"

fi

 

if[ -e $file ]

then

  echo "Fileexists"

else

  echo "File doesnot exist"

fi

运行结果:

File has read access

File has write permission

File has execute permission

File is an ordinary file

This is not a directory

File size is zero

File exists

 

文件测试运算符列表

操作符

说明

举例

-b file

检测文件是否是块设备文件,如果是,则返回 true

[ -b $file ] 返回 false

-c file

检测文件是否是字符设备文件,如果是,则返回 true

[ -b $file ] 返回 false

-d file

检测文件是否是目录,如果是,则返回 true

[ -d $file ] 返回 false

-f file

检测文件是否是普通文件(既不是目录,也不是设备文件),如果是,则返回 true

[ -f $file ] 返回 true

-g file

检测文件是否设置了 SGID 位,如果是,则返回 true

[ -g $file ] 返回 false

-k file

检测文件是否设置了粘着位(Sticky Bit),如果是,则返回 true

[ -k $file ] 返回 false

-p file

检测文件是否是具名管道,如果是,则返回 true

[ -p $file ] 返回 false

-u file

检测文件是否设置了 SUID 位,如果是,则返回 true

[ -u $file ] 返回 false

-r file

检测文件是否可读,如果是,则返回 true

[ -r $file ] 返回 true

-w file

检测文件是否可写,如果是,则返回 true

[ -w $file ] 返回 true

-x file

检测文件是否可执行,如果是,则返回 true

[ -x $file ] 返回 true

-s file

检测文件是否为空(文件大小是否大于0),不为空返回 true

[ -s $file ] 返回 true

-e file

检测文件(包括目录)是否存在,如果是,则返回 true

[ -e $file ] 返回 true

 

2.5 Shell注释

以“#”开头的行就是注释,会被解释器忽略。
sh里没有多行注释,只能每一行加一个#号。只能像这样:

#--------------------------------------------

# 这是一个自动打ipa的脚本,基于webfrogs的ipa-build书写:

# https://github.com/webfrogs/xcode_shell/blob/master/ipa-build

 

# 功能:自动为etao ios app打包,产出物为14个渠道的ipa包

# 特色:全自动打包,不需要输入任何参数

#--------------------------------------------

 

##### 用户配置区 开始 #####

#

#

# 项目根目录,推荐将此脚本放在项目的根目录,这里就不用改了

# 应用名,确保和Xcode里Product下的target_name.app名字一致

#

##### 用户配置区 结束 #####

如果在开发过程中,遇到大段的代码需要临时注释起来,过一会儿又取消注释,怎么办呢?每一行加个#符号太费力了,可以把这一段要注释的代码用一对花括号括起来,定义成一个函数,没有地方调用这个函数,这块代码就不会执行,达到了和注释一样的效果。

2.6 Shell字符串

字符串是shell编程中最常用最有用的数据类型(除了数字和字符串,也没啥其它类型好用了),字符串可以用单引号,也可以用双引号,也可以不用引号。单双引号的区别跟PHP类似。

2.6.1 单引号

str='this is a string'

单引号字符串的限制:

单引号里的任何字符都会原样输出,单引号字符串中的变量是无效的;

单引号字串中不能出现单引号(对单引号使用转义符后也不行)。

2.6.2 双引号

your_name='qinjx'

str="Hello, I know yourare\"$your_name\"!\n"

双引号的优点:

双引号里可以有变量

双引号里可以出现转义字符

2.6.3 拼接字符串

your_name="qinjx"

greeting="hello, "$your_name" !"

greeting_1="hello, ${your_name}!"

 

echo $greeting $greeting_1

2.6.4 获取字符串长度

string="abcd"

echo ${#string} #输出 4

2.6.5 提取子字符串

string="alibaba is a greatcompany"

echo ${string:1:4} #输出liba

2.6.6 查找子字符串

string="alibaba is a greatcompany"

echo `expr index "$string" is`

索引命令功能在字符串$string上找出substring中,字符第一次出现的位置”
所以是在找“i”或者"s”第一次出现的位置

从字串里找到第一个出现的下标,如果找不到则返回0,可见它的下标是从1开始的。

2.7 Shell数组

Shell在编程方面比Windows批处理强大很多,无论是在循环、运算。

bash
支持一维数组(不支持多维数组),并且没有限定数组的大小。类似与C语言,数组元素的下标由0开始编号。获取数组中的元素要利用下标,下标可以是整数或算术表达式,其值应大于或等于0

2.7.1 定义数组

Shell中,用括号来表示数组,数组元素用“空格”符号分割开。定义数组的一般形式为:
    array_name=(value1 ...valuen)
例如:

array_name=(value0 value1 value2 value3)

或者

array_name=(

value0

value1

value2

value3

)


还可以单独定义数组的各个分量:

array_name[0]=value0

array_name[1]=value1

array_name[2]=value2

可以不使用连续的下标,而且下标的范围没有限制。

2.7.2 读取数组

读取数组元素值的一般格式是:
    ${array_name[index]}
例如:

valuen=${array_name[2]}

举个例子:

#!/bin/sh

 

NAME[0]="Zara"

NAME[1]="Qadir"

NAME[2]="Mahnaz"

NAME[3]="Ayan"

NAME[4]="Daisy"

echo "First Index: ${NAME[0]}"

echo "Second Index: ${NAME[1]}"

运行脚本,输出:

$./test.sh

First Index: Zara

Second Index: Qadir

使用@ *可以获取数组中的所有元素,例如:

${array_name[*]}

${array_name[@]}

举个例子:

#!/bin/sh

 

NAME[0]="Zara"

NAME[1]="Qadir"

NAME[2]="Mahnaz"

NAME[3]="Ayan"

NAME[4]="Daisy"

echo "First Method: ${NAME[*]}"

echo "Second Method: ${NAME[@]}"

运行脚本,输出:

$./test.sh

First Method: Zara Qadir Mahnaz Ayan Daisy

Second Method: Zara Qadir Mahnaz Ayan Daisy

2.7.3 获取数组的长度

获取数组长度的方法与获取字符串长度的方法相同,例如:

纯文本复制

#取得数组元素的个数

length=${#array_name[@]}

#或者

length=${#array_name[*]}

#取得数组单个元素的长度

lengthn=${#array_name[n]}

2.8 Shell echo命令

echoShell的一个内部指令,用于在屏幕上打印出指定的字符串。命令格式:

echo arg

您可以使用echo实现更复杂的输出格式控制。

2.8.1 显示转义字符

echo "\"It is a test\""

结果将是:
"It is a test"

双引号也可以省略。

2.8.2 显示变量

name="OK"

echo "$name It is a test"

结果将是:
OK It is a test

同样双引号也可以省略。

如果变量与其它字符相连的话,需要使用大括号({ }):

mouth=8

echo "${mouth}-1-2009"

结果将是:
8-1-2009

2.8.3 显示换行

echo "OK!\n"

echo "It is a test"

输出:
OK!
It is a test

2.8.4 显示不换行

echo "OK!\c"

echo "It is a test"

输出:
OK!It si a test

2.8.5 显示结果重定向至文件

echo "It is a test" > myfile

2.8.6 原样输出字符串

若需要原样输出字符串(不进行转义),请使用单引号。例如:

echo '$name\"'

2.8.7 显示命令执行结果

echo `date`

结果将显示当前日期

从上面可看出,双引号可有可无,单引号主要用在原样输出中。

 

2.9 shell printf命令

printf命令用于格式化输出,echo命令的增强版。它是C语言printf()库函数的一个有限的变形,并且在语法上有些不同。

注意:printf POSIX 标准所定义,移植性要比 echo 好。

如同 echo命令,printf命令也可以输出简单的字符串:

$printf "Hello, Shell\n"

Hello, Shell

$

printf不像 echo那样会自动换行,必须显式添加换行符(\n)

printf
命令的语法:

printf  format-string [arguments...]

format-string为格式控制字符串,arguments为参数列表。

printf()
C语言入门教程中已经讲到,功能和用法与 printf 命令类似,请查看:C语言格式输出函数printf()详解

这里仅说明与C语言printf()函数的不同:

printf 命令不用加括号

format-string 可以没有引号,但最好加上,单引号双引号均可。

参数多于格式控制符(%)时,format-string 可以重用,可以将所有参数都转换。

arguments 使用空格分隔,不用逗号。


请看下面的例子:

# format-string为双引号

$ printf "%d%s\n" 1 "abc"

1 abc

# 单引号与双引号效果一样

$ printf '%d %s\n'1 "abc"

1 abc

# 没有引号也可以输出

$ printf %sabcdef

abcdef

# 格式只指定了一个参数,但多出的参数仍然会按照该格式输出,format-string被重用

$ printf %sabc def

abcdef

$ printf "%s\n"abc def

abc

def

$ printf "%s %s%s\n" a b c d e f g h i j

a bc

d ef

g hi

j

# 如果没有 arguments,那么 %s 用NULL代替,%d 用 0 代替

$ printf "%sand %d \n"

and0

# 如果以 %d 的格式来显示字符串,那么会有警告,提示无效的数字,此时默认置为 0

$ printf "Thefirst program always prints'%s,%d\n'" Hello Shell

-bash: printf: Shell:invalid number

Thefirst program always prints 'Hello,0'

$


注意,根据POSIX标准,浮点格式%e%E%f%g%G是“不需要被支持”。这是因为awk支持浮点预算,且有它自己的printf语句。这样Shell程序中需要将浮点数值进行格式化的打印时,可使用小型的awk程序实现。然而,内建于bashksh93zsh中的printf命令都支持浮点格式。

 

2.10 Shell if else语句

if语句通过关系运算符判断表达式的真假来决定执行哪个分支。Shell有三种 if ... else语句:

if ... fi 语句;

if ... else ... fi 语句;

if ... elif ... else ... fi 语句。

1) if ... else 语句

if ... else语句的语法:

if [ expression ]

then

  Statement(s) to be executed if expression is true

fi

如果 expression返回 truethen后边的语句将会被执行;如果返回 false,不会执行任何语句。

最后必须以 fi来结尾闭合 iffi就是 if倒过来拼写,后面也会遇见。

注意:expression和方括号([ ])之间必须有空格,否则会有语法错误。

举个例子:

#!/bin/sh

 

a=10

b=20

 

if[ $a == $b ]

then

  echo "a is equalto b"

fi

 

if[ $a != $b ]

then

  echo "a is notequal to b"

fi

运行结果:

a is not equal to b

2) if ... else ... 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

如果expression返回 true,那么 then 后边的语句将会被执行;否则,执行 else后边的语句。

举个例子:

#!/bin/sh

 

a=10

b=20

 

if[ $a == $b ]

then

  echo "a is equalto b"

else

  echo "a is notequal to b"

fi

执行结果:

a is not equal to b

3) if ... elif ... 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

  Statement(s) to be executed if no expression is true

fi

哪一个 expression的值为 true,就执行哪个 expression后面的语句;如果都为 false,那么不执行任何语句。

举个例子:

#!/bin/sh

 

a=10

b=20

 

if[ $a == $b ]

then

  echo "a is equalto b"

elif[ $a -gt $b ]

then

  echo "a is greaterthan b"

elif[ $a -lt $b ]

then

   echo"a is less than b"

else

  echo "None of thecondition met"

fi

运行结果:

a is less than b


if ... else语句也可以写成一行,以命令的方式来运行,像这样:

iftest$[2*3]-eq $[1+5];thenecho'The two numbers areequal!';fi;


if ... else语句也经常与 test命令结合使用,如下所示:

num1=$[2*3]

num2=$[1+5]

iftest$[num1]-eq $[num2]

then

   echo 'The two numbersare equal!'

else

   echo 'The two numbersare not equal!'

fi

输出:

The two numbers are equal!

test命令用于检查某个条件是否成立,与方括号([ ])类似。

2.11 Shell case esac语句

case ... esac与其他语言中的 switch ... case语句类似,是一种多分枝选择结构。

case
语句匹配一个值或一个模式,如果匹配成功,执行相匹配的命令。case语句格式如下:

case 值 in

模式1)

    command1

    command2

    command3

    ;;

模式2)

    command1

    command2

    command3

    ;;

*)

    command1

    command2

    command3

    ;;

esac

case工作方式如上所示。取值后面必须为关键字 in,每一模式必须以右括号结束。取值可以为变量或常数。匹配发现取值符合某一模式后,其间所有命令开始执行直至 ;;;;与其他语言中的break类似,意思是跳到整个case语句的最后。

取值将检测匹配的每一个模式。一旦模式匹配,则执行完匹配模式相应命令后不再继续其他模式。如果无一匹配模式,使用星号 *捕获该值,再执行后面的命令。

下面的脚本提示输入14,与每一种模式进行匹配:

echo'Input a number between 1 to 4'

echo'Your number is:\c'

read aNum

case $aNum in

    1)  echo'You select 1'

    ;;

    2)  echo'You select 2'

    ;;

    3)  echo'You select 3'

    ;;

    4)  echo'You select 4'

    ;;

    *)  echo 'You do notselect a number between 1 to 4'

    ;;

esac

输入不同的内容,会有不同的结果,例如:

Input a numberbetween 1 to 4

Your number is:3

You select 3


再举一个例子:

#!/bin/bash

 

option="${1}"

case ${option} in

   -f) FILE="${2}"

      echo "Filename is $FILE"

      ;;

   -d) DIR="${2}"

      echo "Dirname is $DIR"

      ;;

   *)

      echo "`basename${0}`:usage: [-f file] | [-d directory]"

      exit1# Command tocome out of the program with status 1

      ;;

esac

运行结果:

$./test.sh

test.sh: usage: [ -ffilename ] | [ -d directory ]

$ ./test.sh -findex.htm

$ vi test.sh

$ ./test.sh -findex.htm

File name isindex.htm

$ ./test.sh -d unix

Dir name is unix

$

2.12 Shell for循环

与其他编程语言类似,Shell支持for循环。

for
循环一般格式为:

for 变量 in 列表

do

    command1

    command2

    ...

    commandN

done

列表是一组值(数字、字符串等)组成的序列,每个值通过空格分隔。每循环一次,就将列表中的下一个值赋给变量。

in
列表是可选的,如果不用它,for循环使用命令行的位置参数。

例如,顺序输出当前列表中的数字:

for loop in1 2 3 4 5

do

    echo "Thevalue is: $loop"

done

运行结果:

The value is: 1

The value is: 2

The value is: 3

The value is: 4

The value is: 5


顺序输出字符串中的字符:

for str in'This is a string'

do

    echo $str

done

运行结果:

This is a string


显示主目录下以 .bash开头的文件:

#!/bin/bash

 

for FILE in$HOME/.bash*

do

   echo $FILE

done

运行结果:

/root/.bash_history

/root/.bash_logout

/root/.bash_profile

/root/.bashrc

2.13 Shell while循环

while循环用于不断执行一系列命令,也用于从输入文件中读取数据;命令通常为测试条件。其格式为:

while command

do

  Statement(s) to be executed if command is true

done

命令执行完毕,控制返回循环顶部,从头开始直至测试条件为假。

以下是一个基本的while循环,测试条件是:如果COUNTER小于5,那么返回trueCOUNTER0开始,每次循环处理时,COUNTER1。运行上述脚本,返回数字15,然后终止。

COUNTER=0

while [ $COUNTER -lt 5 ]

do

    COUNTER=`expr $COUNTER +1`

    echo $COUNTER

done

运行脚本,输出:

1

2

3

4

5


while循环可用于读取键盘信息。下面的例子中,输入信息被设置为变量FILM,按<Ctrl-D>结束循环。

echo'type <CTRL-D> to terminate'

echo-n 'enter your most liked film: '

while read FILM

do

    echo "Yeah!great film the $FILM"

done

运行脚本,输出类似下面:

type <CTRL-D>to terminate

enter your mostliked film: Sound of Music

Yeah! great film theSound of Music

2.14 Shell until循环

until循环执行一系列命令直至条件为 true时停止。until循环与 while循环在处理方式上刚好相反。一般while循环优于until循环,但在某些时候,也只是极少数情况下,until循环更加有用。

until
循环格式为:

until command

do

   Statement(s) to be executed until command istrue

done

command一般为条件表达式,如果返回值为 false,则继续执行循环体内的语句,否则跳出循环。

例如,使用 until命令输出 0 ~ 9的数字:

#!/bin/bash

 

a=0

 

until [ !$a -lt10 ]

do

   echo $a

   a=`expr $a + 1`

done

运行结果:

0

1

2

3

4

5

6

7

8

9

2.15 Shell breakcontinue命令

在循环过程中,有时候需要在未达到循环结束条件时强制跳出循环,像大多数编程语言一样,Shell也使用 break continue来跳出循环。

2.15.1 break命令

break命令允许跳出所有循环(终止执行后面的所有循环)。

下面的例子中,脚本进入死循环直至用户输入数字大于5。要跳出这个循环,返回到shell提示符下,就要使用break命令。

#!/bin/bash

while:

do

   echo -n "Input a number between 1 to 5:"

    read aNum

    case$aNum in

       1|2|3|4|5) echo "Your number is $aNum!"

       ;;

       *) echo"You do not select a numberbetween 1 to 5, game is over!"

           break

       ;;

    esac

done

在嵌套循环中,break命令后面还可以跟一个整数,表示跳出第几层循环。例如:

break n

表示跳出第 n层循环。

下面是一个嵌套循环的例子,如果 var1等于 2,并且 var2等于 0,就跳出循环:

#!/bin/bash

 

for var1in12 3

do

   for var2in 0 5

   do

     if[$var1 -eq 2 -a $var2-eq 0 ]

     then

        break2

     else

        echo "$var1$var2"

     fi

   done

done

如上,break2表示直接跳出外层循环。运行结果:

1 0

1 5

2.15.2 continue命令

continue命令与break命令类似,只有一点差别,它不会跳出所有循环,仅仅跳出当前循环。

对上面的例子进行修改:

#!/bin/bash

while:

do

   echo -n "Input a number between 1 to 5:"

    read aNum

    case$aNum in

       1|2|3|4|5) echo "Your number is $aNum!"

       ;;

       *) echo"You do not select a numberbetween 1 to 5!"

           continue

           echo "Game isover!"

       ;;

    esac

done

运行代码发现,当输入大于5的数字时,该例中的循环不会结束,语句

echo "Game is over!"

永远不会被执行。

同样,continue后面也可以跟一个数字,表示跳出第几层循环。

再看一个 continue的例子:

#!/bin/bash

 

NUMS="1 2 3 4 5 6 7"

 

for NUMin$NUMS

do

   Q=`expr $NUM% 2`

   if[ $Q -eq 0 ]

   then

     echo "Number is aneven number!!"

     continue

   fi

  echo "Found oddnumber"

done

运行结果:

Found odd number

Number is an even number!!

Found odd number

Number is an even number!!

Found odd number

Number is an even number!!

Found odd number

2.16 Shell函数

函数可以让我们将一个复杂功能划分成若干模块,让程序结构更加清晰,代码重复利用率更高。像其他编程语言一样,Shell也支持函数。Shell函数必须先定义后使用。

Shell
函数的定义格式如下:

function_name () {

    list of commands

    [ return value ]

}

如果你愿意,也可以在函数名前加上关键字 function

functionfunction_name () {

    list of commands

    [ return value ]

}

函数返回值,可以显式增加return语句;如果不加,会将最后一条命令运行结果作为返回值。

Shell
函数返回值只能是整数,一般用来表示函数执行成功与否,0表示成功,其他值表示失败。如果 return 其他数据,比如一个字符串,往往会得到错误提示:“numeric argument required”。

如果一定要让函数返回字符串,那么可以先定义一个变量,用来接收函数的计算结果,脚本在需要的时候访问这个变量来获得函数返回值。

先来看一个例子:

#!/bin/bash

 

# Define your function here

Hello () {

   echo "Urlis http://see.xidian.edu.cn/cpp/shell/"

}

 

# Invoke your function

Hello

运行结果:

$./test.sh

Url is http://see.xidian.edu.cn/cpp/shell/

$

调用函数只需要给出函数名,不需要加括号。

再来看一个带有return语句的函数:

#!/bin/bash

funWithReturn(){

    echo "The function is to get the sum of twonumbers..."

    echo -n "Inputfirst number: "

    readaNum

    echo -n "Inputanother number: "

    readanotherNum

    echo "Thetwo numbers are $aNum and $anotherNum !"

    return$(($aNum+$anotherNum))

}

funWithReturn

# Capture value returnd by last command

ret=$?

echo"The sum of two numbers is $ret !"

运行结果:

The function is toget the sum of two numbers...

Input first number:25

Input anothernumber: 50

The two numbers are25 and 50 !

The sum of twonumbers is 75 !

函数返回值在调用该函数后通过 $?来获得。

再来看一个函数嵌套的例子:

#!/bin/bash

 

# Calling one function from another

number_one () {

   echo "Url_1is http://see.xidian.edu.cn/cpp/shell/"

   number_two

}

 

number_two () {

   echo "Url_2is http://see.xidian.edu.cn/cpp/u/xitong/"

}

 

number_one

运行结果:

Url_1 ishttp://see.xidian.edu.cn/cpp/shell/

Url_2 ishttp://see.xidian.edu.cn/cpp/u/xitong/

像删除变量一样,删除函数也可以使用 unset命令,不过要加上-f选项,如下所示:

$unset -f function_name

如果你希望直接从终端调用函数,可以将函数定义在主目录下的 .profile文件,这样每次登录后,在命令提示符后面输入函数名字就可以立即调用。

 

2.17 Shell函数参数

Shell中,调用函数时可以向其传递参数。在函数体内部,通过 $n 的形式来获取参数的值,例如,$1表示第一个参数,$2表示第二个参数...

带参数的函数示例:

#!/bin/bash

funWithParam(){

    echo "Thevalue of the first parameter is $1 !"

    echo "Thevalue of the second parameter is $2 !"

    echo "Thevalue of the tenth parameter is $10 !"

    echo "Thevalue of the tenth parameter is ${10} !"

    echo "Thevalue of the eleventh parameter is ${11} !"

    echo "Theamount of the parameters is $# !" #参数个数

    echo "The stringof the parameters is $* !" # 传递给函数的所有参数

}

funWithParam1 23 4 5 6 7 8 9 3473

运行脚本:

The value of thefirst parameter is 1 !

The value of thesecond parameter is 2 !

The value of thetenth parameter is 10 !

The value of thetenth parameter is 34 !

The value of theeleventh parameter is 73 !

The amount of theparameters is 12 !

The string of theparameters is 1 2 3 4 5 6 7 8 9 34 73 !"

注意,$10不能获取第十个参数,获取第十个参数需要${10}。当n>=10时,需要使用${n}来获取参数。

另外,还有几个特殊变量用来处理参数,前面已经提到:

特殊变量

说明

$#

传递给函数的参数个数。

$*

显示所有传递给函数的参数。

$@

$*相同,但是略有区别,请查看Shell特殊变量

$?

函数的返回值。

2.18 Shell输入输出重定向

Unix命令默认从标准输入设备(stdin)获取输入,将结果输出到标准输出设备(stdout)显示。一般情况下,标准输入设备就是键盘,标准输出设备就是终端,即显示器。

2.18.1 输出重定向

命令的输出不仅可以是显示器,还可以很容易的转移向到文件,这被称为输出重定向。

命令输出重定向的语法为:

$ command> file

这样,输出到显示器的内容就可以被重定向到文件。

例如,下面的命令在显示器上不会看到任何输出:

$ who > users

打开 users文件,可以看到下面的内容:

$ cat users

oko        tty01   Sep 12 07:30

ai         tty15   Sep 12 13:32

ruth       tty21   Sep 12 10:10

pat        tty24   Sep 12 13:07

steve      tty25   Sep 12 13:03

$

输出重定向会覆盖文件内容,请看下面的例子:

$ echo line 1 > users

$ cat users

line 1

$

如果不希望文件内容被覆盖,可以使用 >>追加到文件末尾,例如:

$ echo line 2 >> users

$ cat users

line 1

line 2

$

2.18.2 输入重定向

和输出重定向一样,Unix命令也可以从文件获取输入,语法为:

command<file

这样,本来需要从键盘获取输入的命令会转移到文件读取内容。

注意:输出重定向是大于号(>),输入重定向是小于号(<)

例如,计算 users文件中的行数,可以使用下面的命令:

$ wc -l users

2 users

$

也可以将输入重定向到users文件:

$ wc -l < users

2

$

注意:上面两个例子的结果不同:第一个例子,会输出文件名;第二个不会,因为它仅仅知道从标准输入读取内容。

重定向深入讲解

一般情况下,每个Unix/Linux命令运行时都会打开三个文件:

标准输入文件(stdin)stdin的文件描述符为0Unix程序默认从stdin读取数据。

标准输出文件(stdout)stdout的文件描述符为1Unix程序默认向stdout输出数据。

标准错误文件(stderr)stderr的文件描述符为2Unix程序会向stderr流中写入错误信息。


默认情况下,command > file stdout重定向到 filecommand < file stdin重定向到file

如果希望 stderr重定向到 file,可以这样写:

$command2 > file

如果希望stderr追加到 file文件末尾,可以这样写:

$command2 >> file

2表示标准错误文件(stderr)

如果希望将 stdout stderr合并后重定向到 file,可以这样写:

$command>file 2>&1

$command>>file 2>&1

如果希望对stdin stdout都重定向,可以这样写:

$command<file1 >file2

command 命令将 stdin 重定向到 file1,将 stdout 重定向到 file2 

全部可用的重定向命令列表

命令

说明

command > file

将输出重定向到 file

command < file

将输入重定向到 file

command >> file

将输出以追加的方式重定向到 file

n > file

将文件描述符为 n 的文件重定向到 file

n >> file

将文件描述符为 n 的文件以追加的方式重定向到 file

n >& m

将输出文件 m n 合并。

n <& m

将输入文件 m n 合并。

<< tag

将开始标记 tag 和结束标记 tag 之间的内容作为输入。

Here Document

Here Document目前没有统一的翻译,这里暂译为”嵌入文档“。Here Document Shell中的一种特殊的重定向方式,它的基本的形式如下:

command<<delimiter

   document

delimiter

它的作用是将两个delimiter之间的内容(document)作为输入传递给 command

注意:

结尾的delimiter 一定要顶格写,前面不能有任何字符,后面也不能有任何字符,包括空格和 tab 缩进。

开始的delimiter前后的空格会被忽略掉。


下面的例子,通过 wc -l命令计算 document的行数:

$wc -l << EOF

   This is a simple lookup program

   for good (and bad) restaurants

   in Cape Town.

EOF

3

$

也可以 Here Document用在脚本中,例如:

#!/bin/bash

 

cat << EOF

This is a simple lookup program

for good(andbad) restaurants

in Cape Town.

EOF

运行结果:

This is a simple lookup program

for good (and bad) restaurants

in Cape Town.


下面的脚本通过 vi编辑器将 document保存到 test.txt文件:

#!/bin/sh

 

filename=test.txt

vi $filename <<EndOfCommands

i

This file was created automatically from

a shell script

^[

ZZ

EndOfCommands

运行脚本:

$ sh test.sh

Vim: Warning: Input is not from a terminal

$

打开test.txt,可以看到下面的内容:

$ cat test.txt

This file was created automatically from

a shell script

$

/dev/null 文件

如果希望执行某个命令,但又不希望在屏幕上显示输出结果,那么可以将输出重定向到 /dev/null

$ command> /dev/null

/dev/null是一个特殊的文件,写入到它的内容都会被丢弃;如果尝试从该文件读取内容,那么什么也读不到。但是 /dev/null文件非常有用,将命令的输出重定向到它,会起到”禁止输出“的效果。

如果希望屏蔽 stdout stderr,可以这样写:

$ command> /dev/null2>&1

2.19 Shell文件包含

像其他语言一样,Shell也可以包含外部脚本,将外部脚本的内容合并到当前脚本。

Shell
中包含脚本可以使用:

. filename

source filename

两种方式的效果相同,简单起见,一般使用点号(.),但是注意点号(.)和文件名中间有一空格。

例如,创建两个脚本,一个是被调用脚本 subscript.sh,内容如下:

url="http://see.xidian.edu.cn/cpp/view/2738.html"

一个是主文件main.sh,内容如下:

#!/bin/bash

. ./subscript.sh

echo$url

执行脚本:

$chomd +x main.sh

./main.sh

http://see.xidian.edu.cn/cpp/view/2738.html

$

注意:被包含脚本不需要有执行权限。

0 0
原创粉丝点击