C语言shell 编程

树一个本子 

  Linux中来好多被不同之shell,但是日常咱们使用bash (bourne again
shell)
进行shell编程,因为bash是免费之还要颇易使。所以当本文中笔者所提供的本子还是以bash(但是以多数景下,这些本子同样可于
bash的老大姐,bourne shell中运行)。 
  如同任何语言同样,通过我们采取任意一栽文字编辑器,比如nedit、kedit、emacs、vi 
  等来修我们的shell程序。 
  程序必须为下面的推行开始(必须着于文件的第一履行): 
#!/bin/sh 
  符号#!用来告诉系统它背后的参数是用来推行该文件的程序。在此例子中我们利用/bin/sh来施行顺序。 
  当编辑好本子时,如果只要执行该脚本,还必须要其可实施。 
  要而下论但是尽: 
chmod +x filename 
  然后,您得经过输入: ./filename 来执行您的台本。 
注释 
  在进行shell编程时,以#始发的词表示注释,直到就等同履行的结。我们热切地建议乃当先后中运用注释。如果你使用了诠释,那么即便相当长之岁月内没有运用该脚本,您吗克以雅缺的辰内明该脚本的意向与办事原理。 
变量 
  于任何编程语言中而得运用变量。在shell编程中,所有的变量都出于字符串组成,并且您不待针对变量进行宣示。要赋值给一个变量,您得这么写: 
变量名=值 
  取出变量值可以加一个美元符号($)在变量前面: 

#!/bin/sh 
#对变量赋值: 
a="hello world" 
# 现在打印变量a的内容: 
echo "A is:" 
echo $a 

  以公的编辑器中输入以上内容,然后将该保存也一个文书first。之后执行chmod
+x first 
  使该可实行,最后输入./first执行该脚本。 
  这个本子将会晤输出: 
A is: 
hello world 
  有时候变量名那个易与其余文字混淆,比如: 
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 
  有成千上万变量是网自动设定的,这将以后头使用这些变量时开展讨论。 
  如果你要处理数学表达式,那么您得运用诸如expr等次第(见底)。 
  除了一般的不过在程序外有效之shell变量以外,还有环境变量。由export关键字处理过的变量叫做环境变量。我们不对环境变量进行座谈,因为一般而言状态下独自在报到脚论被以环境变量。 
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钟头(-mtime
–2则代表过去48钟头)内修改了之公文。如果你想以有查找到的文书由一个担保,则足以应用以下脚本: 
#!/bin/sh 
# The ticks are backticks (`) not normal quotes (‘): 
tar -zcvf lastmod.tar.gz `find . -mtime -1 -type f -print` 
  3) 流程控制 
  ”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表达式作其它业务,但是利用与或操作符会更有益多。 
  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” ;; 
*) error “File $1 can not be uncompressed with smartzip”;; 
esac 
  您可能注意到我们于此运用了一个突出的变量$1。该变量包含了传递让该次的第一只参数值。也就是说,当我们运行: 
smartzip articles.zip 
$1 就是字符串 articles.zip 
  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 
  您吗得于shell中运用如下的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. 
引号 
  于通往程序传递任何参数之前,程序会扩大通配符和变量。这里所谓扩展的意是次会把通配符(比如*)替换成适量的公文称,它变量替换成变量值。为了防
止程序作这种替换,您可以下引号:让咱来拘禁一个例证,假而于当前目录下出部分文件,两单jpg文件,
mail.jpg 和tux.jpg。 

#!/bin/sh 
echo *.jpg 
  这将打印出”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 
Here documents 
  当要以几执行文字传递给一个限令时,here
documents(译者注:目前尚从未看到了针对性该词适合之翻译)一种植对的方。对每个脚本写一段帮助性的文是十分有因此的,此时若我们四发生好
here documents就无须因此echo函数一行行输出。 一个 “Here document” 以
<< 开头,后面接上一个字符串,这个字符串还非得出现在here
document的末尾。下面是一个例,在拖欠例子中,我们对多独文本进行双重命名,并且使here
documents打印帮助: 

复制代码代码如下:

#!/bin/sh 
# we have less than 3 arguments. Print the help text: 
if [ $# -lt 3 ] ; then 
cat < 
ren — renames a number of files using sed regular 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 argument 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命令进行更命名。 
函数 
  如果你写了一部分小复杂一些底先后,您尽管见面发觉在次中或者于几只地方以了同样的代码,并且您吗会发觉,如果我们下了函数,会有利于广大。一个函数是者样子的: 

复制代码代码如下:

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 < 
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 we call the function help: 
[ -z “$1” ] && help 
[ “$1” = “-h” ] && help 
# send the escape sequence to change the xterm 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 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 — -somefile1 somefile2 
  返回的结果是: 
opt_f is 1 
opt_l is hello 
first arg is -somefile1 
2nd arg is somefile2 
  这个脚论是哪些做事之吧?脚本首先以富有输入指令执行参数中进行巡回,将输入参数与case表达式进行较,如果匹配则设置一个变量并且移除该参数。根据unix系统的老办法,首先输入的应当是含减号的参数。 
实例 
  一般编程步骤 
  现在我们来谈谈编写一个本子的一般步骤。任何美好的剧本还应该具备助与输入参数。并且写一个伪脚本(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 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 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.1 to 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. -h for 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 
  这将返回所有语法错误。 
  我们希望你现在可以起写你自己之shell脚本,希望您游玩得开心。