linux脚本编写基础

来源:互联网 发布:java socket实例 编辑:程序博客网 时间:2024/06/14 15:37

Linux脚本编写基础

             1. Linux脚本编写基础

1.1 语法基本介绍

1.1.1 开头

程序必须以下面的行开始(必须方在文件的第一行):

#!/bin/sh

  符号#!用来告诉系统它后面的参数是用来执行该文件的程序。在这个例子中我们使用/bin/sh来执行程序。

  当编辑好脚本时,如果要执行该脚本,还必须使其可执行。

  要使脚本可执行:

编译 chmod +x filename这样才能用./filename 来运行

1.1.2 注释

  在进行shell编程时,以#开头的句子表示注释,直到这一行的结束。我们真诚地建议您在程序中使用注释。

  如果您使用了注释,那么即使相当长的时间内没有使用该脚本,您也能在很短的时间内明白该脚本的作用及工作原理。

1.1.3 变量

  在其他编程语言中您必须使用变量。在shell编程中,所有的变量都由字符串组成,并且您不需要对变量进行声明。要赋值给一个变量,您可以这样写:

#!/bin/sh

#对变量赋值:

a="hello world"

# 现在打印变量a的内容:

echo "A is:"

echo $a

有时候变量名很容易与其他文字混淆,比如:

num=2

echo "this is the $numnd"

这并不会打印出"this is the 2nd",而仅仅打印"this is the ",因为shell会去搜索变量numnd的值,但是这个变量时没有值的。可以使用花括号来告诉shell我们要打印的是num变量:

num=2

echo "this is the ${num}nd"

  这将打印: this is the 2nd

1.1.4 环境变量

export关键字处理过的变量叫做环境变量。我们不对环境变量进行讨论,因为通常情况下仅仅在登录脚本中使用环境变量。

1.1.5 Shell命令和流程控制

shell脚本中可以使用三类命令:

1)Unix 命令:

  虽然在shell脚本中可以使用任意的unix命令,但是还是由一些相对更常用的命令。这些命令通常是用来进行文件和文字操作的。

常用命令语法及功能

  echo "some text":将文字内容打印在屏幕上

  ls: 文件列表

  wc l filewc -w filewc -c file:计算文件行数计算文件中的单词数计算文件中的字符数

  cp sourcefile destfile:文件拷贝

  mv oldname newname :重命名文件或移动文件

  rm file: 删除文件

  grep 'pattern' file:在文件内搜索字符串比如:grep 'searchstring' file.txt

  cut -b colnum file:指定欲显示的文件内容范围,并将它们输出到标准输出设备比如:输出每行第5个到第9个字符cut -b5-9 file.txt千万不要和cat命令混淆,

这是两个完全不同的命令

  cat file.txt:输出文件内容到标准输出设备(屏幕)上

  file somefile:得到文件类型

  read var: 提示用户输入,并将输入赋值给变量

  sort file.txt:file.txt文件中的行进行排序

  uniq: 删除文本文件中出现的行列比如: sort file.txt | uniq

  expr: 进行数学运算Example: add 2 and 3expr 2 "+" 3

  find: 搜索文件比如:根据文件名搜索find . -name filename -print

  tee: 将数据输出到标准输出设备(屏幕)和文件比如:somecommand | tee outfile

  basename file:返回不包含路径的文件名比如: basename /bin/tux将返回 tux

  dirname file:返回文件所在路径比如:dirname /bin/tux将返回 /bin

  head file: 打印文本文件开头几行

  tail file : 打印文本文件末尾几行

  sed: Sed是一个基本的查找替换程序。可以从标准输入(比如命令管道)读入文本,并将

结果输出到标准输出(屏幕)。该命令采用正则表达式(见参考)进行搜索。不要和shell中的通配符相混淆。比如:将linuxfocus替换为LinuxFocus cat text.file | sed 's/linuxfocus/LinuxFocus/' > newtext.file  awk: awk用来从文本文件中提取字段。缺省地,字段分割符是空格,可以使用-F指定其他分割符。

cat file.txt | awk -F, '{print $1 "," $3 }'这里我们使用,作为字段分割符,同时打印第一个和第三个字段。如果该文件内容如下: Adam Bor, 34, IndiaKerry Miller, 22, USA命令输出结果为:Adam Bor, IndiaKerry Miller, USA

2) 概念:管道, 重定向和 backtick

  这些不是系统命令,但是他们真的很重要。

  管道 (|) 将一个命令的输出作为另外一个命令的输入。

grep "hello" file.txt | wc -l

  在file.txt中搜索包含有”hello”的行并计算其行数。

  在这里grep命令的输出作为wc命令的输入。当然您可以使用多个命令。

  重定向:将命令的结果输出到文件,而不是标准输出(屏幕)。

  > 写入文件并覆盖旧文件

  >> 加到文件的尾部,保留旧文件内容。

反短斜线

 使用反短斜线可以将一个命令的输出作为另外一个命令的一个命令行参数。

命令:

find . -mtime -1 -type f -print

  用来查找过去24小时(-mtime2则表示过去48小时)内修改过的文件。如果您想将所有查找到的文件打一个包,则可以使用以下脚本:

#!/bin/sh

# The ticks are backticks (`) not normal quotes ('):

tar -zcvf lastmod.tar.gz `find . -mtime -1 -type f -print`

3) 流程控制

1.if

  "if" 表达式如果条件为真则执行then后面的部分:

if ....; then

  ....

elif ....; then

  ....

else

  ....

fi

大多数情况下,可以使用测试命令来对条件进行测试。比如可以比较字符串、判断文件是否存在及是否可读等等…

  通常用" [ ] "来表示条件测试。注意这里的空格很重要。要确保方括号的空格。

[ -f "somefile" ]:判断是否是一个文件

[ -x "/bin/ls" ]:判断/bin/ls是否存在并有可执行权限

[ -n "$var" ] :判断$var变量是否有值

[ "$a" = "$b" ]:判断$a$b是否相等

  执行man test可以查看所有测试表达式可以比较和判断的类型。

  直接执行以下脚本:

#!/bin/sh

if [ "$SHELL" = "/bin/bash" ]; then

 echo "your login shell is the bash (bourne again shell)"

else

 echo "your login shell is not bash but $SHELL"

fi

  变量$SHELL包含了登录shell的名称,我们和/bin/bash进行了比较。

快捷操作符

熟悉C语言的朋友可能会很喜欢下面的表达式:

[ -f "/etc/shadow" ] && echo "This computer uses shadow passwors"

  这里 && 就是一个快捷操作符,如果左边的表达式为真则执行右边的语句。

您也可以认为是逻辑运算中的与操作。上例中表示如果/etc/shadow文件存在则打印” This computer uses shadow passwors”。同样或操作(||)shell编程中也是可用的。这里有个例子:

#!/bin/sh

mailfolder=/var/spool/mail/james

[ -r "$mailfolder" ]' '{ echo "Can not read $mailfolder" ; exit 1; }

echo "$mailfolder has mail from:"

grep "^From " $mailfolder

该脚本首先判断mailfolder是否可读。如果可读则打印该文件中的"From"一行。如果不可读则或操作生效,打印错误信息后脚本退出。这里有个问题,那就是我们必须有两个命令:

  -打印错误信息

  -退出程序

  我们使用花括号以匿名函数的形式将两个命令放到一起作为一个命令使用。一般函数将在下文提及。

  不用与和或操作符,我们也可以用if表达式作任何事情,但是使用与或操作符会更便利很多。

2.case

case :表达式可以用来匹配一个给定的字符串,而不是数字。

case ... in

...) do something here ;;

esac

  让我们看一个例子。 file命令可以辨别出一个给定文件的文件类型,比如:

file lf.gz

  这将返回:

lf.gz: gzip compressed data, deflated, original filename,

last modified: Mon Aug 27 23:09:18 2001, os: Unix

 我们利用这一点写了一个叫做smartzip的脚本,该脚本可以自动解压bzip2, gzipzip 类型的压缩文件:

#!/bin/sh

ftype=`file "$1"`

case "$ftype" in

"$1: Zip archive"*)

  unzip "$1" ;;

"$1: gzip compressed"*)

  gunzip "$1" ;;

"$1: bzip2 compressed"*)

  bunzip2 "$1" ;;

*) echo "File $1 can not be uncompressed with smartzip";;

esac

  您可能注意到我们在这里使用了一个特殊的变量$1。该变量包含了传递给该程序的第一个参数值。

也就是说,当我们运行:

smartzip articles.zip

$1 就是字符串 articles.zip

3. selsect

select 表达式是一种bash的扩展应用,尤其擅长于交互式使用。用户可以从一组不同的值中进行选择。

select var in ... ; do

 break

done

.... now $var can be used ....

下面是一个例子:

#!/bin/sh

echo "What is your favourite OS?"

select var in "Linux" "Gnu Hurd" "Free BSD" "Other"; do

    break

done

echo "You have selected $var"

  下面是该脚本运行的结果:

What is your favourite OS?

1) Linux

2) Gnu Hurd

3) Free BSD

4) Other

#? 1

You have selected Linux

4.loop

loop表达式:

while ...; do

....

done

  while-loop 将运行直到表达式测试为真。will run while the expression that we test for is true.

关键字"break" 用来跳出循环。而关键字”continue”用来不执行余下的部分而直接跳到下一个循环。

  

for-loop表达式查看一个字符串列表 (字符串用空格分隔)然后将其赋给一个变量:

for var in ....; do

  ....

done

在下面的例子中,将分别打印ABC到屏幕上:

#!/bin/sh

for var in A B C ; do

  echo "var is $var"

done

下面是一个更为有用的脚本showrpm,其功能是打印一些RPM包的统计信息:

#!/bin/sh

# list a content summary of a number of RPM packages

# USAGE: showrpm rpmfile1 rpmfile2 ...

# EXAMPLE: showrpm /cdrom/RedHat/RPMS/*.rpm

for rpmpackage in $*; do

 if [ -r "$rpmpackage" ];then

  echo "=============== $rpmpackage =============="

  rpm -qi -p $rpmpackage

 else

  echo "ERROR: cannot read file $rpmpackage"

 fi

done

  这里出现了第二个特殊的变量$*,该变量包含了所有输入的命令行参数值。

如果您运行showrpm openssh.rpm w3m.rpm webgrep.rpm

此时 $* 包含了 3个字符串,即openssh.rpm, w3m.rpm and webgrep.rpm.

5. 引号

在向程序传递任何参数之前,程序会扩展通配符和变量。这里所谓扩展的意思是程序会把通配符(比如*)替换成合适的文件名,它变量替换成变量值。为了防止程序作这种替换,您可以使用引号:让我们来看一个例子,假设在当前目录下有一些文件,两个jpg文件, mail.jpg tux.jpg

1.2 编译SHELL脚本

#ch#!/bin/sh mod +x filename

 cho *.jpg ∪缓螅梢酝ü淙耄?./filename 来执行您的脚本。

  这将打印出"mail.jpg tux.jpg"的结果。

   引号 (单引号和双引号)将防止这种通配符扩展:

#!/bin/sh

echo "*.jpg"

echo '*.jpg'

  这将打印"*.jpg" 两次。

  单引号更严格一些。它可以防止任何变量扩展。双引号可以防止通配符扩展但允许变量扩展。

#!/bin/sh

echo $SHELL

echo "$SHELL"

echo '$SHELL'

  运行结果为:

/bin/bash

/bin/bash

$SHELL

  最后,还有一种防止这种扩展的方法,那就是使用转义字符——反斜杆:

echo *.jpg

echo $SHELL

  这将输出:

*.jpg

$SHELL

6. Here documents

当要将几行文字传递给一个命令时,here documents(译者注:目前还没有见到过对该词适合的翻译)一种不错的方法。对每个脚本写一段帮助性的文字是很有用的,此时如果我们四有那个 here documents就不必用echo函数一行行输出。一个 "Here document"

4)函数

如果您写了一些稍微复杂一些的程序,您就会发现在程序中可能在几个地方使用了相同的代码,并且您也会发现,如果我们使用了函数,会方便很多。一个函数是这个样子的:

functionname()

{

# inside the body $1 is the first argument given to the function

# $2 the second ...

body

}

您需要在每个程序的开始对函数进行声明。

  下面是一个叫做xtitlebar的脚本,使用这个脚本您可以改变终端窗口的名称。

这里使用了一个叫做help的函数。正如您可以看到的那样,这个定义的函数被使用了两次。

#!/bin/sh

# vim: set sw=4 ts=4 et:

help()

{

  cat  shift by 2

  --) shift;break;; # end of options

  -*) echo "error: no such option $1. -h for help";exit 1;;

  *) break;;

esac

done

echo "opt_f is $opt_f"

echo "opt_l is $opt_l"

echo "first arg is $1"

echo "2nd arg is $2"

  您可以这样运行该脚本:

cmdparser -l hello -f -- -somefile1 somefile2

  返回的结果是:

opt_f is 1

opt_l is hello

first arg is -somefile1

2nd arg is somefile2

  这个脚本是如何工作的呢?脚本首先在所有输入命令行参数中进行循环,将输入参数与case表达式进行比较,如果匹配则设置一个变量并且移除该参数。根据unix系统的惯例,首先输入的应该是包含减号的参数.

2部分实例

现在我们来讨论编写一个脚本的一般步骤。任何优秀的脚本都应该具有帮助和输入参数。并且写一个伪脚本(framework.sh),该脚本包含了大多数脚本都需要的框架结构,是一个非常不错的主意。这时候,在写一个新的脚本时我们只需要执行一下copy命令:

cp framework.sh myscript

 然后再插入自己的函数。

  让我们再看两个例子:

  二进制到十进制的转换

  脚本 b2d 将二进制数 (比如 1101)转换为相应的十进制数。这也是一个用expr命令进行数学运算的例子:

#!/bin/sh

# vim: set sw=4 ts=4 et:

help()

{

 cat

 

--------------------------------------------------------------------------------------------------

--------------------------------------------------------------------------------------------------

shell编程简介

作者: 何斌武 (

hbwork@dlut.edu.cn

,大连理工大学网络中心,April 1999.)

 

 

Bourne Shell

介绍:Bourne Shell基础及其他很多有用的特性,shell编程及组织。

主要内容:

.shell基础基本介绍,环境,选项,特殊字符

.shell变量用户定义变量,环境变量,位置变量(shell参数)

.shell script编程

条件测试,循环及重复控制

.shell定制

1.shell基础知识

作者:Stephen BourneBell实验室开发

建议:man sh 查看相关UNIX上的改进或特性

(1)shell提示符及其环境

/etc/passwd文件

提示符:$

/etc/profile $HOME/.profile

(2)shell执行选项

-n 测试shell script语法结构,只读取shell script但不执行

-x 进入跟踪方式,显示所执行的每一条命令,用于调度

-a Tag all variables for export

-c "string" strings中读取命令

-e 非交互方式

-f 关闭shell文件名产生功能

-h locate and remember functions as defind

-i 交互方式

-k 从环境变量中读取命令的参数

-r 限制方式

-s 从标准输入读取命令

-t 执行命令后退出(shell exits)

-u 在替换中如使用未定义变量为错误

-v verbose,显示shell输入行

这些选项可以联合使用,但有些显然相互冲突,如-e-i.

(3)受限制shell(Restircted Shell)

sh -r /bin/rsh

不能执行如下操作:cd, 更改PATH,指定全路径名,输出重定向,因此可以提供一个较

好的控制和安全机制。通常rsh用于应用型用户及拨号用户,这些用户通常是看不到提

示符的。通常受限制用户的主目录是不可写的。

不足:如果用户可以调用sh,rsh的限制将不在起作用,事实上如果用户在vimore

程序中调用shell,而这时rsh的限制将不再起作用。

(4)set改变 shell选项

用户可以在$提示符下用set命令来设置或取消shell的选项。使用-设置选项,+取消相应

选项,大多数UNIX系统允许a,e,f,h,k,n,u,vx的开关设置/取消。

set -xv

启动跟踪方式;显示所有的命令及替换,同样显示输入。

set -tu

关闭在替换时对未定义变量的检查。

使用echo $-显示所有已设置的shell选项。

(5)用户启动文件 .profile

PATH=$PATH:/usr/loacl/bin; export PATH

(6)shell环境变量

CDPATH 用于cd命令的查找路径

HOME /etc/passwd文件中列出的用户主目录

IFS Internal Field Separator,默认为空格,tab及换行符

MAIL /var/mail/$USERNAME mail等程序使用

PATH

PS1PS2默认提示符($)及换行提示符(>)

TERM 终端类型,常用的有vt100,ansi,vt200,xterm

示例:$PS1="test:";export PS1

test: PS1="\$";export PS1

$echo $MAIL

/var/mail/username

(7)保留字符及其含义

$ shell变量名的开始,如$var

| 管道,将标准输出转到下一个命令的标准输入

# 注释开始

& 在后台执行一个进程

?匹配一个字符

* 匹配0到多个字符(DOS不同,可在文件名中间使用,并且含.)

$- 使用set及执行时传递给shell的标志位

$! 最后一个子进程的进程号

$# 传递给shell script的参数个数

$* 传递给shell script的参数

$@ 所有参数,个别的用双引号括起来

$? 上一个命令的返回代码

$0 当前shell的名字

$n (n:1-) 位置参数

$$ 进程标识号(Process Identifier Number, PID)

>file 输出重定向

`command` 命令替换,如 filename=`basename /usr/local/bin/tcsh`

>>fiile 输出重定向,append

转义符及单引号:

$echo "$HOME $PATH"

/home/hbwork /opt/kde/bin:/usr/local/bin:/bin:/usr/bin:/usr/X11R6/bin:

$echo '$HOME $PATH'

$HOME $PATH

$echo \$HOME $PATH

$HOME /opt/kde/bin:/usr/local/bin:/bin:/usr/bin:/usr/X11R6/bin:/home/hbwork/bin

其他:

$dir=ls

$$dir

$alias dir ls

$dir

ls > filelist

ls >> filelist

wc -l

2. shell变量

变量:代表某些值的符号,如$HOME,cd命令查找$HOME,在计算机语言中可以使用变量可以

进行多种运算和控制。

Bourne Shell有如下四种变量:

.用户自定义变量

.位置变量即 shell script之参数

.预定义变量(特殊变量)

.环境变量(参考shell定制部分)

(1)用户自定义变量(数据的存储)

$ COUNT=1

$ NAME="He Binwu"

技巧:因为大部分UNIX命令使用小写字符,因此在shell编程中通常使用全大写变量,

当然这并不是强制性的,但使用大写字符可以在编程中方便地识别变量。

变量的调用:在变量前加$

$ echo $HOME

/home/hbwork

$ WEEK=Satur

$ echo Today is $WEEKday

Today is

$echo Today is ${WEEK}day

Today is Saturday

Shell变量赋值从右从左进行(Linux Shell/bash从左向右赋值!)

$ X=$Y Y=y

$ echo $X

y

$ Z=z Y=$Z

$ echo $Y

$

使用unset命令删除变量的赋值

$ Z=hello

$ echo $Z

hello

$ unset Z

$ echo $Z

$

有条件的命令替换

Bourne Shell中可以使变量替换在特定条件下执行,即有条件的环境变量替换。

这种变量替换总是用大括号括起来的。

.设置变量的默认值

在变量未赋值之前其值为空。Bourne Shell允许对变量设置默认值,其格式如下:

${variable:-defaultvalue}

例:

$ echo Hello $UNAME

Hello

$ echo Hello ${UNAME:-there}

Hello there

$ echo $UNAME #变量值并未发生变化

$ UNAME=hbwork

$ echo Hello ${UNAME:-there}

Hello hbwork

$

.另一种情况:改变变量的值,格式如下:

${variable:=value}

例:

$ echo Hello $UNAME

Hello

$ echo Hello ${UNAME:=there}

Hello there

$ echo $UNAME #变量值并未发生变化

there

$

.变量替换中使用命令替换

$USERDIR=${$MYDIR:-`pwd`}

.在变量已赋值时进行替换 ${variable:+value}

.带有错误检查的有条件变量替换

${variable:?value}

例:

$ UNAME=

$ echo ${UNAME:?"UNAME has not been set"}

UNAME: UNAME has not been set

$ echo ${UNAME:?}

UNAME: parameter null or not set

(2)位置变量(Shell参数)

shell script中位置参数可用$1..$9表示,$0表示内容通常为当前执行程序的文件名。

.防止变量值被替换 readonly variable

.使用export命令输出变量,使得变量对子shell可用,当shell执行一下程序时,shell

将为其设置一个新的环境让其执行,这称之了subshell.Bourne Shell中变量通常

被认为是本地变量,也就是说在对其赋值之外的shell环境之外是不认识此变量的。使

exportsubshell可用。

例:对变量PS1export操作,shell的提示符将发生变化。

$ PS1=`hostname`$

peony$sh

$ echo $PS1

$

3.Shell Script编程

目的:使用UNIX所提供的最常用工具来完成所需复杂任务的强大功能。

(1)最简单的Shell编程

$ls -R / |grep myname |more

每天数据的备份:

$ cd /usr/yourname; ls * |cpio -o > /dev/rmt/0h

书写程序的目的是一次编程,多次使用(执行)!

$ cat > backup.sh

cd /home/hbwork

ls * | cpio -o > /dev/rmt/0h

^D

执行:

$ sh backup.sh

或:

$ chmod +x backup.sh

$ ./backup.sh

技巧:在shell script中加入必要的注释,以便以后阅读及维护。

(2)shell是一个(编程)语言

同传统的编程语言一样,shell提供了很多特性,这些特性可以使你的shell script

编程更为有用,如:数据变量、参数传递、判断、流程控制、数据输入和输出,子

程序及以中断处理等。

. shell编程中使用数据变量可以将程序变量更为通用,如在上面backup.sh中:

cd $WORKDIR

ls * | cpio -o > /dev/rmt/0h

. Shell编程中的注释以#开头

. shell变量进行数字运算,使用expr命令

expr integer operator integer

其中operator+ - * / %,但对*的使用要用转义符\,如:

$expr 4 \* 5

20

$int=`expr 5 + 7`

$echo $int

12

(3)Shell编程的参数传递,可通过命令行参数以及交互式输入变量(read)

restoreall.sh backup.sh程序的备份磁带进行恢复

$cat > restoreall.sh

cd $WORKDIR

cpio -i

$restore1 file1

恢复多个文件restoreany :

#restoreany --program to restore a single file

cd $WORKDIR

cpio -i $*

$ restoreany file1 file2 file3

(4)条件判断

. if-then语句,格式如下:

if command_1

then

command_2

command_3

fi

command_4

if-then语句中使用了命令返回码$?,即当command_1执行成功时才执行command_2

command_3,command_4总是执行.

示例程序unload: 在备份成功时删除原始文件,带有错误检查

cd $1

#备份时未考虑不成功的情况!

ls -a | cpio -o > /dev/rmt/0h

rm -rf *

改进如下:

#当使用了管道命令时,管理命令的返回代码为最后一个命令的返回代码

if ls -a | cpio -o > /dev/rmt/0h

then

rm -rf *

fi

. if-then-else语句

if command_1

then

command_2

else

command_3

fi

技巧: 由于shell对命令中的多余的空格不作任何处理,一个好的程序员会用这一特性

对自己的程序采用统一的缩进格式,以增强自己程序的可读性.

. 使用test命令进行进行条件测试

格式: test conditions

test在以下四种情况下使用: a.字符比较 b.两个整数值的比较

c. 文件操作,如文件是否存在及文件的状态等

d. 逻辑操作,可以进行and/or,与其他条件联合使用

a. 测试字符数据: shell变量通常民政部下均作为字符变量

str1 = str2 二者相长,相同

str1 != str2 不同

-n string string不为空(长度不为零)

-z string string为空

string string不为空

:

$ str1=abcd #在含有空格时必须用引号括起来

$ test $str1=abcd

$ echo $?

0

$ str1="abcd "

$ test $str1=abcd

$ echo $?

1

Note: test处理含有空格的变量时最好用引号将变量括起来,否则会出现错误的结果,

因为shell在处理命令行时将会去掉多余的空格,而用引号括起来则可以防止

shell去掉这些空格.

:

$ str1=" "

$ test $str1

$ echo $?

1

$ test "$str1"

$ echo $?

0

$ test -n $str1

test: argument expected

$ test -n "$str1"

$ echo $?

0

$

b. 整数测试: testexpr相同,可以将字符型变量转换为整数进行操作,expr进行

整数的算术运算,test则进行逻辑运算.

表达式说明

---------------------------------------

int1 -eq int2 相等?

int1 -ne int2 不等?

int1 -gt int2 int1 > int2 ?

int1 -ge int2 int1 >= int2 ?

int1 -lt int2 int1

:

$ int1=1234

$ int2=01234

$ test $int1 -eq $int2

$ echo $?

0

c. 文件测试:检查文件状态如存在及读写权限等

-r filename 用户对文件filename有读权限?

-w filename 用户对文件filename有写权限?

-x filename 用户对文件filename有可执行权限?

-f filename 文件filename为普通文件?

-d filename 文件filename为目录?

-c filename 文件filename为字符设备文件?

-b filename 文件filename为块设备文件?

-s filename 文件filename大小不为零?

-t fnumb 与文件描述符fnumb(默认值为1)相关的设备是一个终端设备?

d. 测试条件之否定,使用!

:

$ cat /dev/null > empty

$ test -r empty

$ echo $?

0

$ test -s empty

1

$ test ! -s empty

$ echo $?

0

e. 测试条件之逻辑运算

-a And

-o Or

: $ test -r empty -a -s empty

$ echo $?

1

f. 进行test测试的标准方法

因为test命令在 shell编程中占有很重要的地位,为了使shell能同其他编程语言一样

便于阅读和组织, Bourne Shell在使用test测试时使用了另一种方法:用方括号将整个

test测试括起来:

$ int1=4

$ [ $int1 -gt 2 ]

$ echo $?

0

: 重写unload程序,使用test测试

#!/bin/sh

#unload - program to backup and remove files

#syntax: unload directory

#check arguments

if [ $# -ne 1 ]

then

echo "usage: $0 directory"

exit 1

fi

#check for valid directory name

if [ ! -d "$1" ]

then

echo "$1 is not a directory"

exit 2

fi

cd $1

ls -a | cpio -o > /dev/rmt/0h

if [ $? -eq 0 ]

then

rm -rf *

else

echo "A problem has occured in creating backup"

echo "The directory will not be ereased"

echo "Please check the backup device"

exit 3

fi

# end of unload

在如上示例中出现了exit, exit有两个作用:一是停止程序中其他命令的执行,二是

设置程序的退出状态

g. if嵌套及elif结构

if command

then

command

else

if command

then

command

else

if command

then

command

fi

fi

fi

改进:使用elif结构

if command

then

command

elif command

then

command

elif command

then

command

fi

elif结构同if结构类似,但结构更清淅,其执行结果完全相同.

h.交互式从键盘读入数据

使用read语句,其格式如下:

read var1 var2 ... varn

read将不作变量替换,但会删除多余的空格,直到遇到第一个换行符(回车),

并将输入值依次赋值给相应的变量。

例:

$ read var1 var2 var3

Hello my friends

$ echo $var1 $var2 $var3

Hello my friends

$ echo $var1

Hello

$ read var1 var2 var3

Hello my dear friends

$ echo $var3

dear friends

shell script中可使用read语句进行交互操作:

...

#echo -n message输出结果后不换行

echo -n "Do you want to continue: Y or N"

read ANSWER

if [ $ANSWER=N -o $ANSWER=n ]

then

exit

fi

i. case结构:结构较elif-then结构更清楚

比较if-then语句:

if [ variable1 = value1 ]

then

command

command

elif [ variable1 = value2 ]

then

command

command

elif [ variable1 = value3 ]

then

command

command

fi

相应的case结构:

case value in

pattern1)

command

command;;

pattern2)

command

command;;

...

patternn)

command;

esac

* case语句只执行第一个匹配模式

例:使用case语句建立一个菜单选择shell script

#Display a menu

echo _

echo "1 Restore"

echo "2 Backup"

echo "3 Unload"

echo

#Read and excute the user's selection

echo -n "Enter Choice:"

read CHOICE

case "$CHOICE" in

1) echo "Restore";;

2) echo "Backup";;

3) echo "Unload";;

*) echo "Sorry $CHOICE is not a valid choice

exit 1

esac

在上例中,*指默认匹配动作。此外,case模式中也可以使用逻辑操作,如下所示:

pattern1 | pattern2 ) command

command ;;

这样可以将上面示例程序中允许用户输入数字或每一个大写字母。

case "$CHOICE" in

1|R) echo "Restore";;

2|B) echo "Backup";;

3|U) echo "Unload";;

*) echo "Sorry $CHOICE is not a valid choice

exit 1

esac

(5)循环控制

while循环:

格式:

while command

do

command

command

command

...

done

例:计算15的平方

#!/bin/sh

#

#Filename: square.sh

int=1

while [ $int -le 5 ]

do

sq=`expr $int \* $int`

echo $sq

int=`expr $int + 1`

done

echo "Job completed"

$ sh square.sh

1

4

9

16

25

Job completed

until循环结构:

格式:

until command

do

command

command

....

command

done

示例:使用until结构计算1-5的平方

#!/bin/sh

int=1

until [ $int -gt 5 ]

do

sq=`expr $int \* $int`

echo $sq

int=`expr $int + 1`

done

echo "Job completed"

使用shift对不定长的参数进行处理

在以上的示例中我们总是假设命令行参数唯一或其个数固定,或者使用$*将整个命令

行参数传递给shell script进行处理。对于参数个数不固定并且希望对每个命令参数

进行单独处理时则需要shift命令。使用shift可以将命令行位置参数依次移动位置,

$2->$1, $3->$2.在移位之前的第一个位置参数$1在移位后将不在存在。

示例如下:

#!/bin/sh

#

#Filename: shifter

until [ $# -eq 0 ]

do

echo "Argument is $1 and `expr $# - 1` argument(s) remain"

shift

done

$ shifter 1 2 3 4

Argument is 1 and 3 argument(s) remain

Argument is 2 and 2 argument(s) remain

Argument is 3 and 1 argument(s) remain

Argument is 4 and 0 argument(s) remain

$

使用shift时,每进行一次移位,$#1,使用这一特性可以用until循环对每个参

数进行处理,如下示例中是一个求整数和的shell script:

#!/bin/sh

# sumints - a program to sum a series of integers

#

if [ $# -eq 0 ]

then

echo "Usage: sumints integer list"

exit 1

fi

sum=0

until [ $# -eq 0 ]

do

sum=`expr $sum + $1`

shift

done

echo $sum

$ sh sumints 324 34 34 12 34

438

$

使用shift的另一个原因是Bourne Shell的位置参数变量为$1~$9,因此通过位置变量

只能访问前9个参数。但这并不等于在命令行上最多只能输入9个参数。此时如果想访问

9个参数之后的参数,就必须使用shift.

另外shift后可加整数进行一次多个移位,如:

shift 3

. for循环

格式:

for var in arg1 arg2 ... argn

do

command

....

command

done

示例:

$ for letter in a b c d e; do echo $letter;done

a

b

c

d

e

对当前目录下的所有文件操作:

$ for i in *

do

if [ -f $i ]

then

echo "$i is a file"

elif [ -d $i ]

echo "$i is a directory"

fi

done

求命令行上所有整数之和:

#!/bin/sh

sum=0

for INT in $*

do

sum=`expr $sum + $INT`

done

echo $sum

从循环中退出: breakcontinue命令

break 立即退出循环

continue 忽略本循环中的其他命令,继续下一下循环

shell编程中有时我们要用到进行无限循环的技巧,也就是说这种循环一直执行碰

breakcontinue命令。这种无限循环通常是使用truefalse命令开始的。UNIX

系统中的true总是返加0值,而false则返回非零值。如下所示:

#一直执行到程序执行了break或用户强行中断时才结束循环

while true

do

command

....

command

done

上面所示的循环也可以使用until false,如下:

until false

do

command

....

command

done

在如下shell script中同时使用了continue,break以及case语句中的正规表达式用法:

#!/bin/sh

# Interactive program to restore, backup, or unload

# a directory

echo "Welcome to the menu driven Archive program"

while true

do

# Display a Menu

echo

echo "Make a Choice from the Menu below"

echo _

echo "1 Restore Archive"

echo "2 Backup directory"

echo "3 Unload directory"

echo "4 Quit"

echo

# Read the user's selection

echo -n "Enter Choice: "

read CHOICE

case $CHOICE in

[1-3] ) echo

# Read and validate the name of the directory

echo -n "What directory do you want? "

read WORKDIR

if [ ! -d "$WORKDIR" ]

then

echo "Sorry, $WORKDIR is not a directory"

continue

fi

# Make the directory the current working directory

cd $WORKDIR;;

4) :;; # :为空语句,不执行任何动作

*) echo "Sorry, $CHOICE is not a valid choice"

continue

esac

case "$CHOICE" in

1) echo "Restoring..."

cpio -i

2) echo "Archiving..."

ls | cpio -o >/dev/rmt/0h;;

3) echo "Unloading..."

ls | cpio -o >/dev/rmt/0h;;

4) echo "Quitting"

break;;

esac

#Check for cpio errors

if [ $? -ne 0 ]

then

echo "A problem has occurred during the process"

if [ $CHOICE = 3 ]

then

echo "The directory will not be erased"

fi

echo "Please check the device and try again"

continue

else

if [ $CHOICE = 3 ]

then

rm *

fi

fi

done

(6)结构化编程:定义函数

同其他高级语言一样,shell也提供了函数功能。函数通常也称之为子过程(subroutine),

其定义格式如下:

funcname()

{

command

...

command; #分号

}

定义函数之后,可以在shell中对此函数进行调用,使用函数定义可以将一个复杂的程序分

为多个可管理的程序段,如下所示:

# start program

setup ()

{ command list ; }

do_data ()

{ command list ; }

cleanup ()

{ command list ; }

errors ()

{ command list ; }

setup

do_data

cleanup

# end program

技巧:

. 在对函数命名时最好能使用有含义的名字,即函数名能够比较准确的描述函数所完成

的任务。

. 为了程序的维护方便,请尽可能使用注释

使用函数的另一个好处就是可以在一个程序中的不同地方执行相同的命令序列(函数),

如下所示:

iscontinue()

{

while true

do

echo -n "Continue?(Y/N)"

read ANSWER

case $ANSWER in

[Yy]) return 0;;

[Nn]) return 1;;

*) echo "Answer Y or N";;

esac

done

}

这样可以在shell编程中调用iscontinue确定是否继续执行:

if iscontinue

then

continue

else

break

fi

** shell函数与shell程序非常相似,但二者有一个非常重要的差别:shell程序是由子shell

执行的,而shell函数则是作为当前shell的一部分被执行的,因此在当前shell中可以改

变函数的定义。此外在任意shell(包括交互式的shell)中均可定义函数,如:

$ dir

dir: not found

$ dir () { ls -l ;}

$ dir

total 5875

-rw-r--r-- 1 hbwork 100 Nov 10 23:16 doc

-rw-r--r-- 1 hbwork 2973806 Nov 10 23:47 ns40docs.zip

-rw-r--r-- 1 hbwork 1715011 Nov 10 23:30 ns840usr.pdf

-rw-r--r-- 1 hbwork 1273409 Sep 23 1998 radsol21b6.tar.Z

-rw-r--r-- 1 hbwork 7526 Nov 10 23:47 wget-log

-rw-r--r-- 1 hbwork 1748 Nov 13 21:51 wget-log.1

$ unset dir

$ dir () {

> echo "Permission Link Owner Group File_SZ LastAccess FileName"

> echo "-----------------------------------------------------------"

> ls -l $*;

> }

$ dir

Permission Link Owner Group File_SZ LastAccess FileName

-----------------------------------------------------------

total 5875

-rw-r--r-- 1 hbwork 100 Nov 10 23:16 doc

-rw-r--r-- 1 hbwork 2973806 Nov 10 23:47 ns40docs.zip

-rw-r--r-- 1 hbwork 1715011 Nov 10 23:30 ns840usr.pdf

-rw-r--r-- 1 hbwork 1273409 Sep 23 1998 radsol21b6.tar.Z

-rw-r--r-- 1 hbwork 7526 Nov 10 23:47 wget-log

-rw-r--r-- 1 hbwork 1748 Nov 13 21:51 wget-log.1

通常情况下,shell script是在子shell中执行的,困此在此子shell中对变量所作的

修改对父shell不起作用。点(.)命令使用shell在不创建子shell而由当前shell读取

并执行一个shell script,可以通过这种方式来定义函数及变量。此外点(.)命令最

常用的功能就是通过读取.profile来重新配置初始化login变量。如下所示:

$ . .profile

(csh相对于.命令的是source命令).

(7)使用And/Or结构进行有条件的命令执行

And , 仅当第一个命令成功时才有执行后一个命令,如同在逻辑与表达式中如果前面的

结果为真时才有必要继续运算,否则结果肯定为假。

格式如下:

command1 && command2

例:rm $TEMPDIR/* && echo "File successfully removed"

等价于

if rm $TEMPDIR/*

then

echo "File successfully removed"

fi

Or, AND相反,仅当前一个命令执行出错时才执行后一条命令

例: rm $TEMPDIR/* || echo "File not removed"

等价与:

if rm $TEMPDIR/*

then

command

else

echo "File not removed"

fi

混合命令条件执行

command1 && command2 && command3

command1, command2成功时才执行command3

command1 && command2 || comamnd3

仅当command1成功,command2失败时才执行command3

当然可以根据自己的需要进行多种条件命令的组合,在此不多讲述。

(8) 使用getopts命令读取unix格式选项

UNIX格式选项指如下格式的命令行参数:

command -options parameters

使用格式:

getopts option_string variable

具体使用方法请参考getopts的在线文档(man getopts).

示例如下:

#newdate

if [ $# -lt 1 ]

then

date

else

while getopts mdyDHMSTjJwahr OPTION

do

case $OPTION

in

m) date '+%m ';; # Month of Year

d) date '+%d ';; # Day of Month

y) date '+%y ';; # Year

D) date '+%D ';; # MM/DD/YY

H) date '+%H ';; # Hour

M) date '+%M ';; # Minute

S) date '+%S ';; # Second

T) date '+%T ';; # HH:MM:SS

j) date '+%j ';; # day of year

J) date '+%y%j ';;# 5 digit Julian date

w) date '+%w ';; # Day of the Week

a) date '+%a ';; # Day abbreviation

h) date '+%h ';; # Month abbreviation

r) date '+%r ';; # AM-PM time

\?) echo "Invalid option $OPTION";;

esac

done

fi

$ newdate -J

94031

$ newdate -a -h -d

Mon

Jan

31

$ newdate -ahd

Mon

Jan

31

$

示例程序:复制程序

# Syntax: duplicate [-c integer] [-v] filename

# where integer is the number of duplicate copies

# and -v is the verbose option

COPIES=1

VERBOSE=N

while getopts vc: OPTION

do

case $OPTION

in

c) COPIES=$OPTARG;;

v) VERBOSE=Y;;

\?) echo "Illegal Option"

exit 1;;

esac

done

if [ $OPTIND -gt $# ]

then

echo "No file name specified"

exit 2

fi

shift `expr $OPTIND -1`

FILE=$1

COPY=0

while [ $COPIES -gt $COPY ]

do

COPY=`expr $COPY + 1`

cp $FILE ${FILE}${COPY}

if [ VERBOSE = Y ]

then

echo ${FILE}${COPY}

fi

done

$ duplicate -v fileA

fileA1

$ duplicate -c 3 -v fileB

fileB1

fileB2

fileB3

4. Shell的定制

通常使用shell的定制来控制用户自己的环境,比如改变shell的外观(提示符)以及增强

自己的命令。

(1)通常环境变量来定制shell

通常改变环境变量可以定制shell的工作环境。shell在处理信息时会参考这些环境变量

,改变环境变量的值在一定程度上改变shell的操作方式,比如改变命令行提示符。

.使用IFS增加命令行分隔符

默认状态下shell的分隔符为空格、制表符及换行符,但可以通过改变IFS的值加入自己

的分隔符。如下所示:

$ IFS=":"

$ echo:Hello:my:Friend

Hello my Friend

(2)加入自己的命令及函数

如下程序:

#Directory and Prompt change program

#Syntax: chdir directory

if [ ! -d "$1" ]

then

echo "$1 is not a directory"

exit 1

fi

cd $1

PS1=`pwd`$

export PS1

$ chdir /usr/home/teresa

$

但此程序在执行时系统提示符并不会改变,因为此程序是在子shell中执行的。因此其变量

对当前shell并无影响,要想对当前shell起作用,最好是将此作为函数写在自己的.profile

或建立自己的个人函数文件.persfuncs

#Personal function file persfuncs

chdir()

{

#Directory and Prompt change program

#Syntax: chdir directory

if [ ! -d "$1" ]

then

echo "$1 is not a directory"

exit 1

fi

cd $1

PS1=`pwd`$

export PS1;

}

再执行:

$ . .persfuncs

$ chdir temp

/home/hbbwork/temp$

也可在自己的.profile文件中用 . .persfuncs调用.persfuncs.

说明:在bash/tcsh中已经使用别名,相对而言别名比此方法更为方便。

5. 有关shell的专门讨论

(1)shell程序的调试

切记:程序员(人)总是会犯错误的,而计算机是不会错的。

使用-x进行跟踪执行,执行并显示每一条指令。

(2)命令组

用小括号将一组命令括起来,则这些命令会由子shell来完成;而{command_list;}则在当

shell中执行。这两者的主要区别在于其对shell变量的影响,子shell执行的命令不会

影响当前shell中的变量。

$ NUMBER=2

$ (A=2;B=2;NUMBER=`expr $A + $B`; echo $NUMBER)

4

$ echo $NUMBER

2

$ { A=2;B=2;NUMBER=`expr $A + $B`; echo $NUMBER; }

4

$ echo $NUMBER

4

总结:

在本章中讲述了Bourne Shell的基本知识,使用shell变量,shell script基础。这些概念

对于理解学习Korn Shell, csh以及其他script编程都是非常有用的。

很多OS都有不少语言及一些script功能,但很少有象UNIX SHELL这样灵活强大的script

本语言能力。

对于系统管理员或程序员来说,熟练地使用shell script将对日常工作(系统维护及管理)

非常有用,如果你想作一个合格的系统管理员,强烈建议你进一步深入的了解和使用

shell.

另外,对于系统管理员来说,PERL也是一个必不可少的script编程语言,尤其是对于处理

文本格式的各种文件,PERL具有shell, awk, sed, grep等的功能,但使用起来更为灵活,

功能也更强大。大家可以参考“Perl By Examples"来学习和使用PERL

 

原创粉丝点击