Linux Shell编程与编辑器使用详解
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

1.3 Shell基本语法

与大多数编程语言相比,Shell编程语言的语法简单、易于掌握。在学习Shell编程之前,首先要了解Shell的一些基本语法及约定,包括大小写约定、Shell注释、输入输出以及如何运行一个Shell程序等。

1.3.1 Shell脚本中的注释

在各种编程语言中都普遍使用注释。注释只是为阅读代码提供便利,相当于代码的解释和说明,通常用于记录代码的用途、代码编写人、修改时间或代码编写方法等。在执行代码时,注释会被Shell解释器自动忽略。一个优秀的程序员应该养成在代码中书写注释的习惯,良好的注释将有利于日后的维护和更新。

Bourne Again Shell和TC Shell的注释语法相同,使用符号#标识注释行。如果符号#在脚本的第一行出现并且其后没有感叹号!,或者#出现在脚本中的其他任意位置,那么Shell将其视为注释的开始。Shell会忽略从符号#开始到行末(即下一个换行符)之间的所有内容。

例如下面是/etc/rmt文件的部分内容,代码中使用了大量注释,如下所示。

    # cat  /etc/rmt                    //使用cat命令显示rmt文件的内容
    #                                  //以下注释对该文件的功能进行描述
    #!/bin/sh                          //Shell语句的执行开始语句
    #
    # This is not a mistake.  This shell script (/etc/rmt) has been provided
    # for compatibility with other Unix-like systems, some of which have
    # utilities that expect to find (and execute) rmt in the /etc directory
    # on remote systems.      // #以后的语句的为注释语句
    #
    exec /usr/sbin/rmt…  …    // 执行语句

1.3.2 Shell通配符的使用

Shell中支持使用通配符,常用的通配符如下所示。

· ?:表示任意的单个字符。

· *:表示任意长度的任意字符串。

· [ ]:表示匹配放在[ ]中的字符集中的任意一个字符。

· { }:将大括号中的字符串以及前导字符串和后继字符串作为匹配条件。

例如,显示当前目录下的所有文件,命令如下所示。

    # ls *
    10.1  10.11  10.13  10.15.1  10.16.1  10.2  10.4  10.6  10.8  11.16.5  my.txt
    10.10  10.12  10.14  10.15.2  10.16.2  10.3  10.5  10.7  10.9  11.16.2  program.txt

例如,显示当前目录下以字符串“10.1”开头的文件,命令如下所示。

    # ls 10.1*
    10.1  10.10  10.11  10.12  10.13  10.14  10.15.1  10.15.2  10.16.5  10.16.2

例如,表示当前目录下所有以.txt结尾的文件,命令如下所示。

    # ls *.txt
    my.txt  program.txt

例如,显示当前目录下所有名称包含“16”的文件,命令如下所示。

    # ls *16*
    10.16.1  10.16.2  11.16.5  11.16.2

例如,显示当前目录下所有文件名以“10”或“11”开头的文件,命令如下所示。

    # ls 1[0,1]*
    10.1  10.11  10.13  10.15.1  10.16.1  10.2  10.4  10.6  10.8  11.16.5
    10.10  10.12  10.14  10.15.2  10.16.2  10.3  10.5  10.7  10.9  11.16.2

例如,显示所有由4个字符组成的文件名,且前3个字符必须是“10.”,命令如下所示。

    # ls 10.?
    10.2  10.3  10.4  10.5  10.6  10.7  10.8  10.9

例如,显示所有由4个字符组成的文件,且前3个字符必须是“10.”,第4个字符必须是1或2或3或4,命令如下所示。

    # ls  10.[1234]
    10.2  10.3  10.4

通配符[]在表示一个连续的数字或字母范围时可以使用-。例如,[1-9]表示数字1~9之间任意一个数字,[A-Z]表示大写字母A到大写字母Z之间的任意一个字母。[1-389]表示数字1、2、3、8、9中的任意一个数字。可以使用逗号来表示多个集合,例如[A-H,w-z]表示大写字母A到H,小写字母w到z之间的任意一个字母。

在一个集合前使用前缀!表示取反,即由指定集合之外的所有字符组成的集合。例如,[aoeiu]表示所有元音字母中的任意一个字母,而[!aoeiu]表示所有辅音字母中的任意一个字母。

例如,显示所有由4个字符组成的文件,且前3个字符必须是10.,第4个字符必须是1或2或3或4,命令如下所示。

    # ls  10.[1-4]
    10.2  10.3  10.4

例如,显示所有由4个字符组成的文件,且前3个字符必须是“10.”,第4个字符必须是1或2或3或8或9,命令如下所示。

    # ls  10.[1-389]
    10.1  10.2  10.3  10.8  10.9

在查找文件时,大括号是经常使用的通配符,其基本格式为:

    前导字符串{字符串1,字符串2,……}后继字符串

例如,查找文件bear.txt和bean.txt,命令如下所示。

    # ls
    bear.txt  bean.txt   bearer.txt   be.txt  bevy.txt  become.txt
    # ll be{ar,an}.txt
    -rw-r--r--   1 root    root         0  9月 23 17:27 bear.txt
    -rw-r--r--   1 root    root         0  9月 2317:27 bean.txt

例如,下面通过echo命令显示输出多种匹配结果。

    # echo c{oon,op,ud}s
    coons  cops  cuds

大括号也可以嵌套使用,例如:

    # echo b{e{a,c,v}}.txt
    bear.txt  bean.txt   bearer.txt   be.txt  bevy.txt  become.txt

1.3.3 大小写区分

UNIX、Linux都是区分大小写的,对于Shell脚本同样也是区分大小写的。例如,对于Shell脚本中的变量var与Var会被识别为不同的两个变量。

1.3.4 指定使用某个Shell

在Shell脚本文件的第一行通常会放置一行特殊的字符串,告诉操作系统使用哪个Shell来执行这个文件。因为操作系统在试图执行文件之前将检查该程序的起始字符串,这些字符将避免让操作系统进行不必要的失败尝试。

如果脚本的前两个字符是#!,那么系统将这两个字符后面的那些字符作为用来执行该脚本的命令解释器的绝对路径名。该路径可以指定到任何程序的路径名,而并不仅仅局限于Shell。

例如,下面的示例中指定由bash来运行脚本(代码bashtest)。

    1 #!/bin/bash      //指定的是bash脚本
    2 #filename:bashtest
    3 echo  "这是一个bash脚本测试程序。"
    4 echo "-------------------------------"
    5 ps -f

其中,“ps -f”可以用来显示正在执行该脚本的Shell的名字。该脚本执行结果如下所示。

    # chmod u+x bashtest               //赋予文件bashtest可执行权限
    # ./bashtest                       //执行程序bashtest
    这是一个bash脚本测试程序。
    -------------------------------
    UID       PID  PPID   C STIME TTY           TIME CMD
    root     4111  4109   0 21:04 pts/0         00:00:00 bash
    root     4178  4111   0 21:11 pts/0         00:00:00 /bin/bash ./bashtest.sh
    root     4181  4178   0 21:11 pts/0         00:00:00 ps –f
    #

从显示结果可以看到,当前使用的Shell为bash,执行脚本bashtest的Shell也为bash。

如果用户希望使用不同于当前使用的Shell的其他的Shell运行脚本,可以在#!字符后指定使用的其他Shell。例如,下面的示例中使用TC Shell执行脚本(代码tcshscript)。

    1 #!/bin/tcsh
    2 #filename:tcshtest
    3 echo  "这是一个TCsh脚本测试程序。"
    4 echo "-------------------------------"
    5 ps -f

该脚本执行结果如下所示。

    #chmod  u+x  tcshtest              //赋予文件tcshscript可执行权限
    # ./tcshtest                       //执行程序tcshscript
    这是一个TCsh脚本测试程序。
    -------------------------------
    UID       PID  PPID   C   STIME TTY             TIME CMD
    root    3047430472   0   21:06 pts/1           00:00:00-bash
    root    3060130474  30   21:26 pts/1           00:00:00 /bin/tcsh ./tcshtest
    root    3062130601   0   21:26 pts/1           00:00:00 ps -f

从显示结果可以看到,当前正在使用的Shell为bash,执行脚本tcshscript的Shell为TC Shell。因为在脚本中有#!/bin/tcsh一行字符,所以操作系统可以确定由tcsh来执行这个脚本,而不管用户当前正在运行的Shell。

如果没有使用#!指定可执行程序名,将调用当前正在使用的Shell执行脚本。

1.3.5 IO重定向

在Linux系统中,数据流可分为三类:数据输入、数据输出和错误输出。相应地,每一个进程也都有三个特殊的文件描述指针:标准输入(standard input,stdin,文件描述指针为0)、标准输出(standard output,stdout,文件描述指针为1)和标准错误输出(standard error,stderr,文件描述指针为2)。这三个特殊的文件描述指针使进程在通常情况下接收标准输入设备的输入,由标准输出设备显示输出。

标准输入通常指传给一个命令的键盘输入。例如,运行命令ls -al,其中-al是传给ls命令的标准输入。标准输出是一个命令的执行结果,例如运行cat /file1.doc命令后所看到的文件内容就是标准输出。通常标准输出是被定向到显示器的。如果在执行命令的过程中发生错误,可能会存在一条错误消息,这是标准错误数据流,通常也被定向到显示器。例如在上例中,如果file1.doc文件不存在,则系统会提示“cat:/file1.doc:没有此文件或目录”,并从显示器输出。

有两种基本的方法可以用来重定向标准输入(stdin)、标准输出(stdout)和标准错误(stderr):可以利用管道把这些数据流之一输送给另外一条命令,也可以利用I/O重定向把这些数据流之一重定向到一个文件。管道以及I/O重新定向是Linux系统中特有的概念。所谓管道是指将某个命令的输出结果传送到另一个命令,当成另外一个命令的输入,其代表符号是|。所谓I/O重定向是指将命令执行的结果重新导出到其他的设备或文件(以>或>>来表示),或是重新导入到其他的设备或文件(以<或<<来表示)。常用输入输出重定向命令格式与执行结果,如表1-2所示。

表1-2 常用输入输出重定向命令格式说明

1.输入重定向(<)

输出重定向与输入重定向相反,前者是将命令或程序的执行结果通过屏幕或文件输出,但是后者却是将输入设备(如键盘或文件)提供给命令来执行。例如下面的Shell程序实现对/etc目录进行备份,其中使用输入重定向来读入file文件的内容。

    #!/bin/sh
    i=0;
    find / -name etc > file;      //查找/etc的所有文件
    while read LINE               // 读取
    do                            // 执行
      DIRS=$LINE
      BACKUP=”/tmp/backup${i}.tar.gz”
      tar –zcvf  $BACKUP  $DIRS     // 解压
      i=$(($i+1))
    done < file                   // 重定向

2.附加输入重定向(<<字符串)

附加输入重定向经常在电子邮件系统中使用,可以让用户自行定义一个字符串,例如exit,系统在收到此字符串前,会持续地将数据输入文件。下面是一个编写电子邮件的范例,定义的结束字符串是exit,用户可以连续输入邮件内容,直到输入exit。

    # mail long.li@holley.cn << exit
    > This mail is a test.
    > exit

3.错误输出重定向(2>)

输出重定向(>)操作在命令执行发生错误时,将错误信息直接显示到屏幕,并不记录到文件中。而错误输出重定向(2>)会在命令执行发生错误时,将错误信息直接输入到文件中,不会再将信息结果显示在屏幕上。例如:

    root@li-desktop:~# ls
    a.out            Module.symvers   test.c           testmoudule.o         图片
    hello            msg              test.cpp         testtcp.c             文档
    Makefile         nfs.sh           testmoudule.c    workspace.workspace   下载
    message          peter.c          testmoudule.ko   公共的                 音乐
    messagepeter     test1            testmoudule.mod.c模板                  桌面
    modules.order    testblock.o      testmoudule.mod.o视频
    ls: errortest:没有那个文件或目录
    # ls errortest 2> example
    # cat example
    ls: errortest: 没有那个文件或目录

4.标准输出与错误输出重定向(&>)

标准输出与错误输出重定向(&>)可以将标准输出和错误输出信息一并重新定向到文件,屏幕上不会显示任何信息,例如:

    root@li-desktop:~# ls
    a.out            Module.symvers   test.c            testmoudule.o        图片
    hello            msg              test.cpp          testtcp.c            文档
    Makefile         nfs.sh           testmoudule.c     workspace.workspace  下载
    message          peter.c          testmoudule.ko    公共的                音乐
    messagepeter     test1            testmoudule.mod.c模板                  桌面
    modules.order    testblock.o      testmoudule.mod.o视频
    # ls id_testfile
    ls: file: 没有那个文件或目录
    id_testfile
    # ls  id_test file 2> testdoc          //将错误输出重定向到文件testdoc
    id_testfile
    # cat testdoc
    ls: file: 没有那个文件或目录
    # ls  id_testfile &> testdoc           //将标准输出与错误输出一并重定向到文件testdoc
    # cat testdoc
    ls: file: 没有那个文件或目录
    id_testfile

5.输出重定向(>)

一般在Linux下执行任何命令或程序的过程中,默认都是将结果输出到屏幕,但是有时希望将标准输出保存到一个文件中,此时可以使用输出重定向的功能。例如对/etc目录下的所有文件建立一个清单,并保存到根目录的etc_filelist文件中,命令如下所示。

    # ls  /etc  >  /etc_filelist
    # cat  /etc_filelist               //查看建立的列表文件
    acpi
    adduser.conf
    alternatives
    anacrontab
    apm
    apparmor
    apparmor.d
    apport
    apt
    at.deny
    avahi
    bash.bashrc
    bash_completion..
      ….....                           //省略了一部分内容

在执行上例中的ls命令时,由于所有原来会出现的输出内容都已重定向到etc_filelist文件中,屏幕不会再显示任何输出结果。重定向的文件,如本例中的etc_filelist,不需要预先创建,系统在执行此命令时若发现重定向的文件不存在,会自动产生命令中指定的文件。若文件已经存在,则会进行写覆盖。可以使用附加输出重定向(>>)把新添加的数据追加到已存在文件的尾部,来避免写覆盖的发生。

利用前面介绍过的cat命令,配合输出重定向操作,可以将数据直接从键盘录入文件中。例如创建file1.doc文件并录入Hello shell !!!,按Ctrl+C或Ctrl+D组合键可以结束文件的编辑。命令如下所示。

    # cat  >  file1.doc
    Hello shell !!!                    //按回车键
    //按Ctrl+C或Ctrl+D组合键
    #

在输入完毕后先按回车键(否则最后一行不会存储),再按Ctrl+C或Ctrl+D组合键,就可结束文件的编辑,系统会将此内容以file1.doc文件名存储。

例如,利用cat命令与输出重定向,将从标准输入设备的输入,输出到文件temp中保存,执行命令如下所示。

    # cat > temp                       //将标准输入重定向到temp文件中
    Hello, everyone!
    I am bruceli,I like Linux Shell .
    This is a test demo.               //按回车键
    //按Ctrl+D或Ctrl+C组合键结束输入    //Linux中的信号中断
    #
    # cat temp                         //显示temp文件的内容
    Hello, everyone!
    I am bruceli,I like Linux Shell .
    This is a test demo.
    #

注意:Ctrl+C组合键用于结束一个程序,Ctrl+D组合键用于结束终端输入。建议使用Ctrl+D组合键结束终端输入。

6.附加输出重定向(>>)

如果重新定向的文件原来已经存在,那么在使用输出重定向(>)给文件输入新的数据后,新数据将覆盖所有旧的数据。在硬盘空间足够的条件下,为了避免覆盖旧数据,可以采用Linux提供的“附加输出重定向”(>>)。附加输出重定向操作可以将新输入的数据附加(Append)在原有旧数据之后,表示符号为>>。从下例中可以看出输出重定向与附加输出重定向的区别。

    # cat file1.doc
    Hello shell !!!           //源文件内容
    # cat > file1.doc
    This is a new shell !
    # cat file1.doc
    This is a new shell !     //源文件内容被覆盖
    # cat >> file1.doc
    I like this shell.
    # cat file1.doc           //附加在源文件后
    This is a new world!
    I like this shell.

1.3.6 管道(Pipe)

在Linux系统中,管道的主要功能是将其他程序的输出结果直接导出到另一个程序来当成输入数据,即将前一个程序的输出作为后一个程序的输入,管道操作符是|。管道语法格式为:

    命令1  |  命令2  [ |  命令3 ... ]

可以将标准错误输出一起送入管道,命令格式为:

    命令1  |& 命令2  [ |& 命令3 ... ]

注意:管道操作符的左边一定是一条送往标准输出设备的命令,管道操作符的右边一定是一条从标准输入设备读入的命令。

例如,联合使用cat、sort、wc命令,统计文件student中没有重复的行的行数,执行命令如下所示(文件student.exe)。

    # cat test_text
      1 2 hi new learner!
      2 1 hi new learner!
      3 3 hi new learner!
      4 4 hi new learner!
      5 5 hi new learner!          //重复的两行
      6 5 hi new learner!
      7 6 hi new learner!
      8 7 hi new learner!
      9 0 hi new learner!
      10 5 hi new learner!
      11 9 hi new learner!         //读者可以在vim编辑器里用命令行set nu显示行数
      # cat test_text| sort -u | wc -l
      9

可以看到,该命令将cat test_text的输出结果输入给命令sort,由sort命令处理之后再输出给wc命令,最终显示没有重复的行的行数为9。

例如,将ps命令的输出结果作为more命令的输入,以实现分页查看进程信息,命令如下所示。

    #ps  -aux  |  more
    USER     PID  %  CPU  %MEM     VSZ  RSS TTY    STAT START   TIME COMMAND
    root     1  0.0  0.1  2804  1700 ?        Ss   16:47        0:00 /sbin/init
    root     2  0.0  0.0     0     0 ?         S   16:47        0:00 [kthreadd]
    root     3  0.0  0.0     0     0 ?         S   16:47        0:00 [migration/0]
    ……..                     //显示的各个进程的情况包括PID CPU MEM的使用情况//按q键退出

例如,以长格式查看/etc目录下的所有文件。由于/etc目录下的文件有很多,直接使用1s -al命令显示的内容会很快卷过屏幕,无法仔细查看。可以利用管道将1s -al命令的执行结果输入more或less命令,实现分页显示,命令如下所示。

    #ls  -al  /etc  |  more
    总用量 1140
    drwxr-xr-x 129 root   root  122882012-09-23 19:25 .//显示了用户的文件的权限以及用户组
    drwxr-xr-x  24 root   root  40962012-09-22 15:14 ..
    drwxr-xr-x  3 root   root  40962010-08-16 17:48 acpi
    -rw-r--r--  1 root   root  29812010-08-16 17:34 adduser.conf
    drwxr-xr-x  2 root   root  40962012-07-23 21:12 alternatives
    -rw-r--r--  1 root   root  3952010-03-05 10:29 anacrontab
    ……

1.3.7 前台与后台执行任务

一个程序在运行时可以采用两种运行方式,一种是在前台运行,另一种是在后台运行,其主要区别如下所示。

· 前台运行:Shell要等待命令(进程)结束,才恢复自身的运行,即回到操作系统的提示符。即下一命令只有等待当前命令执行完毕才能输入。

· 后台运行:Shell不必等待命令(进程)结束,就恢复自身的运行,即回到操作系统的提示符。用户可以直接输入执行下一命令。

有时用户需要执行的程序要花费很多时间(例如,从打印机输出、远程下载或上传、对源代码进行编译),如果在前台运行,用户将无法继续进行其他工作。为此可将这些耗时程序放到后台运行。在后台执行程序,只需在程序名后添加符号&,格式如下所示。

    root@li-desktop:~# ./a.out &   // 这里介绍一下a.out为linux下的gcc编译程序的默认输出名称 相当于xxx.exe
    [1] 3691
    root@li-desktop:~# Hello world!
    [1]+  Done                ./a.out
    root@li-desktop:~#

例如,在后台执行gcc命令对myprog.c源程序进行编译,执行命令如下所示。

    root@li-desktop:~# gcc test.c&  //gcc后台编译程序
    [1] 3736
    root@li-desktop:~#
    [1]+  Done                gcc test.c

由于后台进程是Shell的一个子进程,所以当用户退出Shell时(即退出登录),后台进程将被自动结束。因此在用户退出系统时,一定要注意是否有还未结束的后台进程正在运行。

注意:所有子进程,在父进程结束时,都将被结束。

1.3.8 命令的执行顺序

当用户交互地向Shell输入多个命令,或者在编写Shell脚本时,必须确定命令的执行顺序。命令执行顺序可分为顺序执行和有条件执行。顺序执行是指多个命令依次执行,前后命令无因果关系。有条件执行是指命令之间存在条件关系,只有满足一定条件才能执行或不被执行。

(1)顺序执行多个命令是指在命令行一条一条地输入命令,命令之间用分号(;)分隔,且分号前后的命令无因果关系。

例如,下面示例将命令echo、pwd和ls组合在一起,并依次顺序执行,如下所示。

    root@li-desktop:~# echo hello shell;pwd;ls
    hello shell           // echo的输出
    /root                 // pwd的输出// ls的输出
    a.out          Module.symvers test.c                testmoudule.o         视频
    hello          msg            test.cpp              testtcp.c             图片
    Makefile       nfs.sh         testmoudule.c         testtext              文档
    message        peter.c        testmoudule.ko        workspace.workspace   下载
    messagepeter   test1          testmoudule.mod.c     公共的                 音乐
    modules.order  testblock.o    testmoudule.mod.o     模板                  桌面

从输出结果可以看到,首先echo命令被执行,显示输出hello shell!,之后执行pwd命令显示当前的工作目录为/root,最后执行ls命令,列出当前目录内容。这行命令的产生结果与下面示例中分别执行这3个命令的结果相同,只是下面示例中每个命令执行完毕均产生一个提示符#,而本示例中仅在执行完3个命令之后才产生提示符。分别执行这3个命令的结果如下所示。

    # echo  hello shell
    Hello shell  //命令echo的输出结果
    # pwd
    /root        //命令pwd的输出结果
    # ls
      a.out         Module.symvers test.c                testmoudule.o         视频
    hello          msg            test.cpp              testtcp.c             图片
    Makefile       nfs.sh         testmoudule.c         testtext              文档
    message        peter.c        testmoudule.ko        workspace.workspace   下载
    messagepeter   test1          testmoudule.mod.c     公共的                 音乐
    modules.order  testblock.o    testmoudule.mod.o     模板                  桌面
    #

注意:分号;也可以看作是一种命令分隔符,但是该分隔符不能立即启动命令执行,也不能改变命令的任何功能。用户可以在单独一个命令行中连续地输入一串命令,并用分号彼此分开。通过在命令行最后输入回车键来执行这一串命令。

(2)有条件执行分为&&和 ||,区别如下所示。

· &&:连接两个命令,前一命令执行成功后才能执行后一个命令,相当于逻辑与。

· ||:连接两个命令,前一命令执行不成功才执行后一个命令,相当于逻辑或。

例如,只有在/home/hellobruce目录存在的情况下,才切换到该目录下。执行命令如下所示。

    [root@localhost bin]# ls /home/hellobruce&& cd /home/hellobruce
    root@li-desktop:/home/hellobruce#

例如,只有在目录/home/hellobruce下不存在文件hibruce时,才将该文件复制到/home/hellobruce目录下,如果目录/home/hellobruce下该文件已经存在,则不再进行复制。利用||可以方便地完成该操作,执行命令如下所示。

    # ls /home/hellobruce/hibruce || cp hibruce /home/hellobruce
    /home/hellobruce/hibruce
    # ls /home/hellobruce
    hibruce

1.3.9 Shell中3种引号的用法

Shell中使用3种引号:双引号、单引号以及反引号。其中双引号对字符串中出现的$、"、`和\进行替换;单引号不进行替换,将字符串作为普通字符输出,而反引号会将字符串作为命令执行,并返回执行的结果。其具体含义如下所示。

· 双引号(“ ”):在双引号中的字符,除了$,",`和\以外的所有字符都解释成字符本身。

· 单引号(' '):在单引号中的所有特殊字符(如$,",`和\)都失去特殊意义,而成为普通字符。

· 反引号(` `):在反引号中的字符被解释成命令,例如`cmd`会被解释成命令cmd执行的结果。

例如,在双引号中输出变量PATH,命令如下所示。

    # echo "$PATH"
    /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/l ocal/arm/4.3.2/bin

可以看到在双引号中,变量PATH被其值替换。例如同样的环境在单引号中引用变量PATH,其结果如下所示。

    # echo '$PATH'
    $PATH

可以看到单引号将$PATH视为普通字符串显示输出。

例如,使用双引号显示输出ps,其结果如下所示。

    # echo "ps"
    ps

例如,使用反引号显示输出ls,其结果如下所示。

    # echo `ls`
    bear.txt  bean.txt   bearer.txt   be.txt  bevy.txt  become.txt

可以看到反引号将ls视为命令,并将ls命令的执行结果返回。例如下面示例中使用双引号,如下所示。

    # echo "I am $USER"       //USER=root
    I am root

可以看到双引号中的$USER被替换。例如下面示例中使用单引号,如下所示。

    # echo 'I am $USER'       //USER只是个字符串了;
    I am $USER

可以看到单引号中的$USER按原样进行输出,没有进行替换。

例如,下面示例中使用反引号获取命令date +%y的执行结果。

    # echo Today \' year is `date +%y`
    Today ' year is 12

1.3.10 运行脚本

运行Shell脚本有3种方法,如下所示。

(1)通过chmod命令把文件的权限设成可读、可执行,然后直接执行该可执行文件,格式如下所示。

    # chmod  u+x  脚本文件名  // chmod 7777(可读可写可执行:最高权限)脚本文件名
    #./脚本文件名及其参数

其中,chmod命令中的选项u表示user,表示该文件的所有者。而u+x表示为文件的所有者添加执行权限。如果其他用户也要执行这个文件,就必须改变该文件的组访问权限(g)或其他用户访问权限(o)。

注意:任何用户希望把文件名作为命令执行时,都必须具有执行权限。如果该文件是一个Shell脚本,用户尝试执行这个文件时,还必须具备读权限。而在执行一个二进制可执行文件(已编译程序)时,不需要读权限。

(2)直接使用Shell的启动命令来执行脚本。例如使用bash命令执行脚本的格式如下所示。

    # bash  脚本文件名及其参数

由于bash命令默认读取一个包含命令的文件,所以不需要为脚本赋予执行权限。如果希望使用TC Shell来执行脚本,格式如下所示。

    #tcsh  脚本文件名及其参数

同样,使用sh命令也可以执行脚本,格式如下所示。

    # sh  脚本文件名及其参数

值得注意的是,用命令sh不能调用原Bourne Shell,因为sh命令实质上是/bin/bash的符号链接文件,其仅仅是bash命令的另一个名称而已。使用ll命令可以查看这种链接关系,如下所示。

    # ll /bin/sh
    lrwxrwxrwx   1 root    root         4  9月 23 21:22 /bin/sh -> bash

在以下示例中分别使用tcsh、bash、sh命令执行脚本tcshtest、bashtest,结果如下所示。

    # tcsh tcshtest
    这是一个TCsh脚本测试程序。
    -------------------------------
    UID       PID  PPID  C STIME TTY        TIME CMD
    root    3047430472  0 20:46 pts/1   00:00:00-bash
    root    3073130474  0 21:00 pts/1   00:00:00 tcsh tcshtest
    root    3075130731  0 21:00 pts/1   00:00:00 ps -f

从显示结果可以看到,当前使用的Shell为bash,执行脚本tcshtest的Shell为tcsh。下面使用命令bash执行脚本bashtest。

    # bash bashtest
    这是一个bash脚本测试程序。
    -------------------------------
    UID       PID  PPID  C STIME TTY        TIME CMD
    root    3047430472  0 20:46 pts/1   00:00:00-bash
    root    3075230474  0 21:07 pts/1   00:00:00 bash bashscript
    root    3075930752  0 21:07 pts/1   00:00:00 ps -f

从显示结果可以看到,当前使用的Shell为bash,执行脚本bashtest的Shell也为bash。下面使用命令sh执行脚本bashtest。

    # sh bashtest
    这是一个bash脚本测试程序。
    -------------------------------
    UID      PID  PPID  C STIME TTY        TIME CMD
    root    3047430472  0 20:46 pts/1   00:00:00-bash
    root    3076030474  0 21:00 pts/1   00:00:00 sh bashtest
    root    3076130760  0 21:00 pts/1   00:00:00 ps -f

从显示结果可以看到,当前使用的Shell为bash,执行脚本bashtest的Shell为sh。

注意:尽管可以使用Shell的启动命令来执行脚本,但该方法将会使脚本的执行速度比具有执行权限之后直接调用脚本慢一些。所以大多数用户更喜欢将文件改为可执行,并在命令行上直接输入文件名来运行脚本。

(3)使用bash内部命令source或.,格式如下所示。

    # source  脚本文件名及其参数
    # .  脚本文件名及其参数

注意:“.”与后面的程序名之间要留有空格。

例如,编写一个简单的Shell程序hello,内容如下所示。

    # cat helloshell
    #Filename:helloshell
    echo "Hello shell! "

采用以上3种方法执行程序helloshell,如下所示。

第1种:将helloshell这个文件的权限设定为可执行。

    #chmod 755 hello      //或  chmod  u+x  helloshell
    #./helloshell
    hello shell !

第2种:直接使用Shell命令sh或bash来执行。

    # sh helloshell
    hello shell!

    # bash helloshell
    hello shell!

第3种:使用bash内部命令source或.。

    #source helloshell
    hello shell!

    # . helloshell
    Hello shell!

注意:一个命令或Shell程序执行完毕之后,将返回给父进程一个退出状态。退出状态是一个0~255之间的整数。通常如果返回状态为0,则表示命令或Shell程序被成功执行。当退出状态非0时,表示命令或Shell程序在某些方面失败了。变量$?中保存了最近一次命令或Shell程序执行。