第十章 shell的交互功能与shell程序设计----操作系统原理和实践

什么是shell?shell的用途是啥?_Darren_wdq的博客-CSDN博客_shell的作用场景:只知道写shell脚本,却不知道什么是shell?那shell是什么呢?找到了之前在腾讯课堂上看的视频,这是课件笔记你学Linux的话,不懂shell等同于不懂linuxshell是操作系统的最外层,shell可以合并编程语言以控制进程和文件,以及启动和控制其他程序。简单来说:shell就是一个用户跟操作系统之间交互的命令解释器感觉像是java和虚拟机的关系一样在一些复杂的L...https://blog.csdn.net/w5wangdeqing/article/details/94456190?spm=1001.2101.3001.6650.2&utm_medium=distribute.pc_relevant.none-task-blog-2~default~CTRLIST~default-2-94456190-blog-92011865.pc_relevant_aa&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2~default~CTRLIST~default-2-94456190-blog-92011865.pc_relevant_aa&utm_relevant_index=5LINUX Shell常用命令_Sunshine~L&H的博客-CSDN博客_linux shell命令Linux Shell常用shell命令一、文件、目录操作命令1、ls命令功能:显示文件和目录的信息ls 以默认方式显示当前目录文件列表ls -a 显示所有文件包括隐藏文件ls -l 显示文件属性,包括大小,日期,符号连接,是否可读写及是否可执行ls -lh 显示文件的大小,以容易理解的格式印出文件大小 (例如 1K 234M2G)ls -lt 显示文件,按照修改时...https://blog.csdn.net/LH0912666/article/details/87897629

本章主要内容

  1. Shell的启动和功能简介
  2. shell识别的命令形式
  3. 输入输出重定向和管道
  4. shell变量和引用符
  5. Shell脚本程序的建立与运行
  6. shell的语句类别
  7. *流编辑器sed和报表生成器awk简介

第十章  shell的交互功能与shell程序设计

UNIX系统中的Shell具有两大功能:

  • 命令解释器:     解释用户发出的各种操作系统命令
  • 程序设计语言: 功能强大, 可包容引用所有的操作系统命令和可执行程序。

     10.1    shell 的启动和终止

用户登录(login)进入系统时,  系统根据/etc/passwd文件中的配置参数, 为该用户启动一个指定类型的shell进程, 专门用于解释执行该用户发出的各种命令。

当用户发出exit或Logout命令时,该shell程序终止运行,该用户退出系统,回到getty状态

10.2.2   输入重定向

              command   <   filename      

 进程的输入来自文件filename,  例如:

 $ cat ↙           cat命令后无文件名, cat等待键盘输入

 abcde

 abcde                                                      键盘输入内容

 this is a test line

 this is a test line                                     cat进程输出内容

 Ctrl+d

 $

cat命令的输入来自标准输入文件(键盘),Ctrl+d结束键盘输入 

10.2.2   输入重定向

$ cat  < abc

aaaaaaaaaaaaaaa

bbbbbbbb                         

cccccccccccccccccc

$

cat进程的输入不是来自命令行参数,而是来自重定向文件abc

cat进程的输出送到标准输出荧光屏上

10.2.3   常见输入输出重定向形式:

命  令

输入

 输出

效果

cat

键盘

屏幕

将键盘输入显示在屏幕上

cat      file1

file1

屏幕

file1的内容显示在屏幕上

cat      file1  >  file2

file1

file2

file1的内容写入file2

cat               >  file2

键盘

file2

键盘输入的内容写入file2

cat  <  file1

file1

屏幕

file1的内容显示在屏幕上

cat  <  file1  >  file2

file1

file2

file1的内容写入file2

10.2.4   标准错误输出重定向:

                                 command   2>   filename                      ( 2和>之间没有空格 )

进程运行中的错误信息重定向到文件filename,  例如:

$ cc  -o  core_prt  core.c  2>  err.log

 在编译过程中如果出现core.c不存在或不能读、core_prt没有写权限等错误, 则把错误信息保存在文件err.log中.

$ cat   file1  file2  >  file3  2>  errfile

如果命令运行正常, 则把结果(连接file1和file2)存入file3中;如果出现错误, 则把错误信息存放到errfile中.

10.2.4   标准错误输出重定向:

$ grep  string6  data_sav   >  count_log  2> &1

        把进程的出错信息存放到标准输出(已重定向到count_log)中,  即把标准输出和标准错误输出都定向到一个文件中.

10.2.5   管道

管道用于连接两个命令, 它把前一个命令的标准输出重定向给后一个命令作为标准输入,  其格式为:

                                command1  |   command2

        对command1来说标准输出被重新定向到管道, 对command2来说标准输入也被重新定向为管道.  

例题1,在4.2.1节中的例题 " 按字母顺序显示当前系统中所有已登录的用户"

例题2:

$ who  |   wc –l

     查看系统当前有几个用户在上机使用系统.

$ pr   myfile  |  lp

     把文件myfile按标准打印格式处理后, 送到打印机打印出来(原文件myfile并未作任何修改).

$ grep  student  user_list  |  sort  >  stu_list

     在包含所有用户名单的文件user_list中, 查找包含student的行,  并把结果排序后存放在文件stu_list中. (管道和输出重定向混合使用)

10.3    shell 可识别的命令形式

10.3.1  单条命令:       

                           $ cat   file1

          这是最常用命令形式, 本命令执行完成后出现shell提示符, 再接收下一条键盘命令.

10.3.2  多条命令:   

                          $ pwd; who; date

          第一条命令执行完成后, (无停顿)再执行第二条命令, 如此下去.  运行功能和效果与在键盘上逐条输入命令并运行是完全一样的, 其主要目的是提高键盘命令输入效率.

10.3.3  复合命令:  

$ ps –e | grep student2

管道前后的命令任意组合、同时运行, 形成功能更强大灵活的复合命令.

$ (ls ; cat file3 ; pwd)  >  run_log

 括号内的命令任意组合、顺序执行,且由一个子shell来单独控制运行, 相当于一个小的功能程序.  方便灵活, 运行效率高.

10.3.4    后台命令:

             $ ls –lR  >  file_list  &
             [1]   7981
             $

普通命令行的行尾加上&符号,就表示该命令在后台执行

 [1]:  当前shell的后台作业(进程)序号

 7981: 当前这个后台进程(ls进程)的进程号(PID)

 Shell启动该后台进程后不再等待该进程结束,立即开始接受新的键盘命令——多进程并发, 数量不限, 充分利用系统资源。

10.4    shell 变量和引用符

每一个shell都可以设定一组变量来限定shell及其子程序的功能模式和取值范围, 这些变量中有些是系统设定的, 有些是由用户设定的.

每个shell都可以有完全不同的变量设置, 由此构成各具特色的运行环境。

系统的基本环境变量放在/etc/profile中, 用户环境变量放在用户主目录下的.profile文件中, 用户shell启动时, 先执行/etc/profile, 再执行用户主目录下的.profile。

环境变量可在shell运行时动态修改。

10.4.1  环境变量      常用的shell环境变量及实例:

HOME=/usr/computer/student6        用户主目录, 注册时的初始目录

PATH=/bin:/usr/bin:$HOME/bin:./                   键盘命令的搜索路径

SHELL=/bin/sh                                         用户的初始shell的路径名称

TERM=vt100                                                         当前所用的终端类型

PS1=$                                                                              shell的主提示符

IFS=                       域分隔符, 通常为空白符, 用来分隔命令行各个域

echo 命令的使用

           echo命令的基本功能就是在标准输出上显示后面的字符串,或变量的值。当字符串中带空白符或其它控制字符时,用引号将其括起来。例如:

$ echo  12345

12345

$ echo  "department computer"

department computer

$ echo "My home directory is: $HOME"

My home directory is:  /usr/teacher/david

$ echo –n "Input your choice (y/n) [  ]\b\b"

Input your choice (y/n) [ _ ]

10.4.2   系统变量

常用系统变量:

$0           当前shell程序的名字

$1 ~ $9   命令行上的第一到第九个参数

$#           命令行上的参数个数

$*           命令行上的所有参数

$@         分别用双引号引用命令行上的所有参数

$$           当前进程的进程标识号(PID)

$?           上一条命令的退出状态

$!            最后一个后台进程的进程标识号

系统变量只能引用不能修改!  

系统变量应用举例:

$ echo aa  bb  cc  dd  $$

aa  bb  cc  dd  2391

$ cat  file1  file2  > file3  2> errlog

$ echo $?     (非0表示命令运行失败,  错误信息在 errlog 文件中)

$ echo

                                           (空行, 即echo输出串尾隐含的换行符)

$ echo  This is         a       test.                         (单词间多个空格)

This is a test.

$ echo  "This is         a      test."         (用引号包括时结果如何?)

10.4.3    局部变量(用户变量)

   局部变量是由用户根据需要任意创建的.  变量名通常由一个字母后跟零个到多个字母、数字或下划线组成。引用变量的值时,在变量名前面加上$符号.  例如:

$ AA=123                                                                 定义变量AA

$ echo  $AA                                                      引用变量AA的值

123                                                                          (变量AA的值)

$  B="this is a string"         定义变量B, (字符串中有空格时用引号)

$  echo  $B                                                           引用变量B的值

this is a string                                                          (变量B的值)

10.4.4    单引号、双引号、反撇号和花括号

$ a="he is a student"

$ echo "She said: $a"

She said: he is a student                          echo执行时,替换了变量$a的值

$ b='The value of a is $a'

$ echo $b

The value of a is $a                                echo执行时,未替换了变量$a的值

   shell规定单引号禁止变量替换, 元字符$和*等保持其符号本身; 而双引号允许元字符变量替换.

$ c="The value of a is $a"

$ echo $c

The value of a is he is a student

10.4.4    单引号、双引号、反撇号和花括号
 

$ a=date

$ echo $a

date                                                              (变量a的值是字符串date)

$ b=`date`                                  (反撇号中的字符串作为命令名)

$ echo $b

Sat Feb 1 16:28:19 Beijing 2003

                                      (变量b的值是反撇号中命令的执行结果)

10.4.4    单引号、双引号、反撇号和花括号

$ c="There is a teach"

$ echo "$cer reading room"

reading room                          (未定义变量cer, 其值用空串替代)

$ echo "${c}er reading room"

There is a teacher reading room

(花括号将变量名和后面的字符串区分开)

10.4.5   变量输出命令 export

       新的shell变量定义后或已有的shell变量修改值后, 如果未经export命令输出, 则只在当前的shell中起作用, 对其各个子shell不产生任何影响. 经过 export 命令输出的变量才能对当前shell的各个子shell、以及子shell的子shell起作用。例如:

$ PATH=$PATH:./

$ export  PATH

说明:  

1.    export后面的变量名前不加$符号

2.    经export输出给子shell的变量如果在子shell中被修改, 则只影响子shell, 不影响父shell; 如果在子shell中被输出,则只影响子shell的子shell

3.    export命令常用在.profile文件中

10.5    Shell的内部命令

        shell 的内部命令包含在shell内部, 不是一条单独的操作系统命令, 因此无法在文件系统中查找到.

例如:

cd       改变当前工作目录

pwd    显示当前工作目录

time  显示当前shell运行命令所花费的时间,例如:

         $ time  ls –lR /usr  >  flist

         real    2m 17.32s     该ls进程的总运行时间

         user   0m 7.63s       其中用户程序部分的运行时间

         sys     0m 6.79s       其中操作系统核心部分运行时间

10.6    进程监控

10.6.1  进程及进程状态

      UNIX/Linux系统中的“进程”是可运行程序在内存中的一次运行实例。

进程和程序的主要区别是:

    . 进程是动态的, 它有自己的生命周期和不同状态; 而程序是静态的, 通常存放在某种介质(如磁盘或纸张等)上。

    . 进程具有运行控制结构和作用数据区;程序没有。

    . 一个程序可以同时在内存中有多个运行实例, 即同时作为多个进程的组成部分。

10.6.1  进程及进程状态

 每个进程运行时都有如下生命周期:

            创建运行等待运行…等待运行结束

生命周期大致分为三种状态:

. 运行态    

       进程正占用CPU和其它资源进行运算.

. 就绪态

       进程已做好一切准备, 等待获得CPU投入运行.

. 睡眠态

       进程因等待输入输出或其它系统资源, 而让出CPU资源, 等待运行条件满足。

10.6.2   获取进程状态信息:   ps 命令

 不带参数的ps命令运行时, 显示该用户当前活动进程的基本信息:

    $ ps
     PID          TTY        TIME        COMMAND
     612           tty08        0:37           sh
     931           tty08        0:01           ps
     $

PID                   进程标识号. 系统每个进程在其生命周期都有一个唯一的PID.

TTY                 启动该进程的终端号

TIME               进程累计占用CPU的时间

COMMAND   产生该进程的命令

 ps命令的常用任选项 -e  (或-a)     显示系统中所有活动进程的信息.

$ ps  -e
PID          TTY          TIME       COMMAND
0                   ?          0:00        swapper
1                   ?          0:01        init
358             00          0:01        sh
695             01          0:01        sh
23                 ?          0:00        logger
732             03          0:00        vi
25                 ?          0:00        cron
681             02          0:00        getty
623             03          0:01        csh
732             01          0:01        ps
$

-f        显示该进程的所有信息. 例如:

$  ps  -f
UID   PID    PPID    C    STIME      TTY    TIME     COMMAND
liu      298    1          0    14:57:02    02      0:02      sh
liu      395    298     16   16:31:19    02      0:00      ps  -f

其中:

UID                    进程所有者的用户标识数

PID                    进程标识数

PPID                  本进程的父进程标识数

C                        进程调度参数, 反映本进程使用CPU的状况

STIME              进程的启动时间

TTY                   启动进程的终端

TIME                 进程累计占用CPU的时间

COMMAND      启动该进程的命令名

10.6.3   暂停进程运行:   sleep 命令

                     sleep  time

  sleep命令使运行它的进程暂停time指定的秒数.

例如:

 $  sleep  5
     [进程暂停5秒钟, 什么也不作]
 $

 $ sleep 10;  who
     [进程暂停10秒钟后, 显示系统中登录的用户名]

 $ echo "I am sleeping…"; sleep 100; echo "I am awake"
  I  am  sleeping …     [等待100秒钟]
  I  am  awake
 $

10.6.4   终止进程运行:    kill 命令

通常在三种情况下进程被终止运行:

     . 进程运行完成,  自动消亡;

     . 用户按^c 或 Del 等中断键,  强行终止前台进程的运行;

     . 用户发出 kill 命令, 强行终止后台进程或键盘锁住了的前台进程的运行.

kill 命令的三种常用格式为:

          kill         PID

正常结束进程, 自动完成所有善后工作, 作用类似于按 Del 键.

          kill   -1   PID

先挂起该进程, 终止子进程, 完成善后工作, 再终止该进程.

          kill   -9   PID

立即强行终止该进程, 不作任何善后工作.  可能出现资源浪费和"孤儿"进程.

10.7  shell 编程

10.7.1    shell 编程的基本过程主要包含以下三步:

1. 建立 shell 文件

       包含任意多行操作系统命令或shell命令的文本文件;

2. 赋予shell文件执行权限

        用chmod命令修改权限;

3. 执行shell文件

       直接在命令行上调用该shell程序.

10.7.2   实例:

1. 建立shell文件 (可用任何建立文本文件的方法):

      $ cat  prog1

       who  |  grep  $1

2. 赋予执行权限:  (初始文本文件无执行权限)

      $ chmod  740  prog1

3. 执行该shell程序

      $ prog1  student5

      prog1: not found    

      (shell在标准搜索目录中找不到prog1命令)

4. 指定路径或修改环境变量PATH后执行shell程序

      $ ./prog1  student5

      student5   tty06   Feb 8   09:12

10.7.3   shell 程序和语句

 shell 程序由零至多条shell语句构成.  shell语句包括三大类: 功能性语句、结构性语句和说明性语句.

说明性语句:

         以#号开始到行尾的部分,不被解释执行。

功能性语句:

         任意的操作系统命令、shell内部命令、自编程序、其它shell程序名等。

结构性语句:

         条件测试语句、多路分支语句、循环语句、循环控制语句等。             

10.7.3.1   说明性语句(注释行)

 注释行可以出现在程序中的任何位置,既可以单独占用一行,  也可以接在执行语句的后面. 以#号开始到所在行的行尾部分,都不被解释执行. 例如:

#! /bin/sh         #告诉OS用哪种类型的shell来解释执行该程序
#
# 本程序说明
#
command_1
command_2                        # command_2的语句说明
……
# 下面程序段的说明
command_m
……
command_n                       # command_n语句的说明
……

10.7.3.2   几个常用的功能性语句(命令)

1、read命令

     read从标准输入读入一行, 并赋值给后面的变量, 其语法为:

   .   read  var

            把读入的数据全部赋给var

   .   read  var1  var2  var3

            把读入行中的第一个参数赋给var1, 第二个参数赋给var2, ……,把其余所有的参数赋给最后一个变量.

        如果执行read语句时标准输入无数据, 则程序在此停留等侯, 直到数据的到来或被终止运行.

10.7.3.2   几个常用的功能性语句(命令)

read应用实例(命令行执行)

       read    读取标准输入(键盘), 输入的内容赋给后面的变量.
$ read  name   age
       yilan   23                                                从键盘输入的内容
$ echo  "student  $name  is  $age  years  old"
       student  yilan  is  23  years old           屏幕显示的内容


read应用实例(shell脚本程序)

# example1  for read
echo  -n "Input  your  name: "
read  username
echo  "Your name is  $username"


read应用实例(shell脚本程序)

#example2  for  read
echo –n  "Input  date  with  format  yyyy  mm dd: "
read  year  month  day
echo  "Today  is  $year/$month/$day,  right?"
echo  -n "Press  any  key  to  confirm  and  continue"
read  answer
echo "I  know  the  date,  bye!"

2、  expr 命令

       算术运算命令expr主要用于进行简单的整数运算,包括加(+)、减(-)、乘(\*)、整除(/)和求模(%)等操作。例如:
 

$ expr  12  +  5  \*  3    #反斜线去掉*号的元字符含义
27
$ expr  3  -  8  /  2
-1
$ expr   25  %  4
1
$ num=9
$ sum=`expr  $num  \*  6 `    #反撇号引用命令的运行结果
$ echo  $sum
54

3、 tput 命令

tput命令主要用于设置终端工作模式,  或读出终端控制字符。 

tput命令与终端控制代码数据库terminfo相连, 根据shell环境变量TERM的值, 读出这种终端的指定功能控制代码。

常用的终端显示功能控制符如下表:

选项

功    能

选项

功    能

bel

终端响铃

el

光标位置到行末清字符

blink

闪烁显示

smso

启动突出显示模式

bold

粗体字显示

smul

启动下划线模式

clear

清屏

rmso

结束突出显示模式

cup  r c

光标移到 r c

rmul

结束下划线模式

dim

显示变暗

rev

反白显示

ed

光标位置到屏幕底清屏

sgr0

关闭所有属性

tput应用实例1:

#
# program1   for   tput
#
tput  clear
tput  cup  11  30
tput  rev
echo  "Hello,  everybody!"
tput  sgr0
tput  cup  24  1

该程序先清屏,  并在屏幕中央位置(11行30列)用反极性显示字符串“Hello,  everybody!”,  恢复正常显示极性后光标停留在屏幕左下角。

tput应用实例2:

#
# program2   for   tput
bell=`tput  bel`
s_uline=`tput  smul`
e_uline=`tput  rmul`
tput  clear
echo  $bell   $s_uline
tput  cup  10  20
echo  "Computer  Department"
echo  $e_uline

功能:  响一声铃后, 在清空的屏幕中央以下划线模式显示字符串“Computer  Department”,完成后重置正常显示模式。

10.7.4   结构性语句

         结构性语句主要根据程序的运行状态、输入数据、变量的取值、控制信号以及运行时间等因素来控制程序的运行流程。主要包括以下几类语句:

  •   条件测试语句(两路分支)
  •   多路分支语句
  •   循环语句
  •   循环控制语句

无任何结构性语句的shell脚本程序是一种特例。

10.7.4  结构性语句 —— 条件语句 if...then...fi

语法结构:

         if    表达式
         then  命令表
         fi     

如果表达式为真, 则执行命令表中的命令; 否则退出if语句, 即执行fi后面的语句。 if和fi是条件语句的语句括号, 必须成对使用。

命令表中的命令可以是一条, 也可以是若干条; 既可以直接在then语句后面写, 也可以提行书写。

if语句应用实例

    shell程序prog2, 测试命令行参数是否为一个已存在的文件或目录。 用法为:

               prog2   file


prog2脚本内容如下:  

#The statement of  if…then…fi                      (注释语句)
if [  -f   $1  ]                                                       (测试命令行第一个参数是否为文件)
then
        echo "File  $1  exists"                       (引用变量值)
fi
if [  -d   $HOME/$1  ]                                        (测试参数是否为目录)
then
        echo "File  $1  is  a  directory"         (引用变量值)
fi


执行prog2程序:

$ prog2   prog1
File  prog1 exists
      $0为prog2; $1为prog1, 是一个已存在的文件.
$ prog2   backup
File  backup is  directory
      $0为prog2;  $1为backup,是一个已存在的目录.

10.7.4  结构性语句 —— 条件语句 if...then...else...fi

语法结构为:

        if      表达式
        then 命令表1
        else  命令表2
        fi

如果表达式为真, 则执行命令表1中的命令, 退出if语句; 否则执行命令表2中的语句, 退出if语句。

注意: 无论表达式是否为真, 都有特定的语句要执行。

应用实例:    shell程序prog3,  用法为:

                prog3   file

prog3的内容如下:

#The statement of if…then…else…fi
if [  -d  $1  ]
then
        echo "$1  is  a  directory"
        exit                                                (立即退出当前的shell程序)
else
        if  [  -f   $1  ]
        then
            echo  "$1  is  a  common  file"
        else
            echo  "unknown"  
        fi
fi

(如果没有exit语句,程序的执行结果有什么不同?)

如果没有exit语句,prog3就只有一个出口(最后一个fi语句);如果有exit语句,prog3就有两个出口。

运行prog3程序:

$ prog3  backup
backup  is  a  directory
$ prog3  prog1
prog1  is  a  common  file
$ prog3  abc
unknown

prog3是对prog2的优化, 逻辑结构更加清晰合理!

10.7.4  结构性语句 —— 测试语句 test

test语句可测试三种对象:

          字符串     整数     文件属性

每种测试对象都有若干测试操作符,例如:

      test  "$answer"  =  "yes"
      变量answer的值是否为字符串yes
      test  $num –eq  18
      变量num的值是否为整数18
      test  -d  tmp    
      测试tmp是否为一个目录名

test命令的应用:

       test命令测试的条件成立时, 命令返回值为真(0),否则返回值为假(非0).

      用法一.

                            test   $name  -eq   $1   
                            echo  $?


      用法二.

                            if   test   -f   $filename
                            then
                                     ……
                            fi
通常简写为:       if  [   -f  $filename  ]    #大部分版本的系统都要求左方括号的右边和右方括号的左边至少一个空格

10.7.4  结构性语句 ——多路分支语句 case…esac

多路分支语句case用于多重条件测试, 语法结构清晰自然.  其语法结构为:
 

     case   字符串变量   in
            模式1)
                       命令表1
                        ;;
            模式2)
                       命令表2
                        ;;
             ……
            模式n)
                       命令表n
                        ;;
    esac

10.7.4  结构性语句 ——多路分支语句 case…esac

实例.  程序prog4检查用户输入的文件名, 用法为:

           prog4    string_name
# The statement of  case…esac
if [  $#  -ne  1  ]
then
        echo  “One argument must be declared"
        exit
fi
case  $1  in
        file1)
            echo  "User selects file1"
            ;;
        file2)
            echo  "User selects file2"
            ;;
        *)
            echo  "You must select either file1 or file2!"
            ;;
esac

10.7.4  结构性语句 —— 循环语句 for…do…done

       当循环次数已知或确定时,  使用for循环语句来多次执行一条或一组命令.  循环体由语句括号do和done来限定。  格式为:              

                     for   变量名   in   单词表
                     do
                              命令表
                     done


变量依次取单词表中的各个单词,  每取一次单词, 就执行一次循环体中的命令.  循环次数由单词表中的单词数确定. 命令表中的命令可以是一条, 也可以是由分号或换行符分开的多条。
    如果单词表是命令行上的所有位置参数时, 可以在for语句中省略 “in  单词表” 部分。
实例.  包含for语句的程序prog5寻找指定文件, 或拷贝当前目录下的所有文件到backup子目录下.  使用语法及程序为:
 

                           prog5    [filename]
# The statement of for…do…done
if  [  !  -d  $HOME/backup  ]
then
        mkdir  $HOME/backup
fi
flist=`ls`
for  file  in  $flist
do
        if [  $#  =  1  ]
        then
            if [  “$1”  =  “$file”  ]
            then
                echo  "$file  found" ;  exit
            fi
        else
            cp  $file  $HOME/backup
            echo  "$file  copied"
        fi
done
echo   '***Back  up  completed***'

10.7.4  结构性语句 —— 循环语句while…do…done
语法结构为:      
                              while     命令或表达式
                              do
                                       命令表
                              done
while语句首先测试其后的命令或表达式的值,如果为真,就执行一次循环体中的命令,然后再测试该命令或表达式的值,执行循环体,直到该命令或表达式为假时退出循环。
while语句的退出状态为“命令表”中被执行的最后一条命令的退出状态。
实例.   创建文件程序prog6,  用法为:
              prog6   file   [number]    #根据命令行参数的个数确定循环的次数
# The statement for  while
if [ $# -eq 2 ]
then
        loop=$2
else
        loop=5
fi
i=1
while  [  $i  -le   $loop  ]
do
        >$1$i        #建立以file开头, 变量i的值结尾的空文件名. 参见命令cmd  >  file
        i=`expr  $i  +  1`
done

10.7.4  结构性语句 —— 循环语句until…do…done
语法结构为:    until   命令或表达式
                          do
                    命令表
                          done
until循环与while循环的功能相似,  所不同的是只有当测试的命令或表达式的值是假时, 才执行循环体中的命令表, 否则退出循环。 这一点与while命令正好相反。
10.7.4  结构性语句 ——循环控制语句 break 和 continue
      break语句从包含该语句的最近一层循环中跳出一层,  break  n  则跳出n层;  continue语句则马上转到最近一层循环语句的下一轮循环上,  continue  n则转到最近n层循环语句的下一轮循环上.

实例.  程序prog7的用法为:
          prog7   整数   整数   整数 …
参数个数不确定, 参数的数量为1~10个, 每个参数都是正整数。prog7的代码如下:
if  [  $#  -eq  0  ]
then
        echo  "Numeric  arguments  required"
        exit
fi
if  [  $#  -gt   10  ]
then
        echo  "Only  ten  arguments  allowed"
        exit
fi
for  number    #取所有位置参数
do
        count=`expr  $number  %  2`        #用2求模, count的值只能是0或1
        if [  $count  -eq  1  ]        #是奇数
        then
            continue
        else
            output="$output   $number"        #偶数放到偶数队列中
        fi
done
echo  "Even  numbers:  $output "

实例.  包含while、until和break语句的prog8 程序.

while  true    #无限循环程序, 只能由人工中断
do
        echo  level1
        until  false
        do
            echo  level2
            while  true
            do
                echo  level3
                break    #改为continue 3开始最外层循环    #如果改为break  3跳出最外层循环
            done
        done
done
10.8  shell 函数
在shell程序中, 常常把完成固定功能、且多次使用的一组命令(语句)封装在一个函数里,每当要使用该功能时只需调用该函数名即可。
函数在调用前必须先定义,即在顺序上函数说明必须放在调用程序的前面。
调用程序可传递参数给函数, 函数可用return语句把运行结果返回给调用程序。
函数只在当前shell中起作用, 不能输出到子shell中。
shell函数的说明格式:
     function_name ( )
    {
            command1
            ……
            commandn
     }
函数的调用格式:
     value_name=`function_name  [arg1 arg2 … ]`    #函数的所有标准输出都传递给了主程序的变量
或者:
     function_name  [arg1  arg2  …  ]    #函数的返回值隐含在变量中, 由主程序使用该变量的值
     echo   $value_name
shell函数调用实例:
check_user( )      #查找已登录的指定用户
{
        user=`who |  grep  $1`
        if [  -n  “$user” ]
        then
               return  0       #找到指定用户
        else
               return  1       #未找到指定用户
        fi
}
while  true         # MAIN, Main, main:   program  begin  here
do
        echo  -n "Input username: "
        read   uname
        check_user  $uname     # 调用函数, 并传递参数uname
        if [ $? –eq  0 ]                 # $?为函数返回值
        then    echo  "user  $uname  online"
        else    echo  "user  $uname  offline"
        fi
done
10.9  shell编程 —— 实例1
功能:给一个文本文件中的每一行前面加上行号。
注意:while循环体的标准输入,被重定向为从管道中读。
          while循环体的标准输出,被重定向到了 tmp$$文件中,这里的$$就是当前进程的进程号,因为进程号的唯一性,所以多个用户同时运行该程序时,不会相互覆盖!
          while循环体内的echo语句的标准输出,被重定向到了当前进程的控制终端(/dev/tty),即启动本进程的用户正在使用的终端。
#!/bin/sh
# Program name: numberit
# Put  line  numbers  on  all  lines  of  a  file
if [ $#  -ne  1 ]
then
       echo  "Usage: $0  filename "  >&2
       exit 1
fi
count=1                     # Initialize count
cat  $1  |  while  read  line   
# Input is coming from file on command line
do
     [ $count -eq 1 ]  &&  echo "Processing file $1..."  >  /dev/tty
     echo  $count  $line
    count='expr $count + 1'
done  >  tmp$$           # Output is going to a temporary file
mv  tmp$$  $1
运行情况:
$ cat  test_file
abc
def
ghi

$ numberit  test_file
 Processing  file  test_file …

$ cat  test_file
1  abc
2  def
3  ghi
10.9  shell编程 —— 实例2
功能:程序从文件中每读入一行,就询问用户是否正确,如用户回答正确(以y或Y开头的字符串)就直接输出;否则就提示用户重新输入这行的内容,并输出。
说明:while循环体的标准输入被重定向为file.old语句,因放在done语句后面的,容易被忽略。
          while循环体内的read语句的标准输入已被重定向为当前键盘(/dev/tty),而不是file.old
          while循环体的标准输出没有重定向,所以其中的提示类信息是显示在终端上的,但输出的文件内容,却被重定向到file.new中了。
#!/bin/sh
# Scriptname: speller
# Purpose: Check and fix spelling errors in a file
>file.new
while  read  line                              # Read from the tmp file
do
     echo  $line
    echo  –n  "Is  this  word  correct? [Y/N] "
     read  answer  <  /dev/tty         # Read from the terminal
     case  "$answer"  in
     [Yy]*)
          echo  $line  >>  file.new
          ;;
        *)
          echo  "What is the correct spelling? "
          read  word  <  /dev/tty
          echo  $word  >>  file.new
          echo  $line  has  been  changed  to  $word.
     esac
done  <  file.old

10.10  流编辑器sed
一. 什么是流编辑器?
      流编辑器是一种流水线型的、非交互式的文本编辑器。 它使用户可以在命令行上(而不是编辑器中)对文件进行无破坏性编辑。
屏幕编辑器与流编辑器的区别

项目

vi

sed

1.用户操作方式

2.文本处理模式

3.编辑命令地点

4.编辑空间

5.对原文本影响

6.批量发出命令

7.基本编辑单位

8.主要应用场合

9.可编文件大小

交互式

全局并行(可逆行)

编辑器中

临时文件(文件缓存)

破坏性的

不能

字符

人工编辑

较小

非交互式

逐行串行(不可逆行)

命令行上

模式空间(行缓存)

非破坏性的

可以

程序自动编辑

较大

10.10.1  sed命令的基本格式
sed   ‘command’   file
sed   -n  ‘command’  file
sed   -e  ‘command1’ –e ‘command2’ file
sed   -f  cmd_file  file

任选项说明:
command:   普通行编命令
              -n:  只显示与模式匹配的行(缺省都显示)
              -e:   在同一命令行上进行多次编辑
              -f:   编辑命令放在随后的命令表文件中
              file:  被编辑的文本文件
注意:
       sed命令的结果是送到标准输出上,即荧光屏上,如果要将结果保存在文件中,应该使用重定向功能!

例如:
   sed  ‘s/student/teacher/g’  oldfile  > newfile

如果要把结果保留在被编辑的原文件中,该如何办?
如果要把结果保留在被编辑的原文件中,只有先把结果暂存在一个临时文件中,编辑结束后再把临时文件换名为原文件名。例如:
sed  .......  file_org  >  file_tmp
mv  file_tmp  file_org
10.10.2  sed中常用的行编辑命令格式
[行定位符][编辑命令元字符]
      例如:    sed  ‘1,9d’  abc
                   sed  -n ‘196p’  abc
/正则表达式/[编辑命令元字符]
      例如:      sed  -n  ‘/student/p’   filename
                      sed  ‘/xyz/d’   filename
3. [定位符][元字符]/正则表达式/[元字符]
 例如:   sed –n ‘3,8s/east/west/’  filename
      sed  –n  ‘1,$s/computer/network/g’  filename
10.10.3  sed中常见的出错信息和退出状态
操作系统命令出错:
               sed  -r  ‘s/this/that/’  myfile
显示:       sed: ERROR: Illegal option – r
退出状态值:   1
例如:
                sed  -n  ‘s/this/that/’   newfile
显示:        sed: ERROR: Cannot open newfile:
                No such file or directory
退出状态值:   2
10.10.3  sed中常见的出错信息和退出状态
2. 正则表达式出错和模式不匹配:
sed  -n  ‘s/this/that’   newfile
显示:        sed: ERROR: Command garbled:
                s/this/that
退出状态值:   0
 例如:
                 sed  -n ‘s/this/that/’   newfile
显示:         无 (文件newfile中无this字符串匹配)
退出状态值:   0
3. 出错信息保存和退出状态值检测
保存出错信息:
       sed –n  ‘1,$s/abc/xyz/’  file  2> err_log
或:   sed –n  ‘1,$s/abc/xyz/’  file  2>> err_log
        (这两条命令功能上的区别是什么?)
常用的退出状态值检测方式:  
 ①   echo  $?                      ②  if [  $?  -eq  0  ]
       其它间接处理                   then
                                                         正常处理    
                                                  else
                                                         出错处理
                                                  fi
10.10.4  sed应用实例
1.打印文件内容:   p命令
      sed  -n  ‘22, 35p’  file1
               打印file1的第22~35行
      sed  -n  ‘/string/p’ file2
               打印file2中包含string的行
      sed  -n  ‘9, /^uestc/p’  file3
               打印file3中第9行到以uestc开头的行
      sed  -n  ‘/[Cc]hina/p’  file4
               打印file4中包含China或china的行
2. 删除文件内容:   d命令
         
         sed   ‘76d’  file5
               删除file5中的第76行
         sed  ‘9,$d’   file6
               删除file6中第8行以后的所有行
         sed   ‘/co*ool/d’  file7
               删除file7中包含cool, coool, cooool, ……等等的行
3. 替换文件内容: s命令
      sed  -n  ‘s/beijing/shanghai/g’  table1
        将table1中所有的beijing替换为shanghai
      sed  -n  ‘s/^ *uid/username/p’  ulist
        将ulist中以零至多个空格开头后跟uid
        的字符串替换为username
4. 多次编辑: e命令
      sed  -e  ’1,5d’  -e  ‘s/good/bad/’  report
        将report中的第1~5行删除, 同时将good替换为bad   
5. 添加行:  a命令

     sed  ‘/^operation/a\
     this  is  an  inserted  line’  course
        在course中的以operation开头的行后加入this is an inserted line一行。

.  course中有多行以operation开头时会怎样?
.  不是另加一个新行, 而是在某行中加入字符串, 如何操作?
特别说明
不同的UNIX操作系统版本中,  sed的格式和语法可能有少量的差异, 使用时可参照联机手册(man命令)。
教材中的例子大部分是在C_shell下进行的, 在B_shell或K_shell下可能有少量的差异. 但这些差异只反映在shell命令中, 而不会反映在编辑命令(表达式)中。
10.11  编程工具 awk
什么是awk
        awk 是一种程序设计语言, 主要用来处理文本类数据并产生报表。
       它执行时对输入数据(文件、标准输入或命令的输出)逐行进行扫描,匹配指定的模式,并执行指定的操作。
10.11.1  awk的基本格式
               awk   ‘pattern   {action}’   filename
 awk扫描filename中的每一行,  对符合模式pattern的行执行操作action。

特例:
①         awk   ‘pattern’   filename
                显示所有符合模式pattern的行
②         awk   ‘{action}’  filename
                对所有行执行操作action
10.11.2  数据文件中记录和域的标识

图中黑色部分的表示数据文件中的实际内容,彩色部分为标识的方法和标识的名称。

数据文件中的每一行就是一条记录,每一个字段就是一个域。

在查找包含空格的字符串(Mary  Adams)时要特别小心该空格的状况, 是一个、两个、多个还是Tab符。

域与域之间的间隔符通常是由shell的环境变量IFS的值(通常为空格)来设定的,为避免上述问题,有些时候把数据文件中域之间的间隔符设定为逗号、句话等可显字符,如果是这样,在使用awk时要把IFS的值也作相应的改变。

10.11.3  应用实例
 

$cat  employees
Tom   Jones   4424    5/12/66      543354
Mary  Adams   5436    11/4/63      28765
Sally Chang   1654    7/22/54      650000
Billy Black   1683    9/23/44      336500
$awk  ‘/Mary/’   employees
Mary  Adams  5436    11/4/63     28765
$awk  ‘{print  $1}’   employees
Tom
Mary
Sally
Billy
$awk  ‘/Sally/  {print  $1, $2}’   employees
Sally   Chang

$awk  ‘/Mary/’   employees    —— 打印employees文件中包含字符串Mary的记录。
$awk  ‘{print  $1}’   employees  —— 打印employees文件中所有记录的第一个域。
$awk  ‘/Sally/  {print  $1, $2}’   employees —— 查找包含Sally的行,把其中的第一和第二个域打印出来。

10.11.4  awk的输入重定向形式
1. 从其它命令输入

格式:      command  |  awk  ‘pattern’
           command  |  awk  ‘{action}’
           command  |  awk  ‘pattern  {action}’


实例:   

$who
zhanglan   tty01    Jan  12   18:36
yuexi      tty02    Jan  12   17:03
liuzhen    tty15    Jan  12   08:45
$who |  awk ‘/tty01/ {print  $1}’    (谁在1号终端上)
zhanglan    


2. 从标准输入设备(键盘)输入
           格式:          awk  ‘pattern  {action}’
由于未指定输入数据来源, 缺省情况下从标准输入
设备(键盘)读取数据. 键盘上每输入一行, awk就处
理一行, 直到遇到^D为止.
例如:   

$ awk  ‘/aaa/ {print  $0, NF}’#查找包含字符串aaa的行,打印整行内容和这行包含的域数
bbbb   bbbbb    bbbbb
aaaa  aaaaaa   aa  aaaaaa
aaaa  aaaaaa   aa  aaaaaa  4             (这是awk的输出行)
xxx  xxxxx   yyyyyyy   xyz
Ctrl+D
$


本例中awk命令后的第二个命令行参数(文件名)缺乏,所以awk要从标准输入(键盘)上读取数据。键盘输入的第一行中没有包含aaa字符串,所以awk没有输出;键盘输入的第二行内容中包含aaa字符串,所以awk命令有输出(第三行);第四行是键盘输入的第三行,同样因为其中没有包含aaa字符串而没有输出,最后输入的^D表示键盘输入结束,因此awk执行结束。
10.11.5  awk的格式化输出
print 函数
           用于不需要复杂格式的简单输出。
例如:

$ ps -e |  awk ‘/tty05/  {print “Terminal 05: ”  $4}’
       (查看5号终端上的用户现在正在干什么)
Terminal 05:  sh
Terminal 05:  cc
Terminal 05:  find


$
2.  printf 函数
         高级格式化输出函数. 用法与C语言中的用法相同。
例如:

$awk ‘{printf “uname: %-8s ID: %6d\n”, $1, $3}’employees
uname:  Tom                ID:   4424
uname:  Mary               ID:   5346
uname:  Sally              ID:    1654
uname:  Billy              ID:    1683


$
10.11.6  awk命令文件
     格式:
           awk  -f   awk_file   data_file    
     当需要对输入数据中的一行执行多项操作时, 常把这些操作命令放在一个命令文件awk_file中, 而不是在命令行上发出.
     awk运行时, 对输入文件中的每一行执行命令文件中的所有操作后, 再对下一行数据进行同样的处理过程, 以此类推, 直到输入文件中的最后一行。
              
应用实例:
 

$ cat  my_awk
/Sally/ {print “**** found  Sally!  ****”}
{print  $1, $2, $3}
$ awk  -f  my_awk   employees
Tom  Jones    4424
Mary Adams   5436
**** found  Sally!  ****
Sally Chang     1654
Billy  Black     16

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值