linux脚本编写基础

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"

这并不会打印出"thisis the 2nd",而仅仅打印"thisis 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:计算文件行数计算文件中的单词数计算文件中的字符数

  cpsourcefile destfile:文件拷贝

  mvoldname newname :重命名文件或移动文件

  rmfile:删除文件

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

  cut-b colnum file:指定欲显示的文件内容范围,并将它们输出到标准输出设备比如:输出

每行第5个到第9个字符cut-b5-9 file.txt千万不要和cat命令混淆,

这是两个完全不同的命令

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

  filesomefile:得到文件类型

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

  sortfile.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

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

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

  headfile:打印文本文件开头几行

  tailfile :打印文本文件末尾几行

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

结果输出到标准输出(屏幕)。该命令采用正则表达式(见参考)进行搜索。

不要和shell中的通配符相混淆。比如:将linuxfocus替换为

LinuxFocus cattext.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

命令输出结果为:AdamBor, IndiaKerry Miller, USA

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

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

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

grep "hello" file.txt | wc -l

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

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

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

  >写入文件并覆盖旧文件

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

反短斜线

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

命令:

find . -mtime -1 -type f -print

  用来查找过去24小时(-mtime–2则表示过去48小时)内修改过的文件。如果您

想将所有查找到的文件打一个包,则可以使用以下脚本:

#!/bin/sh

# The ticks are backticks (`) notnormal 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是否相等

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

  直接执行以下脚本:

#!/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 shadowpasswors”。同样或操作(||)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:182001, 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 beuncompressed 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" "GnuHurd" "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将运行直到表达式测试为真。willrun 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 ofRPM 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

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

如果您运行showrpmopenssh.rpm w3m.rpm webgrep.rpm

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


5. 引号

在向程序传递任何参数之前,程序会扩展通配符和变量。这里所谓扩展的意思是程序会把通配符

(比如*)替换成合适的文件名,它变量替换成变量值。为了防止程序作这种替换,您可以使用

引号:让我们来看一个例子,假设在当前目录下有一些文件,两个jpg文件,mail.jpgtux.jpg

1.2 编译SHELL脚本

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

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

  这将打印出"mail.jpgtux.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

当要将几行文字传递给一个命令时,heredocuments(译者注:目前还没有见到过对该词适合的翻译)

一种不错的方法。对每个脚本写一段帮助性的文字是很有用的,此时如果我们四有那个here documents

就不必用echo函数一行行输出。一个"Here document" << 开头,后面接上一个字符串,这个字符串

还必须出现在heredocument的末尾。下面是一个例子,在该例子中,我们对多个文件进行重命名,并且

使用heredocuments打印帮助:

#!/bin/sh

# we have less than 3 arguments. Printthe help text:

if [ $# -lt 3 ] ; then

cat <

ren -- renames a number of files usingsed regular expressions

USAGE: ren 'regexp' 'replacement'files...

EXAMPLE: rename all *.HTM files in*.html:

 ren'HTM$' 'html' *.HTM

HELP

 exit0

fi

OLD="$1"

NEW="$2"

# The shift command removes oneargument from the list of

# command line arguments.

shift

shift

# $* contains now all the files:

for file in $*; do

  if[ -f "$file" ] ; then

   newfile=`echo"$file" | sed "s/${OLD}/${NEW}/g"`

   if[ -f "$newfile" ]; then

    echo"ERROR: $newfile exists already"

   else

    echo"renaming $file to $newfile ..."

    mv"$file" "$newfile"

   fi

  fi

done

 这是一个复杂一些的例子。让我们详细讨论一下。第一个if表达式判断输入命令行参数是

否小于3(特殊变量$#表示包含参数的个数)。如果输入参数小于3个,则将帮助文字传递

cat命令,然后由cat命令将其打印在屏幕上。打印帮助文字后程序退出。如果输入参数等

于或大于3个,我们就将第一个参数赋值给变量OLD,第二个参数赋值给变量NEW。下一步,我

们使用shift命令将第一个和第二个参数从参数列表中删除,这样原来的第三个参数就成为参

数列表$*的第一个参数。然后我们开始循环,命令行参数列表被一个接一个地被赋值给变量$file

接着我们判断该文件是否存在,如果存在则通过sed命令搜索和替换来产生新的文件名。然后

将反短斜线内命令结果赋值给newfile。这样我们就达到了我们的目的:得到了旧文件名和新

文件名。然后使用mv命令进行重命名。


4)函数

如果您写了一些稍微复杂一些的程序,您就会发现在程序中可能在几个地方使用了相同的代码,

并且您也会发现,如果我们使用了函数,会方便很多。一个函数是这个样子的:

functionname()

{

# inside the body $1 is the firstargument given to the function

# $2 the second ...

body

}

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

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

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

#!/bin/sh

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

help()

{

  cat<

xtitlebar -- change the name of anxterm, gnome-terminal or kde konsole

USAGE: xtitlebar [-h]"string_for_titelbar"

OPTIONS: -h help text

EXAMPLE: xtitlebar "cvs"

HELP

  exit0

}

# in case of error or if -h is given wecall the function help:

[ -z "$1" ] && help

[ "$1" = "-h" ] &&help

# send the escape sequence to changethe xterm titelbar:

echo -e "33]0;$107"

#

在脚本中提供帮助是一种很好的编程习惯,这样方便其他用户(和您)使用和理解脚本。

命令行参数

  我们已经见过$*$1, $2 ... $9 等特殊变量,这些特殊变量包含了用户从命令

行输入的参数。迄今为止,我们仅仅了解了一些简单的命令行语法(比如一些强制性的

参数和查看帮助的-h选项)。但是在编写更复杂的程序时,您可能会发现您需要更多的

自定义的选项。通常的惯例是在所有可选的参数之前加一个减号,后面再加上参数值(

比如文件名)

有好多方法可以实现对输入参数的分析,但是下面的使用case表达式的例子无遗是一个不错的方法。

#!/bin/sh

help()

{

 cat<

This is a generic command line parserdemo.

USAGE EXAMPLE: cmdparser -l hello -f ---somefile1 somefile2

HELP

 exit0

}

while [ -n "$1" ]; do

case $1 in

  -h)help;shift 1;; # function help is called

  -f)opt_f=1;shift 1;; # variable opt_f is set

  -l)opt_l=$2;shift 2;; # -l takes an argument -> 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 -- -somefile1somefile2

  返回的结果是:

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<

b2h -- convert binary to decimal

USAGE: b2h [-h] binarynum

OPTIONS: -h help text

EXAMPLE: b2h 111010

will return 58

HELP

 exit0

}

error()

{

  #print an error and exit

  echo"$1"

  exit1

}

lastchar()

{

  #return the last character of a string in $rval

  if[ -z "$1" ]; then

    #empty string

    rval=""

    return

  fi

  #wc puts some space behind the output this is why we need sed:

  numofchar=`echo-n "$1" | wc -c | sed 's/ //g' `

  #now cut out the last char

  rval=`echo-n "$1" | cut -b $numofchar`

}

chop()

{

  #remove the last character in string and return it in $rval

  if[ -z "$1" ]; then

    #empty string

    rval=""

    return

  fi

  #wc puts some space behind the output this is why we need sed:

  numofchar=`echo-n "$1" | wc -c | sed 's/ //g' `

  if[ "$numofchar" = "1" ]; then

    #only one char in string

    rval=""

    return

  fi

  numofcharminus1=`expr$numofchar "-" 1`

  #now cut all but the last char:

  rval=`echo-n "$1" | cut -b 0-${numofcharminus1}`

}

while [ -n "$1" ]; do

case $1 in

  -h)help;shift 1;; # function help is called

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

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

  *)break;;

esac

done

# The main program

sum=0

weight=1

# one arg must be given:

[ -z "$1" ] && help

binnum="$1"

binnumorig="$1"

while [ -n "$binnum" ]; do

  lastchar"$binnum"

  if[ "$rval" = "1" ]; then

    sum=`expr"$weight" "+" "$sum"`

  fi

  #remove the last position in $binnum

  chop"$binnum"

  binnum="$rval"

  weight=`expr"$weight" "*" 2`

done

echo "binary $binnumorig isdecimal $sum"

 该脚本使用的算法是利用十进制和二进制数权值(1,2,4,8,16,..),比如二进制"10"

以这样转换成十进制:

0 * 1 + 1 * 2 = 2

  为了得到单个的二进制数我们是用了lastchar函数。该函数使用wc–c计算字符个数,

然后使用cut命令取出末尾一个字符。Chop函数的功能则是移除最后一个字符。

文件循环程序

  或许您是想将所有发出的邮件保存到一个文件中的人们中的一员,但是在过了几个月

以后,这个文件可能会变得很大以至于使对该文件的访问速度变慢。下面的脚本rotatefile

可以解决这个问题。这个脚本可以重命名邮件保存文件(假设为outmail)为outmail.1

而对于outmail.1就变成了outmail.2等等等等...

#!/bin/sh

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

ver="0.1"

help()

{

  cat<

rotatefile -- rotate the file name

USAGE: rotatefile [-h] filename

OPTIONS: -h help text

EXAMPLE: rotatefile out

This will e.g rename out.2 to out.3,out.1 to out.2, out to out.1

and create an empty out-file

The max number is 10

version $ver

HELP

  exit0

}

error()

{

  echo"$1"

  exit1

}

while [ -n "$1" ]; do

case $1 in

  -h)help;shift 1;;

  --)break;;

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

  *)break;;

esac

done

# input check:

if [ -z "$1" ] ; then

error "ERROR: you must specify afile, use -h for help"

fi

filen="$1"

# rename any .1 , .2 etc file:

for n in 9 8 7 6 5 4 3 2 1; do

  if[ -f "$filen.$n" ]; then

    p=`expr$n + 1`

    echo"mv $filen.$n $filen.$p"

    mv$filen.$n $filen.$p

  fi

done

# rename the original file:

if [ -f "$filen" ]; then

  echo"mv $filen $filen.1"

  mv$filen $filen.1

fi

echo touch $filen

touch $filen

  这个脚本是如何工作的呢?在检测用户提供了一个文件名以后,我们进行一个91的循环。文件9被命名为10,文件8重命名为9等等。循环完成之后,我们将原始文件命名为文件1同时建立一个与原始文件同名的空文件。

调试

  最简单的调试命令当然是使用echo命令。您可以使用echo在任何怀疑出错的地方打印任何变量值。这也是绝大多数的shell程序员要花费80%的时间来调试程序的原因。Shell程序的好处在于不需要重新编译,插入一个echo命令也不需要多少时间。

  shell也有一个真实的调试模式。如果在脚本"strangescript"中有错误,您可以这样来进行调试:

sh -x strangescript

  这将执行该脚本并显示所有变量的值。

  shell还有一个不需要执行脚本只是检查语法的模式。可以这样使用:

sh -n your_script

  这将返回所有语法错误


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

自动重启脚本



假定需要实现重启的程序名为test,我们这里通过判断进程数目来判断程序是否正常。


ps -ef | grep "$1" | grep -v"grep" | wc –l是获取$1(本例中为test)的进程数,脚本根据进程数来决定下一步的操作。通过一个死循环,每隔1秒检查一次系统中的指定程序的进程数。


代码如下:

脚本check


#!/bin/sh


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

# 函数:CheckProcess

# 功能:检查一个进程是否存在

# 参数:$1 ---要检查的进程名称

# 返回:如果存在返回0,否则返回1.

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

CheckProcess()

{

# 检查输入的参数是否有效

if [ "$1" = "" ];

then

return 1

fi

#$PROCESS_NUM获取指定进程名的数目,为1返回0,表示正常,不为1返回1,表示有错误,需要重新启动

PROCESS_NUM=`ps -ef | grep "$1"| grep -v "grep" | wc -l`

if [ $PROCESS_NUM -eq 1 ];

then

return 0

else

return 1

fi

}


# 检查test实例是否已经存在

while [ 1 ] ; do

CheckProcess "test"

CheckQQ_RET=$?

if [ $CheckQQ_RET -eq 1 ];

then


# 杀死所有test进程,可换任意你需要执行的操作



killall -9 test

exec ./test &

fi

sleep 1

done



脚本start

加入limitcoredumpsize 102400,设置corefile的大小,一旦程序CoreDump,有迹可寻。在该脚本中后台执行check脚本,可以省去很多麻烦,


#!/bin/csh

limit coredumpsize 102400


./check &


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

shell语法

shell语法

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

?

&&左边的命令(命令1)返回真(即返回0,成功被执行)后,&&右边的命令(命令2)才能够被执行;换句话说,“如果这个命令执行成功&&那么执行这个命令”。


||则与&&相反。如果||左边的命令(命令1)未执行成功,那么就执行||右边的命令(命令2);或者换句话说,“如果这个命令执行失败了||那么就执行这个命令。

为了在当前shell中执行一组命令,可以用命令分隔符隔开每一个命令,并把所有的命令用圆括号()括起来。

如果使用{}来代替(),那么相应的命令将在子shell而不是当前shell中作为一个整体被执行,只有在{}中所有命令的输出作为一个整体被重定向时,其中的命令才被放到子shell中执行,否则在当前shell执行。

?

?

?

1.建立和运行shell程序

什么是shell程序呢?简单的说shell程序就是一个包含若干行

shell或者linux命令的文件.

象编写高级语言的程序一样,编写一个shell程序需要一个文本编辑器.VI.

在文本编辑环境下,依据shell的语法规则,输入一些shell/linux命令行,形成一个完整

的程序文件.

执行shell程序文件有三种方法

(1)#chmod +xfile(/etc/profile,加入exportPATH=${PATH}:~/yourpath,就可以在命令行下直接运行,像执行普通命令一样)

(2)#sh file

(3)# . file

(4)#source file

在编写shell,第一行一定要指明系统需要那种shell解释你的shell程序,:#!/bin/bash,

#! /bin/csh,/bin/tcsh,还是#!/bin/pdksh .

2.shell中的变量

(1)常用系统变量

$ # :保存程序命令行参数的数目

$ ? :保存前一个命令的返回码

$ 0 :保存程序名

$ * :("$1$2...")的形式保存所有输入的命令行参数

$ @ :("$1""$2"...)的形式保存所有输入的命令行参数

(2)定义变量

shell语言是非类型的解释型语言,不象用C++/JAVA语言编程时需要事先声明变量.给一

个变量赋值,实际上就是定义了变量.

linux支持的所有shell,都可以用赋值符号(=)为变量赋值.

:

abc=9 (bash/pdksh不能在等号两侧留下空格)

set abc = 9 (tcsh/csh)

由于shell程序的变量是无类型的,所以用户可以使用同一个变量时而存放字符时而存放

整数.

:

name=abc (bash/pdksh)

set name = abc (tcsh)

在变量赋值之后,只需在变量前面加一个$去引用.

:

echo $abc

(3)位置变量

当运行一个支持多个命令行参数的shell程序时,这些变量的值将分别存放在位置变量里.

其中第一个参数存放在位置变量1,第二个参数存放在位置变量2,依次类推...,shell保留

这些变量,不允许用户以令外的方式定义他们.同别的变量,$符号引用他们.

3.shell中引号的使用方法

shell使用引号(单引号/双引号)和反斜线("\")用于向shell解释器屏蔽一些特殊字符.

反引号(")shell则有特殊意义.

:

abc="how are you"(bash/pdksh)

set abc = "how are you"(tcsh)

这个命令行把三个单词组成的字符串howare you作为一个整体赋值给变量abc.

abc1='@LOGNAME,how are you!'(bash/pdksh)

set abc1='$LOGNAME,how are you!' (tcsh)

abc2="$LOGNAME,how are you!"(bash/pdksh)

set abc2="$LOGNAME,how are you!"(tcsh)

LOGNAME变量是保存当前用户名的shell变量,假设他的当前值是ang.执行完两条命令后,

abc1的内容是:$LOGNAME,how are you!.abc2的内容是;wang,how are you!.

象单引号一样,反斜线也能屏蔽所有特殊字符.但是他一次只能屏蔽一个字符.而不能屏蔽

一组字符.

反引号的功能不同于以上的三种符号.他不具有屏蔽特殊字符的功能.但是可以通过他将

一个命令的运行结果传递给另外一个命令.

:

contents=`ls` (bash/pdksh)

set contents = `ls` (tcsh)

4.shell程序中的test命令

bash/pdksh,命令test用于计算一个条件表达式的值.他们经常在条件语句和循环

语句中被用来判断某些条件是否满足.

test命令的语法格式:

test expression

或者

[expression]

test命令中,可以使用很多shell的内部操作符.这些操作符介绍如下:

(1)字符串操作符用于计算字符串表达式

test命令|含义

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

Str1 = str2 | str1str2相同时,返回True

Str1! = str2| str1str2不同时,返回True

Str | str不是空字符时,返回True

-n str | str的长度大于0,返回True

-z str | str的长度是0,返回True

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

(2)整数操作符具有和字符操作符类似的功能.只是他们的操作是针对整数

test表达式|含义

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

Int1 -eq int2|int1等于int2,返回True

Int1 -ge int2|int1大于/等于int2,返回True

Int1 -le int2|int1小于/等于int2,返回True

Int1 -gt int2|int1大于int2,返回True

Int1 -ne int2|int1不等于int2,返回True

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

(3)用于文件操作的操作符,他们能检查:文件是否存在,文件类型等

test表达式|含义

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

-d file |file是一个目录时,返回True

-f file |file是一个普通文件时,返回True

-r file |file是一个刻读文件时,返回True

-s file |file文件长度大于0,返回True

-w file |file是一个可写文件时,返回True

-x file |file是一个可执行文件时,返回True

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

(4)shell的逻辑操作符用于修饰/连接包含整数,字符串,文件操作符的表达式

test表达式|含义

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

! expr |expr的值是False,返回True

Expr1 -aexpr2|expr1,expr2值同为True,返回True

Expr1 -oexpr2|expr1,expr2的值至少有一个为True,返回True

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

注意:

tcsh shell不使用test命令,但是tcsh中的表达式同样能承担相同的功能.tcsh

支持的表达式于C中的表达式相同.通常使用在ifwhile命令中.

tcsh表达式|含义

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

Int1 <= int2 |int1小于/等于int2,返回True

Int1 >= int2 |int1大于/等于int2,返回True

Int1 < int2 |int1小于int2,返回True

Int1 > int2 |int1大于int2,返回True

Str1 == str2 |str1str2相同时,返回True

Str1 != str2 |str1str2不同时,返回True

-r file |file是一个可读文件时,返回True

-w file |file是一个可写文件时,返回True

-x file |file是一个可执行文件时,返回True

-e file |file存在时,返回True

-o file |file文件的所有者是当前用户时,返回True

-z file |file长度为0,返回True

-f file |file是一个普通文件时,返回True

-d file |file是一个目录时,返回True

Exp1 || exp2|exp1exp2的值至少一个为True,返回True

Exp1 && exp2|exp1exp2的值同为True,返回True

! exp |exp的值为False,返回True

5.条件语句

同其他高级语言程序一样,复杂的shell程序中经常使用到分支和循环控制结构,

bash,pdkshtcsh分别都有两种不同形式的条件语句:if语句和case语句.

(1)if语句

语法格式:

bash/pdksh用法:

if [expression1]

then

commands1

elif [expression2]

commands2

else

commands3

if

tcsh用法:

if (expression1) then

commands1

else if (expression2) then

commands2

else

commands3

endif

含义:expression1的条件为True,shell执行then后面的commands1命令;

expression1的条件为false并且expression2的条件满足为True,shell执行

commands2命令;expression1expressin2的条件值同为false,shell执行

commands3命令.if语句以他的反写fi结尾.

(2)case语句

case语句要求shell将一个字符串S与一组字符串模式P1,P2,...,Pn比较,S

某个模式Pi想匹配时,就执行相应的那一部分程序/命令.shellcase语句中字符

模式里可以包含象*这样的通配符.

语法格式:

bash/pdksh用法:

case string1 in

str1)

commands1;;

str2)

commands2;;

*)

commands3;;

esac

tcsh用法:

switch (string1)

case str1:

statements1

breaksw

case str2:

statements2

breaksw

default:

statements3

breaksw

endsw

含义:shell将字符串string1分别和字符串模式str1str2比较.如果string1str1匹配,

shell执行commands1的命令/语句;如果string11str2匹配,shell执行commands2的命令/

语句.否则shell将执行commands3的那段程序/命令.其中,每个分支的程序/命令都要以两个

分号(;结束.

6.循环语句

当需要重复的某些操作时,就要用到循环语句.

(1)for语句

大家知道在很多编程语言中for语句是最常见.shell中也不例外.for语句要求shell将包含

在这个语句中的一组命令连续执行一定的次数.

语法格式:

bash/pdksh

用法1:

for var1 in list

do

commands

done

含义:在这个for语句中,对应于list中的每个值,shell将执行一次commands代表的一组命令.

在整个循环的每一次执行中,变量var1将依此取list中的不同的值.

用法2:

for var1

do

setatements

done

含义:在这个for语句中,shell针对变量var1中的每一项分别执行一次statements代表的一组

命令.当使用这种形式的语句时,shell认为var1变量中包含了所有的位置变量,而位置变量中

存放着程序的命令行参数值.也就是说,他等价于下列形式:

for var1 in " $@"

do

statements

done

tcsh用法:

tcsh中没有for这个单词,for语句起同样功能的是foreach语句

foreach name (list)

commands

end

举例:

for file ;bash/pdksh

do

tr a-z A-Z<$file>file.caps

done

# ;tcsh

foreach file ( $ * )

tr a-z A-Z<$file>$file.caps

end


(2)while语句

while语句是shell提供的另一种循环语句.while语句指定一个表达式和一组命令.这个

语句使得shell重复执行一组命令,直到表达式的值为False为止.

语法格式:

while expression ;bash

do

statements

done

while (expression) ;tcsh

statements

end

举例:

count=1 ;bash

while [ -n "$ *"] ***

do

echo "this is a parameter number$count $1"

shift

count='expr $count + 1'

done

set count = 1 ;tcsh

while ( " $ * " ! = "")

echo "this is a parameter number$count $1"

shift

set count = 'expr $count + 1'

end

语句中shift命令的功能是将所有的命令行参数依次相左传递.

(3)until语句

untilwhile语句具有类似的语法格式和功能,不同的是whileexpression的值为True,

shell执行命令组;until中当expression的值为False,shell才执行那组命令.

语法格式:

until expression

do

commands

done

举例:

count=1

until [ -z " $ * "] ***

echo "this is a parameter number$count $1"

shift

count='expr $count + 1'

done

情注意上述例子中带***.while中的表达式:-n string,他的含义是当string不是空

字符串时,表达式的值为True;until中的表达式:-z string,他的含义是当string是空

字符串时,表达式的值为True.由此可见,两个程序对条件表达式的设置恰好是相反的.

(4)shift语句

bashtcsh都支持shift命令.shift将存放在位置变量中的命令行参数,依次向左传递.例如

位置变量当前值为:

$1=file1 $2=file2 $3=file3

执行一次shift命令后,位置变量的值为:

$1=file2 $2=file3

还可以在shift命令中指定位置变量转移的次数,:

shift n

例子:

while [ "$1"]

do

if [ "$1"="-i"]then

infile=" $2"

shift 2

else if [ "$1"="-o"]then

outfile="$2"

shift 2

else

echo "Program $0 does notrecognize option $1"

fi

done

tr a-z A-Z<$infile>$outfile

(5)select语句

select语句是pdksh提供的一个独特的循环语句.他不同于前面介绍的循环语句.他不是

反复计算一个条件表达式,并依据表达式的值决定是否执行一组命令.select的功能是自动的

生成一个简单的文本菜单.

语法格式:

select menu [in list_of_items]

do

commands

done

含义:当执行一个select语句时,pdksh分别为每个列在list_of_items中的成员建立一个菜单

选项.list_of_items既可以是一个包含多个选项的变量,也可以是直接列在程序中的一组选项

.如果语句中没有提供list_of_items,select语句将使用位置变量作为list_of_items.

举例:

select menuitem in pick1 pick2 pick3

do

echo "are you sure you want topick $menuitem"

read res ;接收用户的输入,并且将输入的值存放在特定变量里.

if [ $res=" y" -o $res="Y"]

then

break ;用于退出while,for,select等循环语句

fi

done

(6)repeat语句

repeat语句是tcsh提供的独有的循环语句.使用repeat命令要求shell对一个命令执行一定

的次数.

语法格式:

repeat count command

;

foreach num ( $ *)

repeat $num echo -n " *"

echo " "

end

7.shell中的函数

shell允许用户定义自己的函数.函数是高级语言中的重要结构.shell中的函数于C或者其他

语言中定义的函数一样.与从头开始,一行一行地写程序相比,使用函数主要好处是有利于组织

整个程序.bash,一个函数的语法格式如下:

fname (){

shell comands

}

定义好函数后,需要在程序中调用他们.bash中调用函数的格式:

fname [parm1 parm2 parm3...]

调用函数时,可以向函数传递任意多个参数.函数将这些参数看做是存放他的命令行参数的

位置变量.

举例:

这个程序定义了4个函数:

upper():将传递给他的文件中的字母转换成大写,并存放到同名的结尾为.out的文件中.

lower():将传递给他的文件里的字母转换成小写,并存放到同名的结尾为.out的文件中.

print ():输出传递给他的文件的内容.

usage_error ():输出程序的帮助信息.

程序的主模块是个case条件语句,他根据命令行中第一个参数,决定程序要完成的功能,并调用相应

的函数完成这一功能.

upper (){

shift

for i

do

tr a-a A-Z<$!>$1.out

rm $1

mv $1.out $1

shift

done; }

lower () {

shift

for i

do

tr A-Z a-z<$1>$1.out

rm $1

mv $1.out $1

shift

done; }

print() {

shift

for i

do

lpr $1

shift

done; }

usage_error() {

echo " $1 syntax is $1"

echo ""

echo " where option is one of thefollowing"

echo " p--to print frame files"

echo " u--to save as uppercase"

echo " l--to save as lowercase";}

case $1 in

p | -p)print $@;;

u | -u)upper $@;;

l | -l)lower $@;;

*) usage_error $0;;

esac

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

总结

利用shell编程是提高系统管理工作效率的重要手段,学好shell跟了解系统基本命令和管理

工具的使用方法同样重要!

:

A.bash中常用的命令

命令|含义

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

Alias |设置命令别名

Bg |将一个被挂起的进程在后台执行

cd |改变用户的当前目录

exit |终止一个shell

export|使作为这个命令的参数的变量及其当前值,在当前运行的shell的子进程中可见

fc |编辑当前的命令行历史列表

fg |让一个被挂起的进程在前台执行

help |显示bash内部命令的帮助信息

history |显示最近输入的一定数量的命令行

kill |终止一个进程

pwd |显示用户当前工作目录

unalias |删除命令行别名

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

B.bash中常用的系统变量

变量|含义

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

EDITOR,FCEDIT |Bashfc命令的默认文本编辑器

HISTFILE |规定存放最近输入命令行文件的名字

HISTSIZE |规定命令行历史文件的大小

HOME |当前用户的宿主目录

OLDPWD |用户使用的前一个目录

PATH |规定bash寻找可执行文件时搜索的路径

PS1 |命令行环境中显示第一级提示符号

PS2 |命令行环境中显示第二级提示符号

PWD |用户当前工作目录

SECONDS |当前运行的bash进程的运行时间(以秒为单位)


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值