《Unix/Linux系统编程》第十章学习笔记 20201209戴骏

第十章 sh编程

一、知识点归纳

(一)sh脚本

sh脚本(Bourne 1982 ; Forouzan和Gilberg 2003 )是一个包含sh语句的文本文件,命令解释程序sh要执行该语句。例如,我们可以创建一个文本文件mysh,包含:

#I /bin/bash
# comment line
echo hello

使用chmod +x mysh使其可执行。然后运行mysho sh脚本的第一行通常以#!组合开始,通常称为shebang。当主sh见到shebang时,会读取脚本所针对的程序名并调用该程序。sh有许多不同的版本,例如Linux的bash、BSD Unix的csh和IBM AIX的ksh等。所有sh程序基本上都执行相同的任务,但它们的脚本在语法上略有不同。shebang允许主sh调用适当版本的sh来执行脚本。如果未指定shebang,它将运行默认的sh,即Linux中的/bin/bash。 当bash执行mysh脚本时,将会打印hello。

(二)sh脚本与C程序

sh脚本和C程序有一些相似之处,但它们在根本上是不同的。下面并列列出了一个sh 脚本和一个C程序,以比较它们的语法形式和用法。

//sh代码
INTERPRETER: read & execute
mysh a b c d
$0 $1 $2 $3 $4
//C代码
COMPILE-LINKED to a.out
a.out a b c d
main(int argc, char *argv[])

首先,Sh是一个解释程序,逐行读取sh脚本文件并直接执行这些行。如果行是可执行命令且为内置命令,那么Sh可直接执行。否则,它会复刻一个子进程来执行命令,并等待子进程终止后再继续,这与它执行单个命令行完全一样。相反,C程序必须先编译链接到一个二进制可执行文件.然后通过主sh的子进程运行二进制可执行文件。其次,在C程序中,每个变量必须有一个类型,例如char、int、float、派生类型(如struct)等。相反,在sh脚 本中,每个变量都是字符串。因此不需要类型,因为只有一种类型,即字符串。最后,每个C程序必须有一个main。函数,每个函数必须定义一个返回值类型和参数(如有)。相反,sh脚本不需要main函数。在sh脚本中,第一个可执行语句是程序的入口点。

(三)命令行参数

可使用与运行sh命令完全相同的参数调用sh脚本,如:

mysh one two three

在sh脚本中,可以通过位置参数$0、$1、$2等访问命令行参数。前10个命令行参数可以作为$0 ~ $9被访问。其他参数必须称为${10}~${n},其中n>10。或者,可以通过稍后显示的shift命令査看它们。通常,$0是程序名本身,$1到$n是程序的参数,在sh中,可用内置变量$#和$*计数并显示命令行参数。

  • $# =命令行参数$ 1到$n的数量
  • $* =所有命令行参数,包括$0
  • SS =执行sh的进程PID
  • $?=最后一个命令执行的退出状态(如果成功,则为0,否则为非0 )

(四)sh变量

sh有许多内置变量,如PATH、HOME、TERM等。除了内置变量外,用户还可使用任何符号作为sh变量。不需要声明。所有的sh变量值都是字符串。未赋值的sh变量是NULL字符串。sh变量可用以下方法设置或赋值:

variable=string # NOTE: no white spaces allowed between tokens

(五)sh中的引导

sh有许多特殊字符,如$、/、*、>、<等。要想把它们用作普通字符,可使用\或单引号来引用它们。用于引用单个字符。单引号用于引用长字符串。单引号内没有替换。双引号用于保留双引号字符串中的空格,但在双引号内会发生替换。

A=xyz
echo \$A       ==>  $A	     #	back quote $ as is
echo * $A/     ==>  $A	     #	NO substitution within SINGLE quotes
echo "see $A"  ==>  see	xyz  #	substitute $A in DOUBLE quotes

(六)sh语句

sh语句包括所有Unix/Linux命令,以及可能的I/O重定向。

Is
Is > outfile 
date
cp f1 f2
mkdir newdir 
cat < filename

此外,sh编程语言还支持控制sh程序执行的测试条件、循环、case等语句。

(七)sh命令

  • 内置命令
    sh有许多内置命令,这些命令由sh执行,不需要创建一个新进程。下面列出一些常用的内置sh命令。

    • .file:读取并执行文件。
    • break [n]:从最近的第n个嵌套循环中退出。
    • cd [dirname]:更换目录。
    • continue [n]:重启最近的第n个嵌套循环。
    • eval [arg...]:计算一次参数并让sh执行生成的命令。
    • exec [arg ...]:通过这个sh执行命令,sh将会退出。
    • exit [n]:使sh退出,退出状态为n。
    • export [var ...]:将变量导岀到随后执行的命令。
    • read [var...]:从stdin中读取一行并为变量赋值。
    • set [arg ...]:在执行环境中设置变量。
    • shift:将位置参数$2 $3...重命名为$1 $2...。
    • trap [arg] [n]:接收到信号n后执行参数。
    • umask [ddd]:将掩码设置为八进制数ddd的。
    • wait [pid]:等待进程pid,如果没有给出pid,则等待所有活动子进程。
    • read命令:当sh执行read命令时,它会等待来自stdin的输入行。它将输入行划分为几个标记,分配给列岀的变量。read的一个常见用法是允许的sh进行交互, 如下面的示例所示。
      echo -n "enter yes or no : " # wait for user input line from stdin
      read ANS	# sh reads a line from stdin
      echo $ANS	# display the input string
      
  • Linux命令
    sh可以执行所有的Linux命令。其中,有些命令几乎已经成为sh不可分割的一部分, 因为它们广泛用于sh脚本中。下文列出并解释了其中一些命令。

    • echo命令:echo只是将参数字符串作为行回显到stdout。它通常将相邻的多个空格压缩为一个空格,除非有引号。
      echo   This is    a line     # display This is a line 
      echo   "This is   a line"    # display This is    a    line                     
      echo   -n hi                 # display hi without NEWLINE                   
      echo      there              # display hithere        
      
    • expr命令:因为所有的sh变量都是字符串,所以我们不能直接把它们改为数值。
      I=123	# I assigned the string *123*
      I=I + 1	# I assigned the string "I + 1"
      
      最后一个语句并不是将I的数值增加1,而仅仅是将I更改为字符串“1+1”,这肯定不 是我们所希聖的结果(1=124 )0可通过expr命令间接更改sh变量的值(数值)。expr是一个程序,它的运行方式如下:
      expr string1 OP string2 # OP = any binary operator on numbers
      
      首先,它将两个参数字符串转换为数字,然后对数字执行(二进制)操作OP,再将得 到的数字转换回字符串。因此,
      I=123
      I=$(expr $I + 1)
      
      将I从"123"更改为"124"。同样,expr也可用于对其值为数字字符串的sh变量执行其他算术操作。
    • 管道命令:在sh脚本中经常使用管道作为过滤器。
      ps -ax | grep httpd
      cat file | grep word
      
    • 实用命令:除了上面的Linux命令之外,sh还使用许多其他实用程序作为命令。其中包括:
      • awk:数据处理程序。
      • cmp:比较两个文件。
      • comm:选择两个排序文件共有的行。
      • grep:匹配一系列文件的模式。
      • diff:找出两个文件的差异。
      • join:通过使用相同的键来连接记录以比较两个文件。
      • sed:流或行编辑命令。
      • sort:排序或合并文件。
      • tail:打印某个文件的最后n行。
      • tr:一对一字符翻译。
      • uniq:从文件中删除连续重复行。

(八)命令替换

在sh中,$A会被替换成A值。同样,当sh遇到'cmd'(用引号括起来)或$(cmd)时, 它会先执行cmd,然后用执行的结果字符串替换$(cmd)。

echo $(date)	# display the result string of date command
echo $(ls dir)	# display the result string of ls dir command

(九)sh控制命令

sh是一种编程语言,支持许多执行控制语句,类似于C语言中的语句。

  • if-else-fi 语句
//语法
if [ condition ]     # NOTE: must have white space between tokens
   then
      statements
   else	             # as usual, the else part is optional
      statements
fi	             # each if must end with a matching fi
//实际
if [ condition ]; then
   statements
 else
   statements
fi
  • for 语句
    sh中的for语句作用类似于C语言中的for循环。
    for VARIABLE in string1 string2 ・・・ stringn
      do
        commands
      done
    
    在每次迭代中,变量接受一个参数字符串值,并执行关键字do和done之间的命令。
  • while 语句
    sh的while语句类似于C语言中的while循环:
    while [ condition ]
      do
        commands
      done
    
    当条件为真时,sh将重复执行do-done关键字中的命令。预计条件会有变化,所以循环将在某个时间点退出。
  • until-do 语句
    该语句类似于c语言中的do-until语句。
    until [ $ANS = "give up" ]
      do
          echo -n "enter your answer :"
          read ANS
      done
    
  • case 语句
    该语句也类似于C语言中的case语句,但在sh编程中很少使用。
    case $variable in
         patternl) commands;; # note the double semicolons ;;
         pattern2) command;; 
         patternN) command;;
    esac
    
  • continue 和 break 语句
    与在C语言中一样,continue重启最近循环的下一个迭代,break退出最近循环。它们的工作原理与在C语言中完全相同。

(十)I/O重定向

当进入sh命令时,我们可以指示sh将I/O重定向到除默认stdin、stdout和sterr以外的 文件。I/O重定向有以下形式和含义:

  • › file stdout转向文件,如果文件不存在,将会创建文件。
  • ›› file stdout追加到文件。
  • ‹ file 将文件用作stdin;文件必须存在并具有r权限。
  • ‹‹ word 从"here"文件中获取输入,直到只包含“word”的行。

(十一)嵌入文档

可以指示输出命令从stdin获取输入,将其回显到stdout,直到遇到预先安排的关键字。

echo « END
   #keep enter and echo lines until a line with only
END
cat « DONE
   #keep enter and echo lines until
DONE

这些文档通常被称为嵌入文档。它们通常用在sh脚本中,以生成长块的描述性文本, 不需要分别回显每一行。

(十二)sh函数

sh函数的定义为:

func()
{
   # function code
}

由于sh逐行执行命令,所以必须在任何可执行语句之前定义Sh脚本中的所有函数。与C语言不同,在sh脚本中无法声明函数原型。sh函数的调用方式与sh脚本文件的执行方式 完全相同。sh语句

func si s2 ... sn

调用sh函数,以参数(字符串)形式传递s1〜sn。在被调函数中,参数被引用为$0、$1到 $n。通常,$0是函数名,$1到$n是与命令行参数对应的位置参数。函数执行结束时,$? 表示其退出状态,如果成功,状态为0,否则,状态为非0。$?值可用函数的显式返回值进行更改。但是,为了测试$?的最后一次执行,必须将其分配给一个变量,然后测试该变量。

(十三)sh中的通配符

  • 星号通配符:sh中最有用的通配符是*,可扩展到当前目录中的所有文件。
    • file*:列出当前目录中所有文件的信息。
    • ls *.c:列出当前目录中所有以.c结尾的文件。
  • ?通配符:查询某文件名中的字符。
    • file ???:有3个字符的所有文件名。
    • ls*.??: 一个点号.后有2个字符的所有文件名。
  • []通配符:查询文件名中一对[]中的字符。
    • file *[ab] *:包含字符a或b的所有文件名。
    • ls *[xyz] *:列出所有包含x、y或z的文件名。
    • ls *[a-m] *:列出包含a到m范围内字符的所有文件名。

(十四)命令分组

  • 在sh脚本中,可以用{}或()对命令进行分组。
    • (ls;mkdir abc;ls;}:通过当前sh执行{}中的命令列表。{}命令分组的唯一用处是在相 同环境下执行这些命令,例如,为分组中的所有命令重定向I/O。
  • 更有用的命令分组是(),由subsh (进程)执行。
    • (cd newdir;ls;A=value;mkdir SA):通过subsh进程执行()中的命令。subsh进程可在不影响父sh的情况下更改其工作目录。此外,当subsh进程终止时,subsh中的任何赋值变量 都不起作用。

(十五)eval语句

eval [arg1 arg1 .. argn]

eval是sh的一个内置命令。它由sh自己执行,而不需要复刻新进程。它将输入参数字符串连接到一个字符串中,计算一次,即执行变量和命令替换,然后给出结果字符串供sh执行。

(十六)调试sh脚本

sh脚本可由带有-x选项的子sh运行,以进行调试,如:

bash -x mysh

子sh将在执行命令之前显示要执行的每个Sh命令,包括变量和命令替换C它允许用户跟踪命令执行。如果出现错误,Sh将在错误行上停止并显示错误消息。

二、问题与解决思路

问题:大家学习过Python,C,Java等语言,总结一下一门程序设计语言有哪些必备的要素和技能?这些要素和技能在shell脚本中是如果呈现出来的?

  • 要素:数据类型,常量,变量,运算符,表达式,标识符,库,数组,函数,算法
  • 技能:数据结构的分析和设计,算法(即处理逻辑,微观层次)的分析和设计,系统架构(即处理逻辑,宏观层次)的分析和设计
  • shell脚本:正规表示法,管道命令以及数据流重导向等功能,以达到我们所想要的处理目的。
    提供了数组,循环,条件以及逻辑判断等重要功能,让使用者可以直接以shell来写程序,而不必使用类似C程序语言等传统程序编写的语法。

问题:sh函数中"{","}"无法识别(图见第三部分)。
解决方法:网上查找资料。

三、实践内容与截图,代码链接






posted @ 2022-09-18 15:10  戴骏  阅读(49)  评论(0编辑  收藏  举报