linux shell 脚本入门

参考文章位置:http://learn.akae.cn/media/ch31s05.html

1.Linux 脚本编写基础...1

1.1 语法基本介绍...1

1.1.1 开头...1

1.1.2 注释...2

1.1.3 变量...2

1.1.4 环境变量...2

1.1.5 Shell命令和流程控制...2

2.case.5

3. selsect6

4.loop.6

5. 引号...7

6. Here documents8

7.脚本深入...15

1、条件测试操作:...15

1.2、整数值 比较:...16

1.3:字符串比较:...16

1.4:逻辑测试:...17

2:if语句的结构:...18

3、使用for魂环语句...20

4、使用while循环语句...21

附录:...23

a.23

b.23

c. Linux中变量$#,$@,$0,$1,$2,$*,$$,$?的含义...23

1. Linux 脚本编写基础

1.1 语法基本介绍

1.1.1 开头

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

#!/bin/sh

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

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

要使脚本可执行:

编译 chmod +xfilename 这样才能用./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 isthe 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

命令输出结果为: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 (`) not normalquotes ('):

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文件存在

则打印” Thiscomputer 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表达式作任何事情,但是使用与或操作符会更便利很多。

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

2.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, gzip 和zip 类型的压缩文件:

#!/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 将运行直到表达式测试为真。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 RPMpackages

# 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.jpg 和tux.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

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

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

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

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

使用heredocuments打印帮助:

#!/bin/sh

# we have less than 3 arguments. Print thehelp text:

if [ $# -lt 3 ] ; then

cat <

ren -- renames a number of files using sedregular expressions

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

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

ren 'HTM$' 'html' *.HTM

HELP

exit 0

fi

OLD="$1"

NEW="$2"

# The shift command removes one argumentfrom 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 existsalready"

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 first argumentgiven 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 an xterm,gnome-terminal or kde konsole

USAGE: xtitlebar [-h]"string_for_titelbar"

OPTIONS: -h help text

EXAMPLE: xtitlebar "cvs"

HELP

exit 0

}

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

[ -z "$1" ] && help

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

# send the escape sequence to change thexterm titelbar:

echo -e "33]0;$107"

#

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

命令行参数

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

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

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

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

比如文件名)。

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

#!/bin/sh

help()

{

cat <

This is a generic command line parser demo.

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

HELP

exit 0

}

while [ -n "$1" ]; do

case $1 in

-h) help;shift 1;; # function help iscalled

-f) opt_f=1;shift 1;; # variable opt_f isset

-l) opt_l=$2;shift 2;; # -l takes anargument -> shift by 2

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

-*) echo "error: no such option $1. -hfor 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

exit 0

}

error()

{

# print an error and exit

echo "$1"

exit 1

}

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 thisis 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 andreturn it in $rval

if [ -z "$1" ]; then

# empty string

rval=""

return

fi

# wc puts some space behind the output thisis 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 -b0-${numofcharminus1}`

}

while [ -n "$1" ]; do

case $1 in

-h) help;shift 1;; # function help iscalled

--) 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 is decimal$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.1to out.2, out to out.1

and create an empty out-file

The max number is 10

version $ver

HELP

exit 0

}

error()

{

echo "$1"

exit 1

}

while [ -n "$1" ]; do

case $1 in

-h) help;shift 1;;

--) break;;

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

*) break;;

esac

done

# input check:

if [ -z "$1" ] ; then

error "ERROR: you must specify a file,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

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

调试//--------------------------------------- 

//---------------------------------------

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

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

sh -x strangescript

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

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

sh -n your_script

这将返回所有语法错误。

7.脚本深入

在Shell脚本中执行使用if语句的好处是:可以根据特定的条件(eg:判断备份目录是否存在)来决定是否执行某项操作,当满足不同的条件时执行不同的操作(eg:备份目录不存在则创建该目录,否则跳过操作)。该文将分别从条件测试操作,if语句结构,应用示例这三个方面讲解if语句在Shell脚本中的应用。

1、条件测试操作:

需要在Shell脚本中有选择性地执行任务时,首先面临的问题就是,如何设置命令执行的条件?

在Shell环境中,可以根据命令执行后返回状态值来判断该命令是否成功执行,当返回值为0是表示成功执行,否则(非0值)表示执行失败。用于特定条件表达式的测试时,可以使用Linux系统中提供的专用工具——test命令、

使用test测试命令时,可以有以下两种形式。

test       条件表达式

【条件表达式】

这两种方式的作用完全相同,但通常后一种形式更为常用,也更贴近编程习惯。需要注意的是,方括号“[”或者“]”与条件表达式语句之间至少需要有一个空格进行分隔。

根据需要判断的条件内容不同,条件操作也不同,最常用的条件主要包括文件状态测试,比较整数值大小,比较字符串,以及同时判断多个条件时的逻辑关系,下面将分别进行讲解。以下主要采用方括号的测试形式。

1.1、测试文件状态

文件状态测试是指根据给定的路径名称,判断该名称对应的是文件还是目录,或者判断文件是否可读,可写,可执行等。根据判断的状态不同,在条件表达式中需要使用不同的操作选项。

-d:测试是否为目录(Directory)。

-e:测试目录或文件是否存在(Exist)。

-f:测试是否为文件(File)。

-r:测试当前用户是否有权限读取(Read)。

-w:测试当前用户是否有权限写入(Write)。

-x:测试当前用户是否可执行(Excute)该文件。

-L:测试是否为符号连接(Link)文件。

执行条件测试操作以后,通过预定义变量“$?”可以获得测试命令的返回状态值,从而能够判断该条件是否成立(返回0值表示条件成立,非0值表示条件不成立)。但通过这种方式查看测试结果会比较繁琐。

例1:测试“/etc/hosts”是否是目录,并通过“$?”变量查看返回状态值,据此判断测试结果。

[ -f /etc/hosts ]

echo $?

0   //返回值为0,表示上一步测试的条件成立。

例2:测试“/media/cdrom/Server”及其父目录是否存在,如果存在则显示“YES”否则不输出任何信息。

[ -e /media/cdrom/Server ] && echo"YES"

//无输出表示该目录不存在

[ -e /media/cdrom ] &&echo"YES"

YES

1.2、整数值比较:

整数值比较是指根据给定的两个整数值,判断第一个数是否大于、等于、小于。。。。。。第2个数,可以使用的操作选项如下:

-eq:第一个数等于(Equal)第二个数。

-ne:第一个数不等于(Not Equal)第二个数。

-gt:第一个数大于(Greater Than)第二个数。

-lt:第一个数小于(Lesser Than)第二个数。

-le:第一个数小于或等于(Lesser or Equal)第二个数。

-ge:第一个数大于或等于(Greater or Equal)第二个数。

整数值比较的测试操作在Shell脚本编写中的应用较多,如:用于判断磁盘使用率、登录用户数量是否超标以及用于控制脚本语句的循环次数等。

例1:测试当前登录到系统中的用户数量是否小于或等于10,是则输出”YES“。

who | wc -l

5

[ `who` | wc -l` -le 10 ] && echo ”YES"

YES

例2:提取出"/boot“分区的磁盘使用率,并判断是否超过95%(为了便于理解,操作步骤适当进行分解,嘿嘿!)

df -hT | grep ”/boot“ | awk '{print $6}'

12%

BootUsage=`df -hT | grep ”/boot" | awk '{print $6}'| cut -d "%" -f 1`

echo $BootUsage

12

[ $BootUsage -gt 95 ] &&echo"YES"    //无输出表示未超标

1.3:字符串比较:

字符串比较可以用于检查用户输入,如:在提供交互式操作时,判断用户输入的选项项是否与指定的变量内容相匹配。“=”、“!=”操作选项分别表示匹配、不匹配。“-z”操作选项用于检查字符串是否为空。其中,“!”符号用于取反,表示相反的意思。

eg:提示用户输入一个文件路径,并判断是否是“/etc/inittab”,如果是则显示“YES”.

read -p "Location: " FilePath

Location:/etc/inittab

[ $FilePath = "/etc/inittab" ] && echo"YES"

YES

eg: 若当前环境变量LANG的内容不是“en.US”,则输出LANG变量的值,否则无输出。

[ $LANG != "en.US" ] && echo $LANG

zh_CN.UTF-8

eg:使用touch命令建立一个新文件,测试其内容是否为空,向文件中写入内容后,再次进行测试。

touch zero.file

[ -z `cat zero.file` ] && echo "yes"

yes

echo "something" > zero.file

[ -z `cat zero.file` ] && echo yes

//无输出

1.4:逻辑测试:

逻辑测试是指同时使用的两个(或多个)条件表达式之间的关系。用户可以同时测试多个条件,根据这些条件是否同时成立或者只要有其中一个条件成立等情况,来决定采取何种操作。逻辑测试可以使用的操作选项如下。

>&&:逻辑与,表示前后两个表达式都成立时整个测试结果才为真,否则结果为假。在使用test命令形式进程测试时,此选项可以改为" -a"。

>||:逻辑或,表示前后两个条件至少有一个成立时整个测试结果即为真,否则结果为假。在使用test命令形式进行测试时,此选项可以改为"-o“。

>!:逻辑否,表示当指定的条件表达式不成立时,整个测试命令的结果为真。

在上述逻辑测试的操作选项中,”&&“和”||“通常也用于间隔不同的命令操作,其作用是相似的。同时使用多个逻辑运算操作时,一般安装从左到右的顺序进行测试。

eg:测试当前的用户是否是teacher,若不是则提示”Not teacher“。

echo $USER

root

[ $USER = “teacher” ] || echo "Not teacher"

Not teacher

eg:只要"/etc/rc.d/rc.local"或者"/etc/init.d/local'中有一个是文件,则显示"YES",否则无任何输出。

[ -f /etc/rc.d/rc.local ] || [ -f /etc/init.d/rc.local ]&& echo "yes“

yes

eg:测试”/etc/profile“文件是否有可执行权限,若确实没有可执行权限,则提示”No x mode.“的信息。

[ ! -x ”/etc/profile" ] && echo "No xmode."

No x mode.

eg:若当前的用户是root且使用的Shell程序是"/bin/bash",则显示"YES“,否则无任何输出。

echo $USER $SHELL

root /bin/bash

[ $USER = ”root" ] && [ $SHELL ="/bin/bash" ] && echo "yes"

yes

2:if语句的结构:

前面什么知道了一下条件测试操作,实际上使用"&&“和”||“逻辑测试可以完成简单的判断并执行相应的操作,但是当需要选择执行的命令语句较多时,再使用这种方式将使命令行语句显得很复杂,难以阅读。而使用if语句,则可以更好地体现有现在性执行的程序结构,使得层次分明,清晰易懂。

if语句的选择结构由易到难可以分为三种类型,分别适用于不同的应用场合。

2.1、单分支的if语句。

单分支的if语句是最简单的选择结构,这种结果只判断指定的条件,当”条件成立“时执行相应的操作,否则不做任何操作。单分支使用的语句格式如下。

if 条件测试命令

then

命令序列

fi

在上述语句中,首先通过判断条件测试命令的返回状态值是否为0(条件成立),如果是,则执行then后面的一条或多台可执行语句(命令序列),一直到fi为止表示结束,如果条件测试命令的返回值不为0(条件不成立),则直接去执行fi后面的语句。

2.2、双分支的if语句。

双分支的if语句使用了两路命令操作,在”条件成立‘、“条件不成立时分别执行不同的命令序列”。双分支使用的语句格式如下:

if 条件测试命令

then

命令序列1;

else

命令序列2;

fi

在上述语句中,首先通过if判断条件测试命令的返回状态值是否为0(条件成立),如果是,则执行then后面的一条或多条可执行语句(命令序列1),然后跳转至fi结束判断,如果条件测试命令的返回状态值不为0(条件不成立),则执行else后面的语句,一直到fi表示结束。

2.3、多分支的if语句。

由于if语句可以根据条件测试命令的两种状态分别进行操作,所以能够嵌套使用,进行多次判断(如:首先判断某学生的得分是否及格,如及格则再次判断是否高于90分。。。)多重分支使用的语句格式如下。

if 条件测试命令1

then

命令序列1

elif 条件测试命令2

then

命令序列2

else

命令序列3

fi

上面的语法格式中只嵌套了一个elif语句,实际上if语句中可以嵌套多个elif语句。if语句的嵌套在编写Shell脚本时并不常用,因为多重嵌套容易使程序结构变得复杂。需要使用多重分支程序结构时,更多的是使用case语句来实现。

eg:检查"/var/log/messages'文件是否存在,若存在则统计文件内容的行数并输出,否则不做任何操作。

vi  chklog.sh

#!/bin/bash

LogFile="/var/log/messages"

if [ -f $LogFile ] ; then

wc -l $LogFile

fi

shchklog.sh               //sh是bash的符号链接

eg:提示用户指定备份目录的路径,若目录已存在则显示提示信息后跳过,否则显示相应提示信息后创建该目录。

[root@localhost ~]# vi mkbak.sh

#!/bin/bash
read -p "What is your directory:"Bakdir
if [ -d $Bakdir ] ; then
echo "$Bakdir already exist."
else
echo "Bakdir is not exist,will makeit."
mkdir $Bakdir

fi

eg:统计当前登录到系统中的用户数量,并判断是否超过三个,若是则显示实际数量并给出警告信息,否则列出登录的用户账号成名及所在终端。

[root@localhost ~]# vim chkuser.sh
#!/bin/bash
UserNum=`wc -l`
if [ $UserNum -gt 3 ] ; then
echo "Alert , too many login users ( Total: $UserNum )."
else
 echo "Login users:"
 who | awk `{print $1,$2}`
fi
eg:检查portmap进程是否已经存在,若已经存在则输出“portmap service is running”;否则检查是否存在“/etc/rc.d/init.d/portmap”可执行脚本,存在则启动portmap服务,否则提示“noportmap script file.”。

[root@localhost ~]# vim chkportmap.sh
#!/bin/bash
pgrep portmap &> /dev/null
if [ $? -eq 0 ]; then
echo "protmap service is running."
elif
[ -x "/etc/rc.d/init.d/portmap" ]; then
service portmap start
else
echo "no portmap script file."
fi

eg:每隔五分钟监测一次mysqld服务程序的运行状态,若发现mysqld进程已终止,则在“/var/log/messages”文件中追加写入日志信息(包括当时时间),并重启mysqld服务,否则不进程任何操作。

vi chkmysql.sh

#!/bin/bash
service mysqld status &> /dev/null
if [ $? -ne 0 ]; then
echo "At time:`date`:Mysql Server is down." >>/var/log/messages
service mysqld restart
fi
chmod u+x chkmysql.sh

crontab -e

*/5 * * * * /root/chkmysql.sh

多多联系喔!

3、使用for魂环语句

在Shell脚本中使用for循环语句时,可以为变量设置一个取值列表,每次读取列表中不同的变量值并执行相关命令操作,变量值用完以后则退出循环。Shell中的for语句不需要执行条件判断,其使用变量的取值来自于预先设置的值列表。

for语句结构:

for 变量名 in取值列表

do

命令序列

done

在上述语句中,使用in关键字为用户自定义变量设置了一个取值列表(以空格分隔的多个值),for语句第一次执行时首先将列表中的第一个取值赋给该变量。然后执行do后边的命令序列;然后再将列表中的第二个取值赋给该变量,然后执行do后边的命令序列......如此循环,直到取值列表中的所有值都已经用完,最后将跳至dine语句,表示结束循环。

for语句示例:

eg:依次输出三条文件信息,包括一天中的"Morning"、"Noon"、"Evening"字串。

vi showday.sh

#!/bin/bash

for TM in "Morning" "Noon""Evening"

do

echo "The $TM of the day."

done

eg:对于使用“/bin/bash”登录Shell的系统用户,检查他们在"/opt"目录中拥有的子目录或文件数量,如何超过100个,则列出具体数量及对应的用户账号。

vi chkfileown.sh

#!/bin/bash

DIR="/opt"               //设置检查的目标目录

LMT=100               //设置文件数量的限制值

ValidUsers=`grep "/bin/bash" /etc/passwd | cut-d ":" -f1`               //找出使用bash的系统用户列表

for UserName in $ValidUsers

do

Num=`find $DIR -user $UserName | wc-l`             //统计每个用户拥有的文件数

if [ $Num -gt $LMT ] ; then

echo "$UserName have $Num files."

fi

done

sh chkfileown.sh

root have 20998 files

4、使用while循环语句

在Shell脚本中使用while循环语句时,将可以根据特定的条件重复执行一个命令列表,直到该条件不再满足时为至。除非有特别需要,否则在脚本程序中应该是避免出现无限循环执行命令的情况,因为若无法跳出循环的话,后边的明白操作将无法执行。为了控制循环次数,通常会在执行的命令序列中包含修改测试条件的语句,当循环达到一定次数后,测试将不再成立,从而可以结束循环。

while语句的结构

while 条件测试命令

do

命令序列

done

在上述语句中,首先通过while判断条件测试命令的返回状态值是否为0(条件成立),如果是,则执行do后边的命令序列,然后返回到while再次进行条件测试并判断返回状态值,如果条件仍然成立,则继续执行do后边的命令序列,然后返回到while重复条件测试......如此循环,直到所测试的条件不成立时,跳转到done语句,表示结束循环。

使用while循环语句时,有两个特殊的条件测试返回值,即“true”(真)、"false"(假)。使用“true”作为测试条件时,条件将永远成立,循环体内的语句将无限次执行下去,反之使用“false”则条件永远不成立,循环体内的语句将不会被执行,这两个特殊值也可以用在if语句的条件测试中。

while语句应用示例:

while语句可以用于需要重复操作的循环系统管理任务,并能够通过设置循环条件来灵活的实现各种管理任务。

eg:由用户从键盘输入一个大于1的整数(如50),并计算从1到该数之间各整数的和。

[root@localhost ~]# vim sumint.sh
#!/bin/bash
read -p "Input a number (>1):" UP
i=1
Sum=0
while [ $i -le $UP ]
do
Sum=`expr $Sum + $i`
i=`expr $i + 1`
done
echo "The sum of 1-$UP is : $Sum"

[root@localhost ~]# sh sumint.sh
Input a number (>1):50
The sum of 1-50 is : 1275
[root@localhost ~]# 
eg:批量添加20个系统用户账号,用户名称依次为“stu1”、"stu2"、“stu3”、.......“stu20”,各用户的初始密码均设置为“123456”。

[root@localhost ~]# vim add20users.sh
#!/bin/bash
i=1
while [ $i -le 20 ]
do
useradd stu$i
echo "123456" | passwd --stdin stu$i &> /dev/null
i=`expr $i + 1`
done
sh add20users.sh

eg:编写一个批量删除用户的脚本程序,将上面添加的20个用户删除。

[root@localhost ~]# vim del20users.sh
#!/bin/bash
i=1
while [ $i -le 20 ]
do
userdel -r stu$i
i=`expr $i + 1`
done
再次查看:cat /etc/passwd就会发现那些用户不再存在。

说了if、for、while、语句后,就可以编写一般的系统管理任务脚本了,记得多多练习!其实除了这些Shell脚本语句外,还有好多好多,如:

case分支语句,until循环、shift移位,以及break和continue循环中断语句。大家可以查询!

附录:

a.

1  if [-d $1]

这个的意思是 从外面传一个参数给$1  -d是判断$!这个目录存不存在

所以你要穿的参数如果是正确的文件目录名,则此时的判断值为0  即真

2  帮你拓展下:

[ -f  判断参数 ]  判断一个普通文件是不是存在

[  -s   文件名 ]   文件存在且内容非空即必须要一个空格以上的字符

[ -e    文件目录 ]  文件目录下是否不为空

b.

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

$? :上一条命令的返回结果.

c. Linux中变量$#,$@,$0,$1,$2,$*,$$,$?的含义

我们先写一个简单的脚本,执行以后再解释各个变量的意义

# touch variable

# vi variable

脚本内容如下:

#!/bin/sh

echo "number:$#"

echo "scname:$0"

echo "first :$1"

echo "second:$2"

echo "argume:$@"

echo "show parm list:$*"

echo "show process id:$$"

echo "show precomm stat: $?"

保存退出

赋予脚本执行权限

# chmod +x variable

执行脚本

# ./variable aa bb

number:2

scname:./variable

first:aa

second:bb

argume:aa bb

show parm list:aa bb

show process id:24544

show precomm stat:0

通过显示结果可以看到:

$# 是传给脚本的参数个数

$0 是脚本本身的名字

$1 是传递给该shell脚本的第一个参数

$2 是传递给该shell脚本的第二个参数

$@ 是传给脚本的所有参数的列表

$* 是以一个单字符串显示所有向脚本传递的参数,与位置变量不同,参数可超过9个

$$ 是脚本运行的当前进程ID号

$? 是显示最后命令的退出状态,0表示没有错误,其他表示有错误

脚本打开新终端执行程序

#!/bin/bashBASHNAME=test.sh
BINPATH=$0
BINNAME=./cyclegnome-terminal -x "${BINPATH/$BASHNAME/$BINNAME}"exit 0

cd 到运行程序或者脚本的目录

###########################################################################################
#  cd to bash dir
###########################################################################################
link=`readlink -f $0`
echo "$link"
dirName=`dirname $link`
echo "$dirName"
cd $dirName; echo "now dir In :$(pwd) ";

查找源文件批处理文件名并比较结果操作字符串

for name in `find ../../src/ -regex  '.*cpp\|.*cc\|.*c' -exec basename {} \;`; dogcov_cmd=`find ../../ -name "*${name}.gcno"`;gcov_out=`gcov ${gcov_cmd} | grep -A 1 ${name}`;percentageNum=`echo $gcov_out | cut -d':' -f2 | cut -d'%' -f1`if [ $(echo "$percentageNum > 80.00" | bc) -eq 1 ]; thenecho "\033[32m $gcov_out \033[0m"elseecho "\033[31m $gcov_out \033[0m"fi
done

linux脚本编程教程相关推荐

  1. linux脚本编程教程,shell脚本编程基础教程

    在Linux中,当你想要做什么事情的时候,可以通过输入命令传递给计算机,但也会有很多时候,我们需要计算机/服务器完成相同的任务,如果我们再一次一次输入命令执行的话,就会非常的不方便,要想让所有的事情变 ...

  2. linux脚本编程(shell)浅介

    linux脚本编程(shell)浅介 2006-01-10 11:45 27693人阅读 评论(18) 收藏 举报 脚本shelllinux编程bash语言 linux脚本(shell)编程 啊,昨天 ...

  3. Linux脚本编程:sudo命令下设置定时任务

    Linux脚本编程:sudo命令下设置定时任务 题目要求 编程平台 本地 云端 代码实现 基本思路 解决脚本 其他说明 题目要求 让普通用户使用sudo命令编写一个脚本,脚本内容为:设置定时任务,每隔 ...

  4. Shell脚本编程教程

    1.Shell脚本语言的基本结构 1.1 Shell脚本的用途: 自动化常用命令 执行系统管理和故障排除 创建简单的应用程序 处理文本或文件 1.2 Shell脚本基本结构: ​ Shell脚本编程: ...

  5. linux脚本编程(shell)浅介 (转载)

    linux脚本(shell)编程 啊,昨天上网看到一篇讲 linux/unix shell 的文章,想想自己最后写这东西也是一年前的事了,想想都快忘光了. 还是整理一下,做一次回顾,以后说不定还用得上 ...

  6. Linux C/C++ 零基础学习编程教程,Linux 系统编程教程

    从事 Linux 系统软件开发十余载,整理多套 Linux C/C++程序设计,Linux 系统编程的教程和实践项目,希望能够和更多的读者一起学习!       更多的资料,可以访问作者的博客网站: ...

  7. 【Linux】Linux脚本编程

    脚本编写基础 变量和参数 变量替换 变量的名字是它的值保存的地方.引用它的值称为变量替换.如果varible1是一个变量,那么$varible1就是引用该变量的值,即这个变量包含的数据. 解释:$为变 ...

  8. linux 脚本编程

    image.png image.png • 查看CPU信息:lscpu • 查看内存信息:free -h • 查看硬盘信息:df -h ls -lh 显示当前文件夹的大小 du -h -d 1du - ...

  9. 【Linux】一步一步学Linux系统编程教程汇总(暂时暂停更新......)

    00. 目录 文章目录 00. 目录 01. 概述和标准 02. 文件操作 03. 进程概念 04. 进程间通信 05. 多线程 06. 信号 07. 同步与互斥 08. 高级IO 09. 其它 10 ...

最新文章

  1. 水母智能联合蚂蚁森林、犀牛智造等,用AI助力非遗出圈,39万人开工得“福”
  2. macOS下nginx配合obs做推流直播.md
  3. Ubuntu下VIM使用指南
  4. 一个基于Mule的企业服务总线的案例(关于JMS)
  5. discord linux_如何在Discord中应用文本格式
  6. java结构体系_java io结构体系
  7. code第一部分数组:从有序数组中移除重复的数据
  8. 告别相杀!面向对象和函数式编程共存
  9. ffmpeg解析h264文件,提取图片信息
  10. 深入理解jvm虚拟机(面试复习)
  11. 蓝海创意云×可米酷 || “360VR全景直播解决方案”亮相企业产品发布会
  12. 图像mnf正变换_PCA和MNF变换
  13. Elasticsearch7.8
  14. 正则表达式 包含a和b,包含a不包含b,包含a不包含b和
  15. (n++)+(n++)+(n++)与(++n)+(++n)+(++n)的区别
  16. Proxy(代理) ARP作用及原理
  17. java回车换行符linux,回车换行符 java
  18. PHP开发的仿山楂岛留言程序源码
  19. twitter推文不收录_如何阻止Twitter用户的转发(但仍然看到他们的推文)
  20. 利用笔记本无线网卡实现共享上网

热门文章

  1. Web前端工程师怎么样呢?薪资待遇如何呢?
  2. 不用ps也能在线设计电商详情页的方法
  3. 想自学编程,不知道先学哪个?
  4. Mac 中 MySQL 的安装与卸载
  5. 如何从 GRUB rescue 恢复 Win7,win7 USB恢复盘制作
  6. ionic3小知识(持续更新...)
  7. C语言中运算符的优先级排序表
  8. 北京多家大型驾校酝酿至少涨价10%
  9. 一个有趣推理题的答案
  10. JS逆向-新榜数据nonce和xyz参数分析