本文来自于哥们对《LINUX与UNIX Shell编程指南》一书的写写画画,并合成了自己当初一些疑问点所致,仅限学习使用,同时向原作者致敬。 
    原书请见 http://product.china-pub.com/632

    shell
        文件安全与权限
        使用find 和xargs
        后台执行命令
        文件名置换
        shell输入与输出
        命令执行顺序
    文本过滤正则表达式介绍
        grep 家族
        awk 介绍
        sed用法介绍
        合并与分割
        tr用法
    登陆环境
        登陆环境
        环境和shell变量
        引号
    基础shell编程
        shell脚本介绍
        条件测试
        控制流结构
        shell 函数
        向脚本传递参数
        创建屏幕输出
        创建屏幕输入
        调试脚本
        shell嵌入命令
    高级shell编程
        深入讨论<<
        shell工具
        几个脚本例子
        运行级别脚本
        cgi脚本
    常用shell命令

    对于文件属主来说,在只有读权限位被置位的情况下,仍然可以通过文件重定向的方法向该文件写入。??
    能否删除一个文件还依赖于该文件所在目录权限位的设置。
    r--r----- 同组用户权限,一般来说,是文件属主所在的缺省组。
    chmod用户权限位的表示方式:比较短的绝对模式(每一个权限位用一个八进制数来代表 0400)和长一些的符号模式:
        s 文件属主和组set-ID
        t 粘性位
        l 给文件加锁,使其他用户无法访问。
        在列文件或目录时,有时会遇到"t"位。“t”代表了粘性位。如果在一个目录上出现"t"位,这就意味着该目录中的文件只有其属主才可以删除,即使某个同组用户具有和属主同等的权限。不过有的系统在这一规则上并不十分严格。
        如果在文件列表时看到"t",那么这就意味着该脚本或程序在执行时会被放在交换区(虚存)。不过由于当今的内存价格如此之低,大可不必理会文件的"t"的使用。
        $ chmod u+x o-w myfile
        可以通过-R 选项连同子目录下的文件一起设置。

    目录:
        目录的读权限位意味着可以列出其中的内容。写权限位意味着可以在该目录中创建文件,如果不希望其他用户在你的目录中创建文件,可以取消相应的写权限位。执行权限位则意味着搜索和访问该目录。
        如果把同组用户或其他用户针对某一目录的权限设置为--x,那么他们将无法列出该目录中的文件。如果该目录中有一个执行位置位的脚本或程序,只要用户知道它的路径和文件名,仍然可以执行它。用户不能够进入该目录并不妨碍他的执行。
        目录的权限将会覆盖该目录中文件的权限。

    suid/guid(有些系统供应商不允许实现或者忽略这一设置,因为它会带来安全性风险。)
        suid意味着如果某个用户对属于自己的shell脚本设置了这种权限,那么其他用户在执行这一脚本时也会具有其属主的相应权限。于是,如果根用户的某一个脚本设置了这样的权限,那么其他普通用户在执行它的期间也同样具有根用户的权限。同样的原则也适用于guid,执行相应脚本的用户将具有该文件所属用户组中用户的权限。
        有相当一些UNIX命令也设置了suid和guid。如果想找出这些命令,可以进入/bin或者/sbin目录,执行如下的命令:
        $ ls -l | grep '^...s'
        $ ls -l | grep '^...s..s'
        一旦设置了这一位,一个s将会出现在x的位置上。记住:在设置suid或guid的同时,相应的执行权限位必须要被设置。例如,如果希望设置guid,那么必须要让该用户组具有执行权限。
        设置suid命令:$ chmod 4741 logit  $ chmod u+s filename
        在查找设置了suid的文件时,没准会看到具有这样权限的文件:rwSr-xr-x,其中S为大写。它表示相应的执行权限位并未被设置,这是一种没有什么用处的suid设置,可以忽略它的存在。
    
    chown和chgrp:
        在改变一个文件的所有权时,相应的suid也将被清除,这是出于安全性的考虑。只有文件的属主和系统管理员可以改变文件的所有权。
        $ chmod -R -h owner file
        -h 选项意味着在改变符号链接文件的属主时不影响该链接所指向的目标文件。
        如果你希望知道自己属于哪些用户组,可以使用如下命令:$ group   $ id
        为了找出其他用户所属于的组,可以用如下的命令:$ group xxx
    
    umask
        在已经登录之后,可以按照个人的偏好使用umask命令来改变文件创建的缺省权限。相应的改变直到退出该shell或使用另外的umask命令之前一直有效。
        一般来说,umask命令是在/etc/profile文件中设置的,每个用户在登录时都会引用这个文件。所以如果希望改变所有用户的umask,可以在该文件中加入相应的条目。如果希望永久性地设置自己的umask值,那么就把它放在自己$HOME目录下的.profile或.bash_profile文件中。
        对于文件来说,这一数字的最大值分别为6,系统不允许你在创建一个文本文件时就赋予它执行权限,必须在创建后用chmod命令增加这一权限。目录则允许设置执行权限,这样针对目录来说,umask中各个数字最大可以到7.
        $ umask nnn
        umask 值002所对应的文件和目录创建缺省权限分别为664和775。
        如果想知晓当前的umask值,可以使用 $ umask
    
    符号链接:
        软连接实际上就是一个指向文件的指针。
        $ ln [-s] source_path target_path 其中的路径可以是目录也可以是文件。
        不管是否在同一个文件系统中,都可以创建链接。在创建链接的时候,不要忘记在原有目录设置执行权限。链接一旦创建,链接目录将具有权限777或rwxrwxrwx,但是实际的原有文件的权限并未改变。

    小结:
        是否适用设置了suid的脚本完全取决于你自己。如果适用的话,一定要确保能够监控它的使用,而且不要以跟用户身份设置suid。


    有时可能需要在系统中查找某一特征的文件(例如文件权限、文件属主、文件长度、文件类型等)。这样做可能有很多原因。可能出于安全性的考虑,或是一般性的系统管理任务,或许只是为了找出一个不知保存在什么地方的文件。
    即使系统中含有网络文件系统(NFS),find命令在该文件系统中同样有效,只要你具有相应的权限。
    find pathname -options [ -print -exec -ok ]
    -exec相应命令的形式为'command' {} \;  注意{} 和 \; 之间的空格。
    find命令选项:
        -name
        -perm 按照文件权限来查找文件
        -prune 使用这一选项可以使find命令不在当前指定的目录中查找,如果同时使用了-depth选项,那么 -prune选项将被find命令忽略?
        -user 按照文件属主来查找文件
        -group
        -mtime -n +n -n表示文件更改时间距现在n天以内,+n表示文件更改时间距现在n天以前
        -atime
        -ctime
        -nogroup 查找无有效所属组的文件,即该文件所属的组在/etc/groups中不存在
        -nouser 在/etc/passwd中不存在
        -newer file1 !file2 查找更改时间比文件file1新但比文件file2旧的文件
        -type b,d,c,p,l(符号链接),f
        -size n[c] 查找文件长度为n块的文件,带有c时表示文件长度以字节计。
        -depth 在查找文件时,首先查找当前目录中的文件,然后再在其子目录中查找。?
        -fstype 查找位于某一类型文件系统中的文件,这些文件系统类型通常可以在配置文件/etc/fstab中找到,该配置文件中包含了本系统中有关文件系统的信息。
        -mount 在查找文件时不跨越文件系统mount点
        -follow 如果find命令遇到符号链接文件,就跟踪至链接所指向的文件。
        -cpio 对匹配的文件使用cpio命令,将这些文件备份至磁带设备中。
    
    -name 
        使用某种文件模式来匹配文件,记住要用引号将文件名模式引起来。
        $ find ~ -name "*.txt" -print
        在当前目录及子目录中查找:
            $ find . -name "[A-Z]*" -print
            $ find . -name "[a-z][a-z][0-9][0-9].txt" -print
    -perm
        最好使用八进制的权限表示法
        $ find . -perm 755 -print
        $ find . -perm -007 -print 权限前加- 表示包含
    -prune?
        可以使用-prune选项来指出需要忽略目录。在使用-prune选项时要当心,因为如果你同时使用了-depth选项,那么-prune选项将会被find命令忽略
        $ find /apps -name "/apps/bin" -prune -o -print 不进入目录
    -user,-nouser
        $ find ~ -user yali -print
        $ find /etc -user uucp -print
    -group,-nogroup
    其他的:
        $ find . -newer age.awk !-newer belts.awk -exec ls -l {} \;
        touch -t 05042140 dstamp ! 指定更改时间
    -type
        查找初目录以外的所有类型的文件
        $ find . ! -type d -print
    文件系统的大小使用块来计量
        $find . -size +10 -print
    -depth
        $find / -name 'con.file' -depth -print
        首先匹配所有的文件,然后进入子目录查找  (先内部,再目录;默认为先目录,后深入)
    -mount
        $ find . -name '*.xc' -mount -print 不进入其他系统
    
    -cpio
        用来向磁带设备备份文件或从中恢复文件。
        $ find etc home apps -depth -print | cpio -ivcdC65536 -o /dev/rmt0
        之所以使用相对路径,是因为从磁带中恢复这些文件的时候,可以选择恢复文件的路径。
        cpio命令使用了C65536选项,本可以使用B选项,不过这样每块的大小只有512字节,而使用了C65536选项后,块的大小编程了64K字节
    find命令只输出从当前路径起的相对路径和文件名
        $ find logs -type f -mtime +5 -exec rm {} \;
    示例:
        $ find ~ -print
        $ find -type f -perm 4755 -print
        $ find . -type d -print -mount | sort
    
    xargs
        有些系统对能够传递给exec的命令长度有限制,这样在find命令运行几分钟之后就会出现溢出错误。错误信息通常是“参数列太长”或“参数列溢出”。这就是xargs命令的用处所在,特别是与find命令一起使用。
        xargs每次只获取一部分文件而不是全部。
        在有些系统中,使用-exec选项会为处理每一个匹配到的文件而发起一个相应的继承,并非将匹配到的文件全部作为参数一次执行;这样在有些情况下就会出现进程过多,系统性能下降的问题,因而效率不高;而使用xargs命令则只有一个进程。
        每一次获取参数的数目都会根据该命令的选项及系统内核中相应的可调参数来确定。
        内存信息转储文件(core dump)

    对于密集访问磁盘的进程,你可能希望它能够在每天的非负荷高峰时间段运行。
    几种措施:
        1、设置crontab文件、并用它来提交作业。
        2、使用at命令来提交作业
        3、在后台提交作业
        4、使用nohup命令提交作用
    crontab
        系统管理员是通过cron.deny和cron.allow这两个文件来禁止或允许用户拥有自己的contab文件
        当使用crontab运行shell脚本时,要由用户来给出脚本的绝对路径,设置相应的环境变量。
        要保证在shell脚本中提供所有必要的路径和环境变量,除了一些自动设置的全局变量。
        如果cron不能运行相应的脚本,用户将会受到一个邮件说明其中的原因。(没有输出是否也发送通知?)
        crontab -u 用户名 -r 删除crontab文件
        初建:
            编辑$HOME目录下的.profile文件,加入:EDITOR=vi;export EDITOR
            /bin/echo `date` >/dev/console 在有些系统中,用tty1来表示控制台
            crontab davecron 将文件提交给cron进程,新创建文件的一个副本已经被放在/var/spool/cron目录中,文件名就是用户名dave
        crontab -e
            在保存cron文件时,cron会对其进行必要的完整性检查。如果其中的某个域出现了超出允许范围的值,它会提示你。
            最好在每一个条目之上加入一条注释,这样就可以知道它的功能、运行时间,更为重要的是,知道这是哪位用户的作业。
    at命令:
        一旦一个作业被提交,at命令将会保留所有当前的环境变量,包括路径,不象crontab,只提供缺省的环境。该作业的所有输出都将以电子邮件的形式发送给用户,除非你对其输出进行了重定向,绝大多数情况下是重定向到某个文件中。
        通过/etc/目录下的at.allow,at.deny文件来控制哪些用户可以使用at命令,哪些不可以。
        $ at [ -f script ] [ -m -l -r ] [ time ] [ date ]
        -l 列出当前所有等待运行的作业,atg命令具有相同的作用。
        -r 清楚作业。为了清楚某个作业,还要提供相应的作业标识(ID),有些unix变体只接受atrm作为清除命令
        -m 作业完成后给用户发邮件
        time at命令的时间格式非常灵活;可以是H,HH.HHMM,HH:MM或者H:M 还可以使用a.m p.m
        date 日期格式可以是月份数或日期数,还可以识别today tomorrow这样的词
        
        at 两种格式:
            1、命令行
                $ at 21:10
                at> fine / -name "passwd" -print
                at>  (ctrl +D)

                at 6.45am MAY 12
                at 11.10pm
                at now + 1 hour
                at 9am tomorrow
                at 15:00 May 24
                at now + 10 minutes

            2、shell脚本
                $ at 3.00pm tomorrow -f /apps/bin/db_table.sh
                $ echo find /etc -name 'passwd' -print | at now +1 minute
                当提交一个作业后,它就被拷贝到/var/spool/at目录中,准备在要求的时间运行

            3、清除一个作业
                atrm [job no]
                at -r [job no]
        &
            需要用户交互的命令不要放在后台执行,因为这样你的机器就会在那里傻等。
            command > out.file 2>&1 &
            当使用ps命令列出进程时,它无法确定该进程是运行在前台还是后台。
        nohup:
            nohup就是不挂起的意思(no hang up)
            nohup command &
            在缺省情况下该作业的所有输出都被重定向到一个名为nohup.out的文件中,除非另外执行了输出文件:
                nohup command > myout.file 2>&1
                    常规来说,在nohup的情况下,其会自动将报错记录到文件中
                    不加文件的话,在当前执行目录下生成nohuo.out
            $ cat >yali
            xxxxx
            

    shell提供了一套完整的字符串模式匹配规则,或者称之为元字符。还可以使用字符类型来匹配文件名。
    [!...]匹配非
    当shell遇到上述元字符时,就会把他们当做特殊字符,而不是文件名中的普通字符,这样用户就可以用他们来匹配响应的文件名。
    $ ls -al | grep ^d
    $ ls log.[!0-9]*
    
    使用元字符可以大大减少你在查找文件名上的工作量。这是一种非常有效的模式匹配方法,在后面的章节中,我们还将讨论正则表达式时候对文本处理中所涉及到的元字符。

    标准输入:缺省为键盘 或者 指定一个文件作为输入
    echo:
        可以显示文本行或变量,或者把字符串输入到文件。
        echo 命令有许多功能,其中最常用的是下面几个:
            \c 不换行
            \f 进纸
            \t 跳格
            \n 换行
            $ echo "what is your name : \c"
            $ read name
            还可以用echo命令输出转义符以及变量。
            $ echo "\007 your home directory is $HOME, you are connected on `tty`"
                如果是linux系统,必须使用-n选项来禁止echo命令输出后换行。必须使用-e选项才能使转义符生效。
            $ echo "\"/dev/rmt0\""
        read:
            从键盘或文件的某一行文本中读入信息,并将其赋给一个变量。如果只指定了一个变量,那么read将会把所有的输入赋给该变量,直至遇到第一个文件结束符或回车。
            如果给出多个变量,shell将用空格作为变量之间的分隔符。
            如果输入文本域过长,shell将所有的超长部分赋予最后一个变量。
            如果担心用户会对此感到迷惑,而已采用每一个read语句只给一个变量赋值的方法。
        cat:
            cat 是一个简单而通用的命令,可以用它来显示文件内容,创建文件,还可以用它来显示控制字符。在使用cat命令时要注意,它不会在文件分页处停下来;它会一下显示完整个文件。如果希望每次显示一页,可以使用more命令或把cat命令的输出通过管道传递到另外一个具有分页功能的命令中。
            $ cat myfile | pg
            -v 显示控制字符
            $ cat > myfile
            this is great
            

            如果不加文件名称,则会在终端回显这些内容。

            DOS机器拷贝过来的文件都会包含控制符

        管道:
            把一个命令的输出传递给另一个命令作为输入
            $ who | awk '{print $1"\t"$2}' | sed s'/\/dev\///g'
            $ sort myfile | lp 先对一个文件进行排序,然后通过管道输出到打印机。
        
        tee:
            把输出的一个副本输送到标准输出,另一个副本拷贝到响应文件中。
            tee -a files 
                -a表示追加到文件末尾。
            $ find etc usr/local home -depth -print | cpio -ovC65536 -O /dev/rmt/0n | tee -a tape.log
            $ echo "xxxxxxxxx" | tee /dev/console
        标准输入、输出和错误:
            在shell中执行命令的时候,每个进程都和三个打开的文件相联系,并使用文件描述符来引用这些文件。
            系统中实际上有12个文件描述符,0、1、2是标准输入、输出和错误。可以任意使用文件描述符3到9
            command < filename >filename2
                以filename文件作为标准输入,以filename2文件作为标准输出
            command << delimiter
                从标准输入中读入,直至遇到delimiter分界符
            command < &m 把文件描述符m作为标准输入
            command > &m 把标准输出重定向到文件描述符m中
            command < &- 关闭标准输入
        重定向标准输出:
            在使用sort命令的时候(或其他含有相似输入文件参数的命令),重定向符号一定要离开sort命令两个空格,否则该命令会把它当做输入文件。
                $ cat passwd | awk -F: '{print $1}' | sort  1>sort.out
            如果想创建一个长度为0的空文件,可以用
                $ >myfile
        重定向标准输入:
            $ sort < name.txt
            $ cat >>myfile < hello $HOME
            > xxxx
            > MYDAY
        
        exec:
            exec命令可以用来替代当前shell;换句话说,并没有启动子shell。使用这一命令时任何现有环境都将会被清除,并重新启动一个shell。它的一般形式为:
            exec command
            exec命令的一个常见用法就是在用户的.profile最后执行时,用它来执行一些用于增强安全性的脚本。如果用户的输入无效,该shell将被关闭,然后重新回到登陆提示符。exec还常常被用来通过文件描述符打开文件,
            记住,exec在对文件描述符进行操作的时候(也只有在这个时候),它不会覆盖你当前的shell、
        
        使用文件描述符:
            可以使用exec命令通过文件描述符打开和关闭文件。
                exec 4 < &0 0 < stock.txt
                read line1
                read line2
                exec 0 < &4
                echo $line1
                echo $line2

                该脚本第一行把文件描述符4指定为标准输入,然后打开stock.txt文件。接下来两行的作用是读入了两行文本。
                接着作为标准输入的文件描述符4被关闭。最后line1和line2两个变量所含有的内容被回显到屏幕上。
        
        小结:
            这里没有涉及的就是文件描述符的应用(3~9)。要想应用这些文件描述符,就一定会涉及循环方法,在后面讲到循环方法的时候,我们会再次回过头来讲述有关文件描述符的问题。

    1、命令执行顺序
    2、命令组合
    && || 
    shell 提供了在当前shell或子shell中执行一组命令的方法,即使用()和{}
    当前shell中执行一组命令:
        (命令1;命令2;...)
    只有在{}中所有命令的输出作为一个整体被重定向时,其中的命令才被放到子shell中执行,否则在当前shell中执行。
        {命令1;命令2;...}
    
    $ comet month_end || ( echo "Hello, guess what! Comet did not work" | mail dave;exit )
        如果只使用了命令分隔符而没有把它们组合在一起,shell将直接执行最后一个命令exit


    当从一个文件或命令输出中抽取或过滤文本时,可以使用正则表达式(RE),正则表达式是一些特殊或不很特殊的字符串模式的集合。
    这些规则由一些特殊字符或进行模式匹配操作时使用的元字符组成。也可以使用规则字符作为模式中的一部分进行搜寻。
    系统自带的所有大的文本过滤工具在某种模式下都支持正则表达式的使用,并且还包括一些扩展的元字符集。
    .允许匹配ASCII集中任意字符,或为字母,或为数字
    匹配空行 ^$
    下列字符可以认为是特殊字符:
        $.'"*[]^|0\+?
    使用[]匹配特定字符串或者字符串集,可以用逗号将括弧内要匹配的不同字符串分开,但并不强制要求这样做(一些系统提倡在复杂的表达式中使用逗号),这样做可以增加模式的可读性。
    使用\{\}匹配模式结果出现的次数
    [.*0]0之前或之后加任意字符?
    [000*]000或更多个
    '"Device"' 单词device
    [^.*$]匹配任意行
    在shell编程中,一段好的脚本与完美的脚本间的差别之一,就是要熟知正则表达式并学会使用它们。相比较起来,用一个命令抽取一段文本比用三四个命令得出同样的结果要节省许多时间。

    相信grep是UNIX和LINUX中使用最广泛的命令之一。grep(全局正则表达式版本)允许对文本文件进行模式查找。如果找到匹配模式,grep打印包含模式的所有行。
    grep支持基本正则表达式,也支持其扩展集。
    grep有三种变型,即:
        Grep:标准grep命令
        Egrep:扩展grep,支持基本及扩展的正则表达式,但不支持\q模式范围的应用,与之相对应的一些更加规范的模式,这里也不讨论。
        Fgrep:快速grep。允许查找字符串而不是一个模式。不要误解fast,实际上它与grep速度相当。
        实际上应该只有一个grep命令,但不幸的是没有一种简单形式能够统一处理grep的三种变形,将之合而为一,并保持grep单模式处理时的速度。GNU grep 虽然在融合三种变形上迈进了一大步,但仍不能区分元字符的基本集和扩展集。
    grep [选项] 基本正则表达式 [ 文件 ]
    双引号引用:
        在grep命令中输入字符串参数时,最好将其用双引号括起来。这样做有两个原因,一是以防被误解为shell命令,二是可以用来查找多个单词组成的字符串。
        在调用变量时,也应该使用双引号,grep "$MYVAR" 文件名,如果不这样,将没有返回结果。
        在调用模式匹配时,应使用单引号。
    grep选项:
        -c 只输出匹配行的计数
        -i 不区分大小写
        -h 查询多文件时不显示文件名
        -l 查询多文件时只输出包含匹配字符的文件名
        -n 显示匹配行及行号
        -s 不显示不存在或无匹配文本的错误信息
        -v 显示不包含匹配文本的所有行。
    精确匹配:
        $ grep "48" data.f
            表示点击tab键
        使用grep抽取精确匹配的一种更有效方式是在抽取字符串后加 \>
            $ grep '48\>' data.f
    grep和正则表达式:
        使用正则表达式最好用单引号括起来,这样可以防止grep中使用的专有模式与一些shell命令的特殊方式相混淆
    使用grep匹配“与”或者“或”模式
        grep命令加-E参数,这一扩展允许使用扩展模式匹配。
            $ grep -E '219|216' data.f
    查询格式化文件名:
        系统中对文本文件有其标准的命名格式。一般最多六个小写字符,后跟句点,接着是两个大写字符
    类名:
        grep允许使用国际字符模式匹配或匹配模式的类名形式
        [[:upper:]]
        [[:lower:]]
        [[:digit:]]
        [[:alnum:]]
        [[:space:]] 空格或tab键
        [[:alpha:]] = [a-zA-Z]
    大多数系统管理员称/dev/null为比特池(系统池),没关系,可以将之看成一个无底洞,有劲没有出,永远也不会填满。
    $ echo $STR | grep "yali"

    egrep 代表 expression或extended grep,适情况而定。egrep接受所有的正则表达式,egrep的一个显著特性是可以以一个文件作为保存的字符串,然后将之传给egrep作为参数,为此使用-f开关
        $ egrep -f grepstrings data.f
        $ who | egrep -v '^(matty|pauline)'
        $ egrep '(shutdown|reboot)(s)?' *    括号不需要转义

    awk在文本浏览和数据的熟练使用上性能优异
    awk是所有shell过滤工具中最难掌握的
        a、复杂的语法
        b、含义不明确的错误提示信息
    awk是一种自解释的编程语言,之所以要在shell中使用awk是因为awk本身是学习的好例子,但结合awk与其他工具,诸如grep和sed,将会使shell编程更加容易。
    本书仅注重于讲述使用awk执行行操作及怎样从文本文件和字符串中抽取信息
    nawk和gawk,他们扩展了awk的文本特性。
    完整的awk脚本通常用来格式化文本文件中的信息。
    调用awk:
        1、awk [-F field-separator] 'commands' input-file(s)
        2、将所有awk命令插入一个文件,并使awk程序可执行,然后用awk命令解释器作为脚本的首行,以便通过键入脚本名称来调用它。
        3、将所有的awk命令插入一个单独文件,然后调用
            awk -f awk-script-file input-files(s)
    awk脚本:
        awk脚本由各种操作和模式组成。
    模式与动作:        
        模式部分决定动作语句何时触发及触发事件。处理即对数据进行的操作。如果省略模式部分,动作将时刻保持执行状态。
        模式可以是任何条件语句或复合语句或正则表达式。模式包含两个特殊字段,BEGIN和END.使用BEGIN语句设置计数和打印头,END打印输出文本总数和结尾状态标志。如果不特别指明模式,awk总是匹配或打印行数。
        实际动作在大括号{}内指明,动作大多用来打印,但是还有些更长的代码诸如if和循环语句及循环退出结构。如果不指明采取动作,awk将打印出所有浏览出来的记录。
    使用标准输入:
        先对awk脚本的输入方法简要减少一下。实际上任何脚本都是从标准输入中接受输入的。例如:
            a、awk脚本 $ belts.awk grade_student.txt
            b、重定向:$ belts.awk < grade2.txt
            c、管道方法 grage2.txt | belts.awk
    打印单独记录:
        不要忘了加逗号以分割域
            $ awk '{print $1,$4}' grade.txt
    awk错误信息提示:
        awk将试图打印错误行,但由于大部分命令都只在一行,因此帮助不大。
        在碰到awk错误时,可相应查找:
            a、确保整个awk命令用单引号括起来
            b、确保命令内所有引号成对出现
            c、确保用花括号括起动作语句,用圆括号括起条件语句。
            d、可能忘记使用花括号,也许你认为没有必要,但awk不这样认为,将按之解释语法。
    awk条件操作:/Green/
    元字符:
        awk中正则表达式匹配操作中经常用到的字符:\^$.[]|()*+?
        这里有两个字符仅适用于awk而不适用于grep或sed
            + ?
    条件操作符:
        < <= == != >= ~ !~
    模式匹配:
        {if($4~/brown/)print}
        匹配记录找到时,如果不特别声明,awk缺省打印整条记录
    精确匹配:
        为精确匹配48,使用等号==,并用单引号括起条件,例如$3 == "48"
        $ awk '$3=="48"{print $0}' grade.txt
    设置大小写:
        为查询大小写信息,可使用[]符号 '/[Gg]reen/'
    任意字符:
        使用.句点 '/^...a/'
    或关系匹配:
        使用竖线时,语句必须用圆括号括起来
        $ awk '$0~/(Yellow|Brown)/' grade.txt
    复合模式或者复合操作符用于形成复杂的逻辑操作,复杂程度取决于编程者本人。有必要了解的是,复合表达式即为模式间通过使用下述各表达式互相结合起来的表达式:&& || ! 
        $awk '{if($1=="P" && $4=="Yellow")print $0}' grade.txt
    awk内置变量:
        awk有许多内置变量用来设置环境信息,这些变量可以被改变。
    ARGC 命令行参数个数
    ARGV 命令行参数排列: ARGV[n] 0为awk本身,1,2标识后面引用的文件位置
    ENVIRON 支持队列中系统环境变量的使用 ENVIRON["EDITOR"]="Vi"
    FILENAME awk浏览的文件名
    FNR 浏览文件的记录数
    FS 设置输入域分隔符,等价于命令行-F选项
    NF 浏览记录的域个数
    NR 已读的记录数
    OFS 输出域分隔符,缺省为空格
    ORS 输出记录分隔符
    RS 控制记录分隔符,缺省为新行 \n
        $ echo $PWD | awk -F/ '{print $NF}'
    awk操作符
        基本表达式可以划分为数字型、字符串型、变量性、域及数组元素
            赋值操作符 = += *= /= %= ^=
            条件表达操作符
            并、与、非 || && !
            匹配操作符,包括匹配和不匹配 ~ !~
            关系操作符 < <= == != >=
            算术操作符 + - * / % ^
            前缀和后缀 ++ --
    设置输入域到域变量名
        $ awk '{name=$1;belts=$4;if(belts ~/Yellow/)print "name is belt"belts}' grade.txt
    修改数值域取值:
        要记住实际输入文件是不可修改的,修改的只是保存在缓存里的awk副本。
        $ awk '{if($1 == "M.Tansley")$6=$6-1;print $1,$6,$7}' grade.txt
    修改文本域:
        记住字符串要使用双引号"",并用圆括号括起整个语法
            $ awk '{if($1=="J.Troll")($1="J.L.Troll");print $1}' grade.txt
    只显示修改记录:
        $ awk '{if($1=="J.Troll"){$1="J.L.Troll";print $1}}' grade.txt
    创建新的输出域:
        '{$8=$7-$6}' 新生成的$8默认会写到标准输出
    增加列值:
        $ awk '(tot+=6);END{print "Club student total points :" tot}' grade.txt
        $ awk '{(tot+=$6)};END{print "Club student total points :" tot}'
        $ ls -l | awk '/^[^d]/{print $9"\t"$5}{tot+=$5}END{PRINT "total KB:"TOT}'
    内置的字符串函数:
        gsub(r,s) 在整个$0中用s替代r
        gsub(r,s,t) 在整个t中用s替代r
        index(s,t) 返回s中字符串t的第一位置
        length(s) 返回s长度
        match(s,r) 测试s是否包含匹配r的字符串,返回字符排列数,没有返回0
        split(s,a,fs) 在fs上将s分成序列a,返回字符串数组元素格式,下标从1开始。
        sprint(fmt,exp) 返回经fmt格式化后的exp
        sub(r,s) 用$0最左边最长的字串代替s,该字串被r匹配,只替换匹配的第一次出现
        substr(s,p) 返回字符串s中从p开始的后缀部分
        substr(s,p,n) 返回字符串s中从p开始长度为n的后缀部分,如果给定长度远大约字符串长度,awk将从起始位置返回所有字符。
            $ STR="mydoc.txt"
            $ echo $STR | awk '{print substr($STR,7)}'
    字符串屏蔽序列:
        awk 使用的屏蔽序列
            \b 退格键
            \f 走纸换页
            \n 新行
            \r 回车键
            \t tab键
            \ddd 八进制值
            \c 任意其他特殊字符,例如\\为反斜线符号
            $ awk 'BEGIN{print "\nMay\nMay \104\141\171"}'
                \104为D的八进制ASCII码,\141为a的八进制ASCII码,等等
    awk printf修饰符
        - 左对齐
        Width 域的步长,用0表示0步长
        .prec 最大字符串长度,或小数点右边的位数
    awk printf格式
        %c ASCII 字符
        %d 整数
        %e 浮点数,科学计数法
        %f 浮点数,例如(123.44)
        %g awk决定使用哪种浮点数转换e或者f
        %o 八进制数
        %s 字符串
        %x 十六进制
        $ echo "65" | awk '{printf "%c\n",$0}' 缺省情况下printf不做换行动作
        浮点数转换
            $ awk 'BEGIN{printf "%f\n",999}'
                999.000000 转换后被加了六个小数点
        格式化输出
            $ awk '{printf "%-15s %s\n",$1,$3}' grade.txt
    向一行awk命令传值
        $ awk '{if($5 < AGE)print $0}' AGE=10 grade.txt
        传入环境变量:
            user=$LOGNAME
    awk脚本文件:
        好处:命令长、不必反复输入、可以增加注释
            给所有awk程序假如awk扩展名是一种好习惯
            #!/bin/awk -f
            next 的使用?
        数组使用前,不必定义,也不必指定数组元素个数。经常使用循环来访问数组。
            For (element in array) print array[element]
            $ arraytest.awk /dev/null
        在打印语句末尾有一个\符号,用来通知awk(或相关脚本)命令持续到下一行,当输入一个很长的命令,并且想分行输入时可以使用这种方法。

    sed是一个非交互性文本流编辑器。它编辑文件或标准输入导出的文本拷贝。标准输入可能是来自键盘、文件重定向、字符串或变量,或者是一个管道的文本。
    sed一次性处理所有改变,因而变得很有效,对用户来讲,最重要的是节省了时间。
    内容包含:
        抽取域
        匹配正则表达式
        比较域
        增加、附加、替换
        基本的sed命令和一行脚本
    无论命令是什么,sed并不与初始化文件打交道,它操作的只是一个拷贝,然后所有的改动如果没有重定向到一个文件,将输出到屏幕。
    因为sed是一个非交互性编辑器,必须通过行号或正则表达式制定要改变的文本行。
    sed怎样读取数据:
        sed从文件的一个文本行或从标准输入的几种格式中读取数据,将之拷贝到一个编辑缓冲区,然后读命令行或脚本的第一条命令,并使用这些命令查找模式或定位行号编辑它。重复此过程直到命令结束。
    调用sed
        调用sed有三种方式:
            在命令行键入命令
            将sed命令插入脚本文件,然后调用sed
            将sed命令插入脚本文件,并使sed脚本可执行。
            sed [选项] sed命令 输入文件
        不管使用shell命令行方式或脚本文件形式,如果没有指定输入文件,sed从标准输入中接收输入,一般是键盘或重定向结果。
        sed选项:
            n 不打印,p命令打印编辑行
            c 下一命令是编辑命令,使用多项编辑时加入此选项
            f 调用sed脚本文件使用
    使用sed在文件中查询文本的方式:
        有两种方式定位文本:
            1、使用行号或行号范围
            2、使用正则表达式
                x x为一行号
                x,y 行范围
                /pattern/
                /pattern/pattern/ 查询包含两个模式的行
                /pattern/,x 在给定行号上查询包含模式的行
                x,/parttern/ 通过行号和模式查询匹配行 从第一个匹配到第二个匹配
                x,y 查询不包含制定行号x和y的行?
    sed 编辑命令
        p 打印匹配行
        = 显示文件行号
        a\ 在定位行号后附加新文本信息
        i\ 在定位行号后插入新文本信息
        d 删除定位行
        c\ 用新文本替换定位文本
        s 使用替换模式替换相应模式
        r 从另一个文件中读文本
        w 写文本到一个文件
        q 第一个模式匹配完成后退出或立即退出
        l 显示与八进制ASCII代码等价的控制字符 显示每行数据的控制符
        {}在定位行执行的命令组
        n 从另一个文件中读文本下一行,并附加在下一行
        g s/ */,/g
        y y/abc/ABC/将把所有小写的a转换成A
        n 延续到下一输入行;允许跨行的模式匹配语句
    $ sed -n '2p' quote.txt
    $ sed -n '/Neave/'p quote.txt
    $ sed -n '4,/The/'p quote.txt 只在第四行匹配The
    $ sed -n '/\$/'p quote.txt 匹配元字符
    $ sed -n '1,$p' quote.txt $即最后一行
    $ sed -n '/.*ing/'p quote.txt
    $ sed -n -e '/pattern/p' -e '/pattern/=' 打印行号
    如果不指定文本放置文职,sed缺省放在每一行后面。附加文本时不能指定范围,只允许一个地址模式。
    地址指定一个模式或行号,定位新文本附件位置,斜划线代表换行。如果不加,sed假定这是附加命令结尾。
        #! /bin/sed -f
    修改命令将在匹配模式空间的指定行用新文本加以替代。
        可以对统一脚本中的相同文件进行修改、附加、插入三种动作匹配和混合操作。
    删除文本:地址可以是行的范围或模式。
    替换文本:
        替换命令用替换模式替换指定模式
            [address[,address]] s/pattern-to-find/replacement-pattern/[g p w n]
            g 缺省情况下只替换第一次出现模式,使用g替换全局
            p 输出
            w 文件名 输出定向到文件
                $sed 's/splendid/SPLENDID/w sed.out' quote.txt
        使用替换修改字符串
            如果要附加或修改一个字符串,可以使用 & 命令,& 命令保存发现模式以便重新调用它,然后把它放在替换字符串里面
                $ sed -n 's/played/from hock &/p' quote.txt
    从文本中读文本:
        sed允许从另一个文件中读文本,并将其文本附加在当前文件,注意所读的文件名需要用单引号括起来
            $ sed '/company./r sedex.txt' quote.txt
    匹配后退出:
        在模式匹配首次出现后退出sed,以便执行其他处理脚本。
            $ sed '/.a.*/q' quote.txt
    显示文件中的控制字符(非打印字符):
        cat -v filename 即可识别
        sed 格式:
            [address,[address]]l
                l即为列表
                \033代表退格键,OP为F1键值
                各系统控制字符键值可能不同,主要取决于其映射方式(例如 使用 terminfo 或 terncap)
                    在文本文件中插入控制字符F1键
                        vi
                        ctrl + v
                        F1 (显示OP)
                        esc
                产生控制字符^M
                    ctrl+v
                    释放v,按住^键
                    释放两个键,按键
            如果使用sed对文件进行过滤,最好将问题分成几步,分步执行,且边执行边测试结果。经验告诉我们,这是执行一个复杂任务的最有效方式。
    高级编辑命令:
        由于各种各样的原因,比如用户希望在某个条件下脚本中的某个命令被执行,或者希望模式空间得到保留以便下一次的处理,都有可能使得sed在处理文件的时候不按照正常的流程来进行。这个时候,sed设置了一些高级命令来满足用户的要求。
            sed命令:
            + g:[address[,address]]g 将hold space中的内容拷贝到pattern space中,原来pattern space里的内容清除
            + G:[address[,address]]G 将hold space中的内容append到pattern space\n后
            + h:[address[,address]]h 将pattern space中的内容拷贝到hold space中,原来的hold space里的内容被清除
            + H:[address[,address]]H 将pattern space中的内容append到hold space\n后
            + d:[address[,address]]d 删除pattern中的所有行,并读入下一新行到pattern中
            + D:[address[,address]]D 删除multiline pattern中的第一行,不读入下一行
            PS:不论是使用G、g还是H、h,它们都是将hold space里面的内容“copy”到pattern space中或者将pattern space中的内容“copy”到hold space中。
	有几种工具用来处理文本文件分类、合并和分割操作:
        sort
        uniq
        cut
        paste
        split
    sort
        实际上,在使用其他unix工具时,已假定工作文件已经被分过类。无论如何,分类文件比不分类文件看起来更有意义。
        sort选项:
            sort -cmu -o output_file [other options] +pos1 +pos2 input_files
            -c 测试文件是否已经分类
            -m 合并两个分类文件
            -u 删除多有复制行
            -o 存储sort结果的输出文件名

            -b 使用域进行分类时,忽略第一个空格
            -n 指定分类是域上的数字分类
            -t 域分隔符;用非空格或tab键分隔域??
            -r 对分类次序或比较求逆
            +n n为域号,使用此域号开始分类
            n n为域号,在分类比较时忽略此域,一般与+n一起使用
            post1 传递到m,n。m为域号,n为开始分类字符数;例如 4,6即以第5域分类,从第7个字符开始
        缺省情况下,sort认为一个空格或一系列空格为分隔符,要假如其他方式分隔,使用-t选项
        缺省时sort将整个行排序,指定域号的情况例外
            $ sort -t: +1 video.txt
            $ sort -t: +3n video,txt
            $ sort -t: -k4 video.txt 从1开始
            $ sort -t: -k4 -k1 video.txt
            使用-n选项指定不适用哪个分类键进行查询
                sort +0 -2 +3 以域0分类,忽略域2,然后再使用域3分类
            $ sort -t: +1.2 video.txt
        如果使用head或者tail时想省略显示行数,缺省时显示10行
        将两个分类文件合并:
            将文本合并前,他们必须已被分类。合并文件可用于事务处理和任何种类的修改操作
                $ sort -m sorted_file1 sorted_file2
    uniq用法:
        uniq用来从一个文本文件中去除或禁止重复行。一般uniq假定文件已分类,并且结果正确。
        sort的唯一性选项去除所有的重复行,而uniq命令并不是这样做。重复行是什么?在uniq里即持续不断重复出现的行,中间不夹杂任何其他文本。
        uniq -u d c -f input-file output-file
            -u 只显示不重复行
            -d 只显示有重复数据行,每种重复行只显示其中一行
            -c 打印每一重复行出现次数
            -f n为数字,前n个域被忽略
            一些系统不识别-f选项,这时替代使用-n 从1开始
    join用法:
        join 用来将来自两个分类文本文件的行连在一起
        这里有点像修改一个主文件,使之包含两个文件里的共同元素。
        文本文件中的域通常由空格或tab键分隔,一些系统要求使用join时文件域要少于20,为公平起见,如果域大于20,应使用DBMS系统。
        join [options] input-file1 input-file2
            选项列表:
                an n为一数字,用于连接时从文件n中显示不匹配行。
                o n.m n为文件号,m为域号。1.3表示只显示文件1第三个域,每个n,m必须用逗号分隔
                j n m n为文件号,m为域号。使用其他域做连接域
                t 域分隔符。
                缺省join删除或去除连接键的第二次重复出现,这里即为名字域
            $join -j1 4 -j 2 2 per pers2
    cut用法
        cut用来从标准输入或文本文件中剪切列或域。剪切文本可以将之粘贴到一个文本文件。
        cut [options] file1 file2
            -c list 指定剪切字符数
            -f field 指定剪切域数
            -d 指定与空格和tab键不同的域分隔符
            -c 用来指定剪切范围
                -c1,5-7 剪切第一个字符,然后是第5~7个字符
                -c1-50 剪切前50个字符
            -f 格式与-c相同
                -f1,5 剪切第1域,第5域
                -f1,10-12
            使用-c选项指定精确剪切数目。这种方法需要确切知道开始以及结束字符,使用在固定长度的域或文件名上
    paste用法:
        粘贴两个不同来源的数据时,首先需要将其分类,并确保两个文件函数相同。
        paste将按行将不同问津啊行信息放在一行。缺省情况下,paste连接时,用空格或tab键分隔新行中不同文本,除非指定-d选项,它将成为域分隔符
        paste -d -s file1 file2
            -s 将每个文件合并成行而不是按行粘贴?
            - 使用标准输入,例如ls | paste 即只在一列上显示输出
        paste命令管道输入:
            paste命令还有一个很有用的选项-,即对每一个-,从标准输入读一次数据,使用空格做域分隔符,以一个4列格式显示
                $ls | paste -d" " - - - -
                xx xx xx xx
                xx xx
    split用法:
        split用来将大文件分隔成小文件
        split -output_file-size input-filename output-filename
            每个文件格式为x[aa]~x[zz]
 
    tr用来从标准输入中通过替换或删除操作进行字符转换。
    tr刚执行时,字符串1中的字符被映射到字符串2中的字符,然后转换操作开始。
    内容:
        大小写转换
        去除控制字符
        删除空行
    tr -c -d -s ["string1_to_translate_from"] ["string2_to_translate_to"] file
        -c 用字符串1中字符集的补集替换此字符集,要求字符集为ASCII
        -d 删除字符串1中的所有输入字符
        -s 删除所有重复出现字符序列,只保留第一个;即将重复出现字符串压缩为一个字符串。
    字符范围:
        使用tr时,可以指定字符串列表或范围作为形成字符串的模式。指定字符串1或字符串2内容时,只能使用单字符或字符串范围或列表。
            [a-z]
            [A-Z]
            [0-9]
            /octal 一个三位的八进制数,对应有效的ASCII字符
            [O*n]表示字符O重复出现指定次数n
        大部分tr变种支持字符类和速记控制字符。字符类格式为[:class],包含数字、希腊字母、空行、小写、大些、cntrl键、空格、点记符、图形等
            tr中特定控制字符的不同表达方式:
                速记符、含义、八进制方式
                \a Ctrl-G铃声 \007
                \b Ctrl-H退格符 \010
                \f Ctrl-L走行换页 \014
                \n Ctrl-J新行 \012
                \r Ctrl-M回车 \015
                \t Ctrl-I tab键 \011
                \v Ctrl-X \030
        $ tr -s "[a-z]" < oops.txt
        $ cat oops.txt | tr -s "[a-z]" 去除重复字符
    删除空行:
        $ tr -s "[\012]" < plane.txt
        $ tr -s ["\n"] < plane.txt
    大写到小写:
        $ tr "[a-z]" "[A-Z]"
        $ tr "[:lower:]" "[:upper:]"
    删除指定字符:
        需要结合使用-c和-s选项完成此功能
        $ tr -cs "[a-z][A-Z]" "[\012*]" < diary.txt
    转换控制字符:
        tr的第一个功能就是转换控制字符,特别是从dos向UNIX下载文件时,忘记设置ftp关于回车换行转换的选项时更是如此。
        tr -s "[\015\032]" "\n" < stat.tmp
            ^八进制为136,^M为015,tab键为011 ^Z为032
    $ tr "[0*4]" "*" < hdisk.txt 替换字符串为*号
    tr主要用于字符转换或者抽取控制字符。本章所有功能都可以用sed来完成,但有些人宁愿使用tr,因为tr更加快捷、容易。

    登陆系统时,在进入命令提示符前,系统要做两个工作。键入用户名和密码后,系统检查是否为有效用户,为此需查询/et/passwd文件。如果登陆名正确并且密码有效,开始下一步过程,即登陆环境。
    内容范围:
        登陆过程
        文件/etc/passwd
        $HOME.profile
        定制 $HOME.profile
    登陆成功后,系统执行两个环境设置文件,第一个是/etc/profile,第二个是.profile,位于用户跟目录下。
    系统还会处理其他的初始化文件,这里只涉及profile文件
    /etc/profile
        此文件包含:
            全局或局部环境变量
            PATH信息
            终端设置
            安全命令
            日期信息或放弃操作信息
        设置全局环境变量便于用户及其进程和应用访问它,PATH定位包含可执行文件,库文件及一般文本文件的目录位置,便于用户快速访问。
        终端设置使系统获知用户终端的一般特性。
        安全命令包含文件创建模式或敏感区域的双登陆提示?。
        日期信息是一个文本文件,保存用户登陆时即将发生事件的记录或放弃登陆的信息文件。
    用户的$HOME.profile
        一般来说创建账户时,一个profile文件的基本框架即随之创建。不要忘了在.profile文件中可以通过设置相关条目以不同的值或使用uset命令来覆盖/etc/profile文件中的设置。如果愿意,可以定制用户自己的.profile文件。
        $ PS1="`hostname`>"
        设置辅助命令提示符
            $PS2="`echo "\251"`:"  @ ASCII代码八进制数
    stty用法:
        stty用于设置终端特性。stty -a
        设置终端时遇到的一个最普遍的问题时退格键不起作用。本机stty命令中^?即为退格键,使用可能会退格并删除前一个字符
        stty name character
        $ stty erase '\^H'
        在vim下,Ctrl+V,释放V键,再按下H键
        常用stty命令如下:
            intr ^C 终止进程
            echo     打开echo功能
            -echo    关闭echo功能?
            eof  ^D  文件尾;注销
            kill ^Y  删除一行
            start ^Q 滚动屏幕文本
            stop ^S  停止滚动屏幕文本
        stty -g 此选项允许以可读格式保存stty现有设置,便于以后重置回stty
        如果linux中使用转义字符,使用 echo -e
        stty命令可以与终端、打印机、调制解调器打交道,功能十分丰富。使用stty时要慎重,不要使用已经使用的键或无效值。
    创建.logout文件:
        使用Bourne shell 与其他shell不同,其确定是不包含.logout文件。此文件保存有执行exit命令时,在进程终止前执行的命令。
        但是通过使用trap命令,Bxx shell 也可以创建自己的.logout文件。
        trap "$HOME/.logout" 0

    shell变量可以保存诸如路径名、文件名或者一个数字这样的变量名。shell将其中任何设置都看做文本字符串。
    两种变量:本地和环境
    内容涵盖:
        shell变量
        环境变量
        变量替换
        导出变量
        特定变量
        向脚本传递信息
        在系统命令行下使用位置参数
     使用变量时,如果用花括号将之括起来,可以防止shell误解变量值。
     $yali=111 ${yali=111}
     变量设置时的不同模式:
        xxx=value
        xxx+value 如果设置了xxx,则重设其值?
        xxx:?value 如果未设置xxx,显示未定义用户错误信息??
        xxx?value  如果未设置xxx,显示系统错误信息??
        xxx:=value 如果未设置xxx,设置其值
        xxx:-value 同上,但是取值并不设置到xxx,可以被替换        
        echo $yali ;echo ${yali}
        unset yali
    使用set命令显示所有本地定义的shell变量
    将变量并排可以使变量结合在一起
        $echo  ${FIRST}${SURNAME}
    ${var:-value} 如果设置了变量值,则使用它,否则取新值
    设置只读变量
        $ xxx=value
        $ readonly xxx
    环境变量:
        环境变量用于所有用户进程(经常为子进程)。登陆进程成为父进程。shell中执行的用户进程均称为子进程。
        环境变量可以在命令行中设置,但用户注销时即丢失,因此最好在.profile文件中定义
            xxx=value; export xxx;
        使用env即可查看所有的环境变量
    嵌入shell变量
        brourneshell中预留的环境变量名,这些变量名不能用作其他用途。通常在/etc/profile中建立这些嵌入的环境变量,但也不完全是,这取决于用户自己。
        CDPATH
            $CDPATH=:/home/dave/bin:/usr/local/bin;export CDPATH
        EXIINIT
            EXINIT变量保存使用vi编辑器时的初始化选项。例如,使用vi时,要显示行号,且在第10个空格加入tab键,命令为:
            $ EXINIT='set nu tab=10';export EXINIT;
        HOME
        IFS
            IFS用作shell指定的缺省域分隔符。原理上将域分隔符可以是任意字符,但缺省通常为空格、新行或tab键。IFS在分隔文件或变量中各域时很有用。
            $ export IFS=:
            $ echo $PATH
            /sbin /bin /usr/sbin /usr/bin /root/bin
            要设置其返回初始设置:
                $ ifs=;export IFS;
        LOGNAME
        MAIL:缺省为/var/spool/mail/
        MAILCHECK:缺省每60s检查新邮件
        MAILPATH:
            如果有多个邮箱要用到MAILPATH,此变量设置将覆盖MAIL设置
            $MAILPATH=/var/spool/dave:/var/spoll/admin;export MAILPATH
        PATH
            $ PATH=$PATH:/$HOME/BIN; export PATH;
        PS1
            基本提示符包含shell提示符,缺省对超级用户是#,其他为$
        PS2
            附属提示符,缺省为符号>
        SHELL
            保存缺省shell
        TERMINFO
            终端初始化变量保存终端配置文件的位置,通常在/usr/lib/terminfo或/usr/share/terminfo
        TERM
            TERM变量保存终端类型。设置TERM使应用获知终端对屏幕和键盘响应的控制序列类型,常用的用vt100,vt200,vt220-8
        TZ
            时区变量保存时区值,只有系统管理员才可以更改此设置。
            $echo $TZ
                GMT2EDT
                     返回值表明正在使用格林威治标准时间,与GMT时差为0,并作EDT保存。
     其他环境变量:
        还有一些预留的环境变量。其他系统或命令行应用将用到他们。以下是最常用的一些,注意这些值均未有缺省设置,必须显示说明。
        EDITOR
        PWD
        PAGER
            保存屏幕翻页命令,如pg、more,在查看man文本时用到此功能。
        MANPATH
            保存系统上man文本的目录
        LPDEST PRINTER
            保存缺省打印机名,用于打印作业时指定打印机名
                $ LPDEST=hp3si-systems
    set -a 指明所有变量直接被导出
    将变量导出到子进程:
        不可以将变量从子进程导出到父进程,然而通过重定向就可做到这一点。
    位置变量参数:
        四种变量:
            本地
            环境
            还有两种变量被称为是特殊变量,因为它们是只读的。这两种变量即为位置变量和特定变量参数
            参数数目可以任意多,但只有前9个可以被访问,使用shift命令可以改变这个限制
    特定变量参数:
        脚本运行时的一些相关控制信息,这就是特定变量的由来,共7个
            $# 传递到脚本的参数个数
            $* 以一个单字符串显示所有向脚本传递的参数。与位置变量不同,此选项参数可以超过9个。
            $$ 脚本运行的当前进程ID号
            $!后台运行的最后一个进程的进程ID号?
            $@ 与$#相同,但使用时加引号,并在引号中返回每个参数?
            $- 显示shell使用的当前选项,与set命令功能相同?
            $? 显示最后命令的退出状态。
        特定变量增强了脚本的功能并提供了传递到脚本的参数的更多信息。
    在脚本中执行变量替换时最容易犯的一个错误就是由于引用错误。
    内容范围:
        引用的必要性
        双引、单引和反引号
        使用反斜线实现屏蔽
    脚本中执行行操作时,shell将对脚本设置予以解释。要采取一种方法防止shell这样做,即使用引用号,包括各式引用或使用反斜线。
    shell应用类型:
        ""双引号
        ''单引号
        ``反引号
        \反斜线
    使用双引号可引用除字符$、`、\外的任意字符或字符串
    单引号会忽略任何引用值
    反引号用于设置系统命令的输出到变量。
        $ date +%A" the "%e" of "%B" "%Y
    反斜线:
        如果下一个字符有特殊含义,反斜线防止shell误解其含义,即屏蔽其特殊含义。即:& * + ^ $ ` " | ?
        $echo $$
        $echo \$$
        $echo "This is a copyright \251 sign"
        $expr 12 \* 12
        在echo命令中假如元字符,必须用反斜线起屏蔽作用,例如\$19.99

    内容范围:
        使用shell脚本的原因
        shell脚本基本元素
        shell脚本运行方式
    原因:
        shell脚本在处理自动循环或大的任务方面可节省大量的时间,且功能强大。
        创建一个脚本,在使用一系列系统命令的同时,可以使用变量、条件、算术和循环快速创建脚本以完成相应工作。
        shell的可迁移性不成问题,但是系统间命令的可迁移性存在差别。
        如果写一段脚本,其执行结果与预想的不同,不必着急。无论多不可思议的结果,记住先把它保存起来,这是修改的基础。这里要说的意思是不要害怕对待新事物,否则将不能树立信息,学起来会更加困难。
    脚本:
        #!/bin/sh
        # name:cleanup
        # this is a general cleanup script
        xxxx

    写脚本时,有时要判断字符串是否相等,可能还要检查文件状态或是数字测试。基于这些测试才能做进一步动作。
    Test命令用于测试字符串,文件状态和数字。if then else
    内容范围:
        对文件、字符串和数字使用test命令
        对数字和字符串使用expr命令
     expr命令测试和执行数值输出。使用最后退出状态命令$?可测知test和expr,二者均以0表示正确,1表示返回错误。
     测试文件状态:
        test condition
        [condition]
        使用方括号时,要注意在条件两边加上空格。
            文件状态测试:
                -d 目录
                -f 正规文件
                -L 符号链接
                -r 可读
                -s 文件长度大于0、非空
                -w 可写
                -u 文件有suid位设置
                -x 可执行
            $ [ -w scores.txt ]
            $ test -w scores.txt
        测试时使用逻辑操作符:
            -a 逻辑与
            -o 逻辑或
            !  逻辑否
            $ [ -w results.txt -a -w scores.txt ]
        字符串测试:
            字符串测试时错误捕获很重要的一部分,特别在测试用户输入或比较变量时尤为重要。
            字符串测试有5种格式:
                test "string"
                test string_operator "string"
                test "string" string_operator "string"
                [ string_operator string ]
                [ string string_operator string ]
                    string_operator
                        = 相等
                        != 不等
                        -z 空串
                        -n 非空串
            没有规定在设置变量时一定要用双引号,但在进行字符串比较时必须这样做。
                $ [ "$TYPE" != "$TYPE2" ]
        测试数值:
            [ "number" numberic_operator "number" ]
                operator:
                    -eq
                    -ne
                    -gt
                    -lt
                    -le
                    -ge
                $ [ "$NUMBER" -eq "130" ]
                $ [ "990" -le "995" -a "123" -gt "33" ]
        expr用法:
            expr命令一般用于整数值,但也可用于字符串
            expr argument operator argument
                expr 10 + 10
                expr 10 \* 10
            数值测试:
                可以用expr测试一个数,如果试图计算非整数,将返回错误。
                $ expr $VALUE + 10 > /dev/null 2>&1
                $ echo $?
            expr也可以返回其自身的退出状态,但其与系统最后退出命令刚好相反,成功返回1,任何其他值为无效或错误。
            模式匹配:
                使用expr通过指定冒号选项计算字符串中字符数。
                expr length "this is a test"
                expr substr "this is a test" 3 5
                expr index "sarasara"  a
                在expr中可以使用字符串匹配操作
                    $expr $VALUE : '\(.*\).doc'
                    accounts

    所有功能脚本必须有能力进行判断,也必须有能力基于一定条件处理相关命令。
    内容范围:
        退出状态
        while for 和 until loops 循环
        if then else 语句
        脚本中动作
        菜单
    退出状态:
        主要有4中退出状态
            最后命令退出状态$?
            控制次序命令 $$,||
            其余两种是处理shell脚本或shell退出及相应状态退出状态或函数返回码
            如果不加参数,exit将试图返回上一个命令返回值。
    shell会提供一些列命令声明语句等补救措施来帮助你在命令成功或失败时,或需要处理一个命令清单时采取正确的动作。
        循环和流控制
    流控制:
        if then else 语句提供条件测试
        case语句允许匹配模式、单词或值,一旦模式或值匹配,就可以基于这个匹配条件作其他声明
    循环:
        循环或跳转是一系列命令的重复执行过程。
            for循环,每次处理依次列表内信息,直至循环耗尽。
            until循环
            while循环
    if then else
        if 条件1
            then 命令1
        elif 条件2
            then 命令2
        else 命令三
        fi
        使用if语句时,必须将then部分放在新行,否则会产生错误。如果要不分行,必须使用命令分隔符。
            if 条件; then
                命令
            fi
        if grep 'Dave\>' data.file >/dev/null 2>&1
            then
                xxx
            else
                xxx
            fi
            如果匹配成功,grep返回0,if部分为真?
        决定脚本是否为交互模式:
            有时需要知道脚本运行时交互模式(终端模式)还是非交互模式(cron或at)。脚本也许需要这个信息以决定从哪里取得输入以及输出到哪里,使用test命令并带有-r选项很容易确认这一点,如果test返回值为1,则为交互模式。
                if [ -t ];then
                    xxx
                fi
        null:命令用法
            if xxx
            then
                xxx
            else:
            fi
        if [ "$ANS" = "y" ] || [ "$ANS" = "Y" ]
             ……
     case语句
        case 值 in
            模式1)
                命令1
                ……
                ;;
            模式2)
                命令2
                ……
                ;;
            *)
                ……
                ;;
        esac
        模式部分可能包含元字符,与在命令行文件扩展名例子中使用过的匹配模式类型相同,即:
            * 任意字符
            ? 任意单字符
            [..]类或返回中任意字符
        对匹配模式使用|
    for循环
        for 变量名 in 列表
        do
            命令1
            命令2……
        done
        in列表可以包含替换、字符串和文件名
            for loop in 1 2 3 4 5
            for loop in "orange red blue grey"
            for loop in `ls`
        在for循环中省去in列表选项时,它将接受命令行位置参数作为参数。实际上即指明:
            for params in "$@"
            for params in "$*"
            
            $ XXXXX < < MAYDAY
            $ MAYDAY  DOC文档
    until循环:
        until 条件
            命令1
            ……
        done
        条件可为任意测试条件,测试发生在循环末尾,因此循环至少执行一次。
    while循环:
        while 命令
        do
            命令1
            命令2
        done
        使用while循环读入键盘输入:
            while read FILM
            do
                xxx
            done
            直至输入< Ctrl+D >终止
            
            while read LINE
            do
                echo $LINE
            done < names.txt
        使用IFS读文件:
            输出时要去冒号域分隔符,可使用变量IFS
            while read NAME DEPT ID
                xxx
        case $LINE in
            \#$);;
    while循环和文件描述符:
        while循环用到了空命令:,这是一个死循环,因为null永远返回真。
        exec 3 < &-
    使用break和continue控制循环:
        如果在两层循环内,用break 2 刚好跳出整个循环
    菜单选择:
        如果用户选择无效,应发出警报并带有警告信息。
            echo "\007 the bell ring"
        tput clear

    shell允许将一组命令集或语句形成一个可用块,这些块成为shell函数
    内容范围:
        定义函数
        在脚本中使用函数
        在函数文件中使用函数
        函数举例
    函数有两个部分组成:
        函数标题
        函数体
    标题名必须唯一,如果不是,将会混淆结果,因为脚本在查看调用脚本前将首先搜索函数调用相应的shell
        函数名()
        {
            命令1
            ……
        }
        function 函数名(){
        }
        可以将函数看做是脚本中的一段代码,但是有一个主要区别。执行函数时,它保留当前shell和内存信息。此外,如果执行或调用一个脚本文件中的另一段代码,将创建一个单独的shell,因而去除所有原脚本中定义的存在变量?
        所有函数在使用前必须定义。这意味着必须将函数放在脚本开始部分。
    向函数传递参数:
        向函数传递参数就像在一般脚本中使用特殊变量 $1,$2,...$9一样
    在shell中使用函数:
        文件名应包含语句 #!/bin/sh
        使用set命令查看所有定义的函数
    echo -n 不打印换行
    echo -e 反馈控制字符
        echo -e -n "$@"
    在菜单中进行选择时,最麻烦的工作是必须在选择后键入回车键,或显示“press any key to continue”。可以使用dd命令解决不键入回车符以发送击键序列的问题。
    dd命令常用于对磁带或一般的磁带解压任务中出现的数据问题提出质疑和转换,但也可用于创建定长文件。
        dd if:/dev/zero of=myfine count=512 bs=2048
            创建长度为1兆的文件myfine
        dd命令可以翻译键盘输入,可被用来接受多个字符。这里如果只要一个字符,dd命令需要删除换行字符,这与用户点击回车键相对应。dd只送回车前一个字符。再输入前必须使用stty命令将终端设置成未加工模式,并在dd执行前保存配置,在dd完成后恢复终端设置。
            SAVESTTY=`stty -g`
            stty cbreak
            dd if=/dev/tty bs=1 count=1 2>/dev/null
            stty -cbreak
            stty $SAVEDTTY
    函数中用_开头的表示临时变量,果真是临时的吗?
    nl 列出文件行号
    echo $@ | tr '[a-z]' '[A-Z]'
    获取字符串长度,length($0)
    wc会将两端的空格也作为字符串的一部分,而awk在读取键盘时缺省截取字符串末尾处空格。
    测试函数时,首先将其作为代码测试,当结果满意时,再将其转换为函数。
    函数调用:
        从源文件中调用函数和使用脚本中的函数
    定位文件不只针对于函数,也包含组成配置文件的全局变量。
    创建可用和可重用的脚本很有意义,可以使主脚本变短,结构清晰。

    可以创建一个usage语句,需要时可通知用户怎样以适当的调用参数调用脚本或函数。
    内容涵盖:
        shift
        getopts
        shift和getopts例子
    任何UNIX或LINUX命令均接受一般格式:
        命令 选项 文件
        选项部分最多可包含12个不同的值。?
        幸运的是shell提供shift命令以帮助偏移选项,使用shift可以去除只使用$1到$9传递参数的限制。
            shift 每次将参数位置向左偏移一位
    命令行输入的最后一个参数:
        eval echo \$$#
        使用shift命令: shift `expr $#-2`
        shift 在接收多个参数时,显得力不从心,这就需要用到getopts命令
    getopts
        getopts可以编写脚本,使控制多个命令行参数更加容易。getopts用于形成命令行处理标准形式。原则上讲,脚本应具有确认带有多个选项的命令文件标准格式的能力。
        getopts脚本实例:
            while getopts ahfgv OPTION
            do
                case $OPTION in
                    a)ALL=true
                        echo "ALL is $ALL"
                        ;;
                    ……
                esac
            done
        getopts option_string variable
            如果未发现匹配字符,变量被设置为?
    使用getopts指定变量取值
        有时有必要在脚本中指定命令行选项取值。
            getopts ahfvc: OPTION
                使用选项取值时,必须使用变量OPTARG保存该值
                \?)
                    $usage statement
            getopts的一种功能是运行后台脚本。这样可以使用户加入选项,指定不同的磁带设备以备份数据
        while getopts :luv 
            最前面的冒号“:”用于指定getopts工作于silent mode
    常用命令行选项和含义列表:
        -a 扩展
        -c 计数、拷贝
        -d 目录、设备
        -e 执行
        -f 文件名、强制
        -h 帮助
        -i 忽略状态
        -l 注册文件
        -o 完整输出
        -q 退出
        -p 路径
        -v 显示方式或版本

    用户可以使用shell脚本创建交互性的、专业性强的屏幕输出。要实现这一点,系统上需要一个彩色监视器和tput命令。
    内容范围:
        tput命令
        使用转义序列和产生控制码
        使用颜色
    tput至今为止最好的是GNU tput。tput使用文件/etc/terminfo或/etc/termcap,这样就可以在脚本中使用终端支持的大部分命令了。
    虽然tput不识别颜色设置,但是可以使用控制字符实现这一点。
    tput
        在使用tput之前,需要在脚本或命令行中使用tput命令初始化终端。
            $tput init
                tput产生三种不同的输出:字符型、数字型和布尔型(真/假)
        字符串输出:
            大部分常用字符串:
                bel 警铃
                blink 闪烁模式
                bold 粗体
                civis 隐藏光标
                clear 清屏
                cnorm 不隐藏光标
                cup 移动光标到屏幕位置(x,y)
                el 清除到行尾
                ell 清除到行首
                smso 启动突出模式
                rmso 停止突出模式
                sc 保存当前光标位置
                rc 恢复光标到最后保存位置
                sgr0 正常屏幕
                rev 逆转视图
        数字输出:
            大部分常用数字输出:
                cols 列数目
                it tab设置宽度
                lines 屏幕行数
        布尔输出:
            两种布尔输出:
                chts 光标不可见
                hs 具有状态行
    tput用法:
        可以取得所有tput名字输出,将其保存为更有意义的变量名
            variable_name=`tput name`
        使用布尔输出:
            if `tput hs`
        所有控制字符均以一个转义序列开始。通常转义键后紧跟字符[。然后实际序列打开和关闭某终端属性。
            发送一转义序列以关闭光标
                linux/bsd echo -e "\033[?251"
                System V  echo "\033[?251"
                General method echo "[?251"
                    \033为转义键取值
                        要反馈一个 @ 字符
                            echo "@"
                            echo -e "\100"
                命令clear表示清屏并发送光标到屏幕左上角,此位置一般也称为home
                对于嵌入在文本中的任何控制字符,不要试图剪切和粘贴,因为这样会失去其特殊含义。
                例如,要插入控制字符,打开光标,方法如下:
                    echo ' hit the  key then [?25h'
                        即先击,再击退格键,确保这不是一个仿真器。然后加入一小段脚本将之打开和关闭。
            光标位置:
                可以用tput将光标放在屏幕任意位置
                    cup r c
                        r为从上至下屏幕行数,c为穿过屏幕列数
            屏幕中间显示:
                LEN=`echo $STR | wc -c`
                COLS=`tput cols`
                NEW_COL=`expr \($COLS-$LEN\)/2`
                xy 10 $NEW_COL
            查找终端属性:
                使用tput访问terminfo,显示前面提到过的tpur命令下的一些终端转义码
                查看终端定义文件的完整列表:
                    $ infocmp $TERM
            在脚本中使用功能键
                使用cat命令可以查看发送的任意特殊键控制序列(F1,上箭头等),键入cat -v,饭后按任意控制键,回车,在下一行就可以知道终端发送了什么功能键。结束后按退出。
                    F1(^[OP]) F2([OQ]),上箭头[^[[A]
            使用颜色:
                对域使用颜色可以使数据输入屏幕看起来更加专业。下面将使用的颜色是ANSI标准颜色,并不是所有颜色都适合于所有系统。下面列出了大部分常用颜色。
                1、前景色:
                    数字  颜色
                    30 黑色
                    31 红色
                    32 绿色
                    33 黄(或棕)色
                    34 蓝色
                    35 紫色
                    36 青色
                    37 白(或灰)色
                2、背景色
                    40 黑色
                    41 红色
                    42 绿色
                    43 黄(或棕)色
                    44 青色
                    45 蓝色
                    46 青色
                    47 白(或灰)色
                 [background_number;foreground_number m
            产生颜色:
                产生颜色需要在echo语句中嵌入控制字符
                    要产生一个黑色背景加绿色前景色
                        linux/bsd echo -e "\033[40;32m"
                        System V echo "\033[40;32m"
                        Generic method echo "[40;32m"
                    将颜色设置与echo语句放在一个case语句里,然后将之编成一个函数,这样做最好。
                作者终端的缺省屏幕颜色是黑色和白色。但是如果要用黑色背景加绿色前景,可插入一个echo语句,同时将之放入用户.profile文件中。
                trap命令,用户忽略信号2,3,15 这样将防止用户试图跳出菜单。
    使用tput命令可以增强应用外观及脚本的控制。颜色设置可以增加应用的专业性。可以使用和读取控制字符来增加脚本的灵活性,特别是对用户击键输入操作更是如此。

    屏幕输入或数据输入时接受输入(这里是键盘)并验证其有效的能力。如果有效,接受它,如果无效,放弃该输入。
    内容范围:
        验证有效输入
        增加、删除、修改和查看记录
        修改脚本的工作文件。
    运行脚本应具有一些菜单选项,与任务或模块相连接或包含在文件里与菜单脚本相关的一些列函数相连接。
        每一段脚本均执行trap命令,信号2、3和15被忽略。
    如果文件包括超过几百个记录,建议使用awk,因为使用awk比直接从文件中读取数据快得多,同样也比用grep将各域分开存入变量要快一些。
        使用grep -v 执行记录删除
    打印一个记录
        pr <<- MAYDAY
            ADADF:$REC
            ……
        MAYDAY
            如果用双引号或单引号将”limit_string”引用起来或用转义符\将其转义,则here-document中的文本将不被扩展,即参数替换被禁用。
            否则,here-document中的所有文本都将进行常规的参数扩展、命令替换、表达式计算。
            在后一种情况下,字符序列\将被忽略, 并且必须对元字符\, $, 和`使用\进行转义。
            如果用<<而不是<<-,则后面的limit_string必须位于行首,否则,若here_document用在函数内部,则会报语法错误;用在函数外面,其后面的所有内容均会被当做here_document的内容。
            如果重定向操作符是<<-, 则msg_body和limit_string行中的所有前缀TAB字符都将被忽略(但空格不会被忽略)。这样源代码中的here-documents就可以按照优雅的嵌入方式进行对齐。
    在计算机工业发展史上有一句老话:送进去的是垃圾,出来的肯定是垃圾,但知道这一点时已经太晚了,意即如果没有在脚本中测试垃圾数据,就会输出垃圾信息。
    shell编程最烦人的一项工作是调试问题。有一些方法可以借鉴,但是最好能在问题出现前防止大部分错误,为此应遵循以下规则:
        将设计脚本分成几个任务或过程,然后再继续下一步前分别予以测试。
        内容范围:
            一般错误
            set命令介绍
        经常碰到的问题时忘了使用引号或在if语句末尾维嘉fi
        需要牢记的一点是当shell打印出一个脚本错误后,不要只看那些疑问行。而是要观察整个相关代码段。shell不会对错误进行精确定位,而是在试图结束一个语句时进行错误统计。
    一般错误:
        循环错误:
        典型的漏写引号
        测试错误
        字符大小写
        for循环
            使用for循环时,有时会忘了在循环列表部分用$符号,特别是在读取字符串时。
        echo
            最有用的调试脚本工具时echo命令,在变量读取或修改操作其前后加入echo命令
            使用最后状态命令判断命令是否成功,这里需要注意的是,不要使用echo命令后直接加最后状态命令,因为此命令永远为真?
    set命令:
        set命令可辅助脚本调试,以下是set命令常用的调试选项:
            set -n 读命令但不执行
            set -v 显示读取的所有行
            set -x 显示所有命令及其参数
        将set选项关闭,只需用+替代-
    跟踪错误的最好方式是亲自查阅脚本,并使用set命令并加大量的echo语句。

    这些命令是在实际的Bourne shell里创建而不是存在于/bin或usr/bin目录里。嵌入命令比系统里的相同命令要快。
    内容范围:
        标准的bourne shell嵌入命令列表
            例如:
                cd 和 pwd命令可同时在系统和嵌入命令中发现。如果要运行系统版,简单输入命令路径即可:/bin/pwd
    shell嵌入命令完整列表:
        : 空,永远返回为true
        . 从当前shell中执行操作
        break 退出for、while、until或case语句
        cd 改变到当前目录
        continue 执行循环的下一步
        echo 反馈信息到标准输出
        eval 读取参数,执行结果命令
        exec 执行命令,但不在当前shell
        exit 退出当前shell
        export 导出变量,使当前shell可利用它
        pwd 显示当前目录
        read 从标准输入读取一行文本
        readonly 使变量只读
        return 退出函数并带有返回值
        set 控制各种参数到标准输出的显示
        shift 命令行参数向左偏移一个
        test 评估条件表达式
        times 显示shell运行过程的用户和系统时间?
        trap 当捕获信号时运行指定命令
        ulimit 显示或设置shell资源
        umask 显示或设置缺省文件创建模式
        unset 从shell内存中删除变量或函数
        wait 等待直到子进程运行完毕,报告终止 ??
    set
        在查看调试脚本、打开或关闭shell选项时,曾用到set命令。set也可用于在脚本内部给出其运行参数。
            set param1 param2
        当测试一段脚本且脚本包含参数时,这样使用set命令,就不必在每次运行脚本时重复输入参数。
    times
        times命令给出用户脚本或任何系统命令的运行时间。第一行给出shell消耗时间,第二行给出运行命令消耗的时间?
    type
        使用type查询命令是否仍驻留系统及命令类型。type打印命令名是否有效及该命令在系统的位置。
    ulimit
        ulimit设置运行在shell上的显示设置。通常此命令定位于/etc/profile中,但是可以从当前shell或用户.profile文件中将之移入用户需要的位置。
            ulimit options
                常用选项:
                    -a 显示当前限制
                    -c 限制内核垃圾大小
                    -f 限制运行进程创建的输出文件的大小。
     wait
        wait命令等待直到一个用户子进程完成,可以在wait命令中指定进程ID号,如果并未指定,则等待直到所有子进程完成。

    内容涵盖:
        快速创建一个文件
        自动进入菜单
        ftp传输
        连接至其他应用系统
            command << word
            text
            wort
            可以使用<<来创建文件、显示文件列表、排序文件列表以及创建屏幕输入
    快速创建一个文件
        $ cat >> myfile <> myfile <<- NEWFILE
    快速创建打印文档:
        $ lpr <> $log_f 2>&1 << MAYDAY
        2
        3
        Y
        MAYDAY
        编写一个使用<<输入的脚本就可以自动运行原来的脚本
    自动ftp传输
        当用户输入想要连接的主机之后,首先执行一个名为traceroute的脚本验证本地主机是否能够连接到远程主机。
        缺省为二进制模式
    也可以结合着mysql来用。
    本章进一步给出了一些实用<<来自动完成某些任务的例子。<<的用途很广,特别是在连接某些应用程序或使用ftp时。你可以灵活的使用<<来自动运行以前编写的脚本,从而完成各种不同的任务。
    内容涵盖:
        创建以日期命名的文件和临时文件
        信号
        trap命令以及如何捕获信号
        eval命令
        logger命令
    信号:
        信号就是系统向脚本或命令发出的消息,告诉他们某个事件的发生。这些事件通常是内存错误、访问权限问题或某个用户试图停止你的进程。信号实际上是一些数字。
            信号 信号名 含义
            1 SIGHUP 挂起或父进程被杀死
            2 SIGINT 来自键盘的中断信号,通常是
            3 SIGQUIT 从键盘退出
            9 SIGKILL 无条件终止
            11 SIGSEGV 段(内存)冲突
            15 SIGTERM 软件终止(缺省杀进程信号)
            还有信号0,该信号为“退出shell”信号。为了发出信号0,只要从命令行键入exit,或在一个进程或命令行中使用即可。
        kill [-signal no:| signal name] process ID
        杀死一个进程:
            发送信号1将使一个进程重新读入配置文件。
        检测信号:
            有些信号可以被应用程序或脚本捕获,并依据该信号采取相应的行动。另外一些信号不能被捕获。例如,如果一个命令收到了信号9,就无法再捕捉其他信号。
            在编写shell脚本时,只需关心信号1、2、3、15
        trap
            trap可以使你在脚本中捕获信号:
                trap name signal(s)
                    Name 需要用双引号引起来。
                脚本在捕捉到一个信号以后,通常会采取某些行动。最常见的包括:
                    1、清除临时文件
                    2、忽略该信号
                    3、询问用户是否终止该脚本的运行
                trap "" 2 3 忽略信号2和信号3,用户不能终止该脚本
                trap "commands" 2 3
                trap 2 3 复位信号2和3,用户可以终止该脚本。
                    也可以用单引号来代替双引号,其结果是一样的。
                在case语句中一定要包含用户输入空字符串的情况。
            锁住终端:
                trap 命令捕捉信号2、3、15
                如果你从另外一个终端上杀死了进程,当再次回到这个终端时,可能会遇到终端设置问题,例如回车键不起作用,而已试着使用以下命令:
                    $stty sane
    eval
        eval 命令将会首先扫描命令行进行所有的置换,然后再执行该命令。该命令适用于那些一次扫描无法实现其功能的变量。该命令对变量执行两次扫描。这些需要进行两次扫描的变量有时被称为复杂变量。
        $ eval echo $NAME
        $(eval echo \$$#)
        eval命令并不是一个在脚本中很常见的命令,但是如果需要对变量进行两次扫描的话,就要使用eval命令了。
    logger命令:
        系统中含有相当多的日志文件。其中一个日志文件叫做messages,它通常位于/var/adm或/var/log目录下。一个名为syslog的配置文件可以用来定义记录在messages文件中的消息,这些消息有一定的格式。
        如果想知道系统中的相应配置,可以查看/etc/syslog.conf文件。该文件中包含了用于发送各种不同类型消息的工具及他们的优先级。
        可能会基于以下原因向该文件发送消息:
            在某一个特定的时间段出现的访问或登录
            你的某些执行关键任务的脚本运行失败
            监控脚本的报告。
        logger -p -I message
            -p 为优先级,这里只涉及到提示用户注意的优先级,这也是缺省值。
            -i 在每个消息中记录发送消息的进程号
    脚本的一个优点:
        它不是很长、很复杂,只需很短的代码就能够完成相当多的功能,可以节约大量的时间。
    备份:find,cpio
    如果希望临时禁止某个用户登陆,可以修改/etc/passwd文件,把该用户的口令域的第一个字符变成*。
        linux提供了一个工具,可以通过他在login.access文件中写入用户名和用户组。该文件可以用来允许或禁止用户对系统的访问。
        grep的精确匹配模式 all\> ???
    linux每块一般为4K字节

    如果希望在系统启动时自动运行某些应用程序、服务或脚本,或者在系统重启时能够正确的关闭这些程序,那么需要创建运行级别脚本。除一种linux变体外,所有的linux版本都含有这种基于系统V的运行级别配置目录,就像其他unix版本那样。
    内容涵盖:
        运行级别
        如何创建rc.scripts
        如何在不同的运行级别实现相应的rc.scripts
        如何从inittab中启动应用程序
    能够创建运行级别脚本就意味着能够更灵活的控制系统。
    任何用关键字start或stop调用的、能够启动或停止程序运行的脚本都可以看做是一个rc.script。注意,应当由用户来保证他或她所提交的脚本是一个有效的脚本,能够正确的启动或停止某以服务。
    运行级别配置目录的机制使得rc.script只在系统切换运行级别时有效。
    还可以按照运行的服务来控制系统的运行级别,本书不予讨论
    怎么知道系统中是否含有运行级别目录:
        rc.scripts一般保存在(实际上是个链接)/etc/rcN.d或/etc/rc.d/rcN.d目录下
            其中N是一个数字,通常是7个,0~6.
                不过在系统上可能会有另外几个目录,如rcS.d,这不重要,我们只关心带数字的目录
    确定当前的运行级别:
        $ who -r
        $ runlevel 系统的前一个运行级别、当前的运行级别
    快速熟悉inittab
        运行级别目录中含有一系列启动服务的脚本。这里的服务可以是守护进程、应用进程、服务器、子系统或脚本进程。在系统启动的过程中,将会启动一个名为init的进程(它是系统中所有进程的祖先)。它所完成的一部分工作就是看看需要启动哪些服务,应当缺省地进入哪一个运行级别。他通过查看一个名为inittab的配置文件来获得上述信息。
        init进程还按照该文件中的设置加载特定的进程。如果需要编辑这个配置文件,一定要先做一个备份。如果该文件被破坏或出现“降级”错误,系统将无法正常启动,到那时,将不得不进入单用户模式并修改该文件。
        inittab文件所包含的域具有严格的格式,该文件中每个条目的格式为:
            id:rstart:action:process
                id是相应进程的唯一标识
                rstart域所包含的数字表示运行该进程的级别。
                action域告诉ini进程如何对待process所对应的进程。这里有很多中动作,但是最常见的是wait和respawn。
                    wait意味着当进程启动后等待它结束。respawn则意味着如果该进程不存在,则启动相应的进程,如果它存在,那么只要她一掉下来就立即重新启动它。(大概不超过1s)
                process域包含实际要运行的命令。
    运行级别:
        init进程在系统完全就绪之前所做的最后几项工作之一就是执行缺省运行级别所包含的所有脚本。该进程是通过/etc/rc.d/rc或/etc/rc.init来启动这些脚本的。它的作用是首先杀死该运行级别所包含的进程再启动这些进程。
            给每一个链接名为K开头的相应脚本赋予参数stop;给每一个链接名以S开头的响应脚本赋予参数start。在运行级别切换时,上述脚本也会完成同样的工作,只不过根据相应的运行级别来启动或停止对应的脚本。
        真正的脚本实际上是放在/usr/sbin/init.d或/etc/init.d目录,linux是在/etc/rc.d/init.d
        可选的参数包含restart 和status
    各种运行级别:
        系统含有七种运行级别,不同的系统在某些运行级别上稍有差别。
            运行级别0 启动和停止整个系统
            运行级别1 单用户或管理模式
            运行级别2 多用户模式;部分网络服务被启动,有些系统将其作为正常运行模式,而不是级别3
            运行级别3 正常操作运行模式,启动所有的网络服务
            运行级别4 用户定义的模式,可以使用该级别来定制所需要运行的服务。
            运行级别5 有些unix操作系统变体将其作为缺省X-windows模式,还有些系统将它作为系统维护模式
            运行级别6 重启动
    运行级别脚本的格式:
        rcN.d目录
            Snnn.script_name
            Knnn.script_name
            nn是00至99的两位数字,不过在有些系统中是000至999三位数字,在不同目录中的链接应采用同一数字。
        当init进程调用相应的运行级别脚本时,杀进程从高到低的K序号,启动从低到高。
    安装运行级别脚本:
        满足以下条件:
            编写该脚本,确保它符合调用标准
            确信它能够启动或终止相应的服务
            将该脚本放置于(取决于操作系统)/etc/init.d或/usr/sbin/init.d或/etc/rc.d中
            在相应的rcN.d目录中按照合理的命名方式创建链接。
        系统并不对使用已占用的序号做任何检查。
    使用inittab来启动应用程序
        由于我有几个用于系统检查的脚本需要在系统刚刚就绪之后运行。使用inittab是实现上述功能的理想途径。
    启动和停止服务的其他方法
        /etc/rc.local
            该脚本文件将在inittab和运行级别脚本之后运行,可以在该文件中加入任何命令,或从中调用最习惯用的启动脚本。
    运行级别的确是一个系统管理问题,本章的目的在于:使你了解在系统启动时,如何按照需求灵活的控制各种服务和脚本的启动。


    内容涵盖:
        基本cgi脚本
        使用服务器端内嵌(Server Side Includes,SSI)
        get方法
        post方法
        创建交互式脚本
        能够自动重载web页面的cgi脚本
    如果一个web服务器能够交换信息脚本,那么它必须支持一种被称为公共网关接口的协议,即大家所熟悉的cgi
    cgi:
        cgi是一种规范,它规定了获取信息的脚本如何从服务器中取得信息或向服务器中写入歇息。这种脚本或cgi脚本可以用任何语言来实现。最流行的是Perl语言。
    对编码字符串解码:
        所有的空格用+来替代
        所有的值域用&隔开
        所用的值和相应域用=隔开
        所有的符号和一些特殊字符用%xy形式表示,其中xy是该字符的16进制ASCII码
            这种16进制字符,包括特殊字符&、%、+、=、(、)及所有ASCII码超过127的其他特殊字符。
    我们可以编写用于监视、显示、数据库查询等的脚本。

    cat:
        cat dt1 | while read line
        do
            echo $line
        done
    compress:
        compress options files
            -v 显示压缩结果
        compress用来压缩文件,压缩后的文件名具有'.Z'后缀。

    cp:
        cp options file1 file2
            -p 保留权限模式和更改时间
            -r 拷贝相应的目录和子目录
        diff:
            diff options file1 file2
                -c 按照标准格式输出
                -I 忽略大小写
    dircmp:
        dircmp options dir1 dir2
            -s 不显示相同的文件
            dircmp命令与diff命令十分相似,它比较并显示两个目录的不同。
    du:
        du options directory
            -a 显示每个文件的大小,不仅是整个目录所占用的空间。
            -s 只显示总计

        du显示的磁盘空间占用是以512字节的块来表示的。它主要用于显示目录所占用的空间。
    file:
        用于确定文件的类型
    fuser
        fuser options file
            -k 杀死所有访问该文件或文件系统的进程
            -u 显示访问该文件或文件系统的所有进程
            fuser命令可以显示访问某个文件或文件系统的所有进程。在有些系统上,-u和-m选项可以互换。还可以在if语句中使用fuser命令。
        fuser -m /dev/hda5
    mkdir
        mkdir options directory
            -m 在创建目录时按照该选项的值设置访问权限
    more
        more options files
            该命令和page和pg命令的功能相似,都能够分屏显示文件内容
                -c 不滚屏,而是通过覆盖来换页
                -d 在分页处显示提示
                -n 每屏显示n行
    nl:
        nl options file
            -I:行号每次增加n;缺省为1
            -p:在新的一页不重新计数
        nl myscript | lpr
    printf
        printf format arguments
            格式符format包含三种类型的项,这里我们只讨论格式符:
                %[- +]m.nx
                其中横岗-为从行首算起的起始位置。一般说来,m表示域的宽度而n表示域的最大宽度。
                %后面可跟下列格式字符:
                    s:字符串
                    c:字符
                    d:数字
                    x:16进制数
                    o:10进制数
                常用转义字符:
                    \a 响铃
                    \b 退格
                    \r 回车
                    \f 换页
                    \n 换行
                    \t 跳格
                $printf "\x2B\n"
                    显示+
                $printf "%-10sStand-by\n"
                    从左起第10个字符的位置开始显示字符
        script:
            script option file
                -a 将输出附加在文件末尾 ???
        strings
            strings filename 查看二进制文件中所包含的文本
        touch
            touch options filename
                -t MMDDhhmm 创建一个具有相应月、日、时分时间戳的文件。
                下面的命令能够以当前时间创建文件或更新已有文件的时间戳??
        tty
            可以使用tty来报告所连接的设备或终端
            $ tty
                /dev/tty08
            可以使用tty -s 命令来确定脚本的标准输入。返回码为:
                0:终端
                1:非终端
        uname 
            uname options
                -a 显示所有信息
                -s 系统名
                -v 只显示操作系统版本或其发布日期
        uncompress
            在解压缩时不必给出.Z后缀
        wait
            wait process ID
            $ wait 
                等待所有的后台进程结束后再执行当前脚本
                ?
        whereis
            whereis 命令能够给出系统命令的二进制文件及其在线手册的路径。
        who
            who options
                -a 显示所有的结果
                -r 显示当前的运行级别(在linux中应当使用runlevel)
                -s 列出用户名及时间域