shell编程笔记:

20170621 林林

脚本编程语言是一种解释型语言,这类程序的执行,是由解释器interpret读入程序代码,并将其转化
成内部的形式,在执行。需要注意的是解释器本身是一般的编译型程序。

脚本语言的优缺点:
    脚本语言多半运行在比编译型语言更高的层次,能够轻易地处理文件与目录之类的对象。但它们的效率通常不
如编译型语言。

脚本编程语言:awk Perl Python Ruby Shell

使用Shell脚本的优点:
    简单性:Shell是一个高级语言,通过它,可以简洁的表达复杂的操作
    可移植性:使用POSIX所定义的功能,可以做到脚本无需修改就可在不同的系统中执行
    开发容易:可在短时间内开发一个功能强大又好用的脚本
   

Shell脚本基础:

每个Shell脚本,第一行一定是使用 #! 开头再加上一个可用于执行程序的解释器的完整路径
    例如:#! /bin/sh
除此之外,内核还会扫描是否有一个选项要传递给解释器,内核会以被引用的选项来引用解释器,再搭配命令行的其他部分。
    例如:#! /bin/bash -f
注意:
    在某些系统中,命令行部分包含了命令的完整路径名称,不过有些系统却并非如此,命令行的部分会原封不动的
    传给程序,因此,脚本是否具有可移植性取决于是否有完整的路径名称。
    不能在选项之后放置任何空白,因为空白也会被传递给被引用的程序
   
Shell的基本元素:
    命令与参数:
        Shell最基本的工作就是执行命令。
    格式:
        1.使用空白(空格或Tab键)隔开命令行中的各个组成部分
        2.命名名称是命令行的第一个项目,通常其后会跟选项,任何额外的参数都会放在选项之后。
        3.选项开头是一个破折号,后面接一个字母
        4.分号 ;用来分隔同一行里的多条命令
        5.使用&而不是分号,则Shell会在后台执行其前面的命令,这意味着,Shell不用等到该命令完成,就可以执行下一个命令
       
    Shell识别的三种基本命令:内建命令,Shell函数,外部命令
    (1)内建命令就是由Shell本身所执行的命令。
    有些命令是由于其必要性才内建的,例如cd用来改变目录,read会将来自用户(和文件)的输入数据传给Shell外亮。
    另一种内建命令的存在则是为了效率,其中最典型的就是test命令,编写脚本时经常会用到它。另外还有I/O命令,例如echo于printf.
    (2)Shell函数是功能健全的一系列程序代码,以Shell语言写成,它们可以像命令那样引用。
    (3)外部命令就是由Shell副本(新的进程)所执行的命令,基本的过程如下:
    a. 建立一个新的进程。此进程即为Shell的一个副本。
    b. 在新的进程里,在PATH变量内所列出的目录中,寻找特定的命令。
       /bin:/usr/bin:/usr/X11R6/bin:/usr/local/bin为PATH变量典型的默认值。
       当命令名称包含有斜杠(/)符号时,将略过路径查找步骤。
    c. 在新的进程里,以所找到的新程序取代执行中的Shell程序并执行。
    d. 程序完成后,最初的Shell会接着从终端读取下一条命令,和执行脚本里的下一条命令。

变量:某个信息片段的名字,每个变量都有一个值。在Shell中,变量值可以是(通常是)空(null),即不含任何字符。
     这是合理的,也是常用的,好用的特性。
     Shell变量名:以一个字母或一个下划线开头,后面接任意长度的字母,数字或下划线符号。
     变量赋值:NAME=value
     注意:等号两边没有任何空格,当所赋予的值内含空格时,需要加上引号
     当想要取出Shell变量的值时,需要在变量名称前面加上$符
   
输出:
    echo:用于输出指定字符串或用于在Shell中打印Shell变量的值
    语法格式:echo [选项] [参数]
    -n:不输出换行
    linlin@ubuntu:~/linlin/text$ echo -n leihou
  leihoulinlin@ubuntu:~/linlin/text$ echo leihou
  leihou

    -e: 激活转义字符, 输出彩色文字时使用
    常见的转义字符:
    \a 发出警告声
    \b 删除前一个字符
    \c 最后不加上换行符
    \f 换行,单光标任停留在原来的位置
    \n 换行,且光标移至行首
    \r 光标移至行首,但不换行
    \t 插入Tab
    \\ 表示\
    \### 插入###(八进制)所代表的ASCII字符
   
    printf:格式化并输出到标准输出
    printf与echo不同的是,他不会自动添加换行,需要手动使用
    linlin@ubuntu:~/linlin/text$ printf "haha"
  hahalinlin@ubuntu:~/linlin/text$ printf "haha\n"
  haha
  
  
  格式替代符
  %b 相对应的参数被视为含有要被处理的转义序列之字符串。
  %c ASCII字符。显示相对应参数的第一个字符
  %d, %i 十进制整数
  %e, %E, %f 浮点格式
  %g %e或%f转换,看哪一个较短,则删除结尾的零
  %G %E或%f转换,看哪一个较短,则删除结尾的零
  %o 不带正负号的八进制值
  %s 字符串
  %u 不带正负号的十进制值
  %x 不带正负号的十六进制值,使用a至f表示10至15
  %X 不带正负号的十六进制值,使用A至F表示10至15
  %% 字面意义的%
  
  转义序列
  \a 警告字符,通常为ASCII的BEL字符
  \b 后退
  \c 抑制(不显示)输出结果中任何结尾的换行字符(只在%b格式指示符控制下的参数字符串中有效),而且,任何留
     在参数里的字符、任何接下来的参数以及任何留在格式字符串中的字符,都被忽略
  \f 换页(formfeed)
  \n 换行
  \r 回车(Carriage return)
  \t 水平制表符
  \v 垂直制表符
  \\ 一个字面上的反斜杠字符
  \ddd 表示1到3位数八进制值的字符,仅在格式字符串中有效
  \0ddd 表示1到3位的八进制值字符
  
      先把控制码列出来(从网上搜来的):
    \033[0m               关闭所有属性
    \033[1m                       设置高亮度
    \033[4m                       下划线
    \033[5m                        闪烁
    \033[7m                        反显
    \033[8m                        消隐
    \033[30m----\33[37m 设置前景色
    \033[40m----\33[47m 设置背景色
    \033[nA                        光标上移n行
    \033[nB                       光标下移n行
    \033[nC                       光标右移n行
    \033[nD                       光标左移n行
    \033[y;xH                     设置光标位置
    \033[2J                        清屏
    \033[K                         清除从光标到行尾的内容
    \033[s                          保存光标位置
    \033[u                          恢复光标位置
    \033[?25l                      隐藏光标
    \033[?25h                     显示光标
    背景色:
    40:黑?41:深红?42:绿 43:黄色?44:蓝色?45:紫色?46:深绿?47:白色
    前景色:
    30:黑?31:红?32:绿?33:黄?34:蓝色?35:紫色?36:深绿?37:白色
    控制字符是打开某种样式,输出完成时需要再关闭样式才能使terminal恢复到原来状态,简单例子:
    printf("\e[32m%s\e[0m\n", "hello world");
    \033和\e是一回事,使用\e会更简单一些,输出为绿色字体,如下图:

    \e[32m为打开绿色前景色样式,\e[0m为关闭所有样式,如果未关闭,则所有输出字体均为绿色前景色,如下代码:
     printf("\e[32m%s\n", "hello world");
    输出效果如下:

    绿色高亮代码如下:
    printf("\e[32m\e[1m%s\e[0m\n", "hello world");
    输出效果如下:

    另外光标移动的控制码在输出类似于下载进度的信息时用得到。
    在shell中也可以直接用echo输出,需要加-e选项打开转义字符解释,如输出高亮的绿色字体为:
    echo -e "\e[32m\e[1mhello world\e[0m"
    root@ubuntu:/home/linlin/linlin/shell# echo -e "\e[44mHello World! \e[0m"
        Hello World!

基本的IO重定向
标准输入输出是软件设计原则里最重要的概念呢之一,即程序应该有数据的来源端,数据的目的端以及报告问题的地方
即标准输出,标准输出,标准错误输出
Linux —— 重定向与管道

系统的输入输出包括:
默认输入设备:标准输入,STDIN,描述符为0
默认输出设备:标准输出,STDOUT,描述符为1
              标准错误输出,STDERR,描述符为2
             
标准输入,标准输出,标准错误输出是三个不同的数据流
             
系统默认标准输入是从键盘读取,标准输出和标准错误输出是输出到显示器

Linux中I/O重定向:
    输出重定向,
    >:覆盖输出,作为输出目标的文件原有内容会被覆盖
    示例:
    root@ubuntu:/home/linlin/linlin/test# echo 'The dog is so cute'
  The dog is so cute
  root@ubuntu:/home/linlin/linlin/test# echo 'The dog is so cute' > file
  root@ubuntu:/home/linlin/linlin/test# cat file
  The dog is so cute
  覆盖
  root@ubuntu:/home/linlin/linlin/test# echo 'The cat is so smart' > file
  root@ubuntu:/home/linlin/linlin/test# cat file
  The cat is so smart

    >>:追加输出,保留作为输出目标的原有文件
    示例:
    root@ubuntu:/home/linlin/linlin/test# echo 'The dog is so cute' >> file
  root@ubuntu:/home/linlin/linlin/test# cat file
  The cat is so smart
  The dog is so cute
   
    在使用输出重定向时,可能由于某个输出目标文件内容很重要,这时就需要使用追加
    输出,但在实际使用过程中,很容易错把>当做>>使用,为了防止这个错误,我们可以
    使用set命令
    set:shell内置功能开关
    set -C:禁止对已存在文件使用覆盖重定向
    强制覆盖输出使用 >|
    set +C:允许对已存在内容重定向
    root@ubuntu:/home/linlin/linlin/test# set -C
  root@ubuntu:/home/linlin/linlin/test# echo 'The cat is so smart' > file
  bash: file: cannot overwrite existing file
  
  tee:从标准输入读取数据,输出到标准输出,并保存至文件
  
  2>:标准错误输出重定向
  2>>:标准错误追加重定向

    &>:重定向标准输出和标准错误输出到同一个文件
    注意 &>> 不支持
   
    <:输入重定向
    <<结束符:表示在此处生成文档,当输入为结束符时表示输入结束
    here document具体解释https://linux.die.net/abs-guide/here-docs.html
   
   
构造管道时,应该试着让每个阶段的数据量变得更少,换句话说,如果你有两个要完成的步骤与先后次序无关,你可以
把让数据变少的那一个步骤放在管道的前面,这么做可以提高脚本的整体性能,应为此时系统只需要在两个程序中移动
少的数据量,每个程序要做的是也比较少。

特殊文件:

/dev/null :位桶bit bucket,传送到此文件的数据都会被系统丢掉。当系统将数据写到此文件时,会认为它已成功完
成写入数据的操作,但实际什么都没做。如果需要一个命令的退出状态而不是它的输出,此功能十分实用。

/dev/tty :当程序打开此文件时,系统将会自动将它重定向到一个终端(一个实体控制台,或串行端口也可能是一个通
过网络与登录窗口的伪终端)在于程序结合。这个在程序必须读取人工输入时特别有用,另外也可以用来产生错误信息。

stty: 控制终端的各种设置
stty +/- 功能: 启用或禁用某种功能

echo $PATH: 查看Shell寻找命令的查找路径,
root@ubuntu:/home/linlin# echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games
名称为bin的目录用来保存可执行文件

访问Shell脚本参数:
位置参数:Shell脚本中的命令行参数

Shell脚本中以#开头的行为注释
各种Locale环境变量
LANG            未设置任何LC_***变量时使用默认值
LC_ALL          用来覆盖掉所有其他LC_***变量的值
LC_COLLATE      使用指定地区的排序规则
LC_CTYPE        使用所指定地区的字符集(字母,数字,标点符号等)
LC_MESSAGES     使用指定地区的响应与信息,仅POSIX适用
LC_MONETYPE     使用所指定地区的货币格式
LC_NUMERIC      使用所指定地区的数字格式
LC_TIME         使用所指定地区的日期与时间格式

locale -a:列出所有的locale名称
linlin@ubuntu:~$ locale -a
C
C.UTF-8
en_AG
en_AG.utf8
en_AU.utf8
en_BW.utf8
en_CA.utf8
en_DK.utf8
en_GB.utf8
en_HK.utf8
......

  
正则表达式:regular expression REGEXP
    基本正则表达式
      
      元字符:
    . 点:匹配任意单个字符
    []  :匹配任意指定范围内的单个字符
    [^] :匹配任意指定范围外的单个字符
    
      匹配次数:
    *  :匹配其前面的字符任意次数
    .* :表示任意长度任意字符
    \  :用来转义,转义字符
    \? :匹配其前面的字符1次或0次
    \{m,n\}:匹配其前面字符最少m次,最多n次,n可以省略,表示最多无限次,m不可以省略
    
    位置锚定:
    ^ :锚定行首,表示此字符后面的字符串必须出现在行首
    $ :锚定行尾,表示此字符前面的字符串必须出现在行尾
    ^$:表示空白行
    \<或\b:锚定词首,其后的任意字符必须作为单词的首部出现
    \>或\b:锚定词尾,其前的任意字符必须作为单词的尾部出现
    \< \>:其内的字符串必须以单个字符出现
    
    分组:
    \( \):将其内的字符串作为一个整体看待
    \n:调用前面第n个左括号以及与之对应的右括号没有匹配到的内容
    
        后向引用:将正则表达式前面的某个部分看做一个整体,在正则表达式的后面引用它
            将子表达式放在\(STRING\)内,单个模式里可包括至多9个子表达式,且可为嵌套结构
            \(ab\)\(cd\)[def]*\2\1    可以匹配到abcdcdab  abcdeeeecdab  abcdddeeffcdab
            \(why\).*\1               可以匹配一行里出现两次why
            \(["']\).*\1              匹配单引号或双引号括起来的字符串
           
    BRE运算符的优先级,从高到低
    [..] [==] [::]      用于字符排序的方括号符号
    \                   转义字符
    []                  方括号表达式
    \(\)  \n            分组与后向引用
    *  \{\}             前置单个字符重现的正则表达式
    无符号              连续
    ^ $                 位置锚定
  
    扩展正则表达式
   
        字符匹配:
        . 点:匹配任意单个字符
    []  :匹配任意指定范围内的单个字符
    [^] :匹配任意指定范围外的单个字符
    
    次数匹配:
    *  :匹配其前面的字符任意次数
    ? :匹配其前面的字符一次
    +  :匹配其前面的字符至少一次
    \{m,n\}:匹配其前面字符最少m次,最多n次,n可以省略,表示最多无限次,m不可以省略
    
    位置锚定:
    ^ :锚定行首,表示此字符后面的字符串必须出现在行首
    $ :锚定行尾,表示此字符前面的字符串必须出现在行尾
    ^$:表示空白行
    \<:锚定词首,其后的任意字符必须作为单词的首部出现
    \>:锚定词尾,其前的任意字符必须作为单词的尾部出现
    
    分组:
    \( \):将其内的字符串作为一个整体看待
    \n:调用前面第n个左括号以及与之对应的右括号没有匹配到的内容
         
    或者:| 交替
    例如 C|cat 表示C或者cat
    使用 | 时,左边的会一直扩展到运算符的左边,右边的也会一直扩展到运算符的右边
    有事 | 需要使用转义符\转义,否则可能会被理解成管道
    root@ubuntu:/home/linlin/linlin/shell# egrep root\|linlin /etc/passwd
    root:x:0:0:root:/root:/bin/bash
    linlin:x:1000:1000:linlin's ubunto,,,:/home/linlin:/bin/bash
    
    ERE运算符优先级,由高至低
    [..] [==] [::]      用于字符对应的方括号符号
    \                   转义字符
    []                  方括号表达式
    \(\)  \n            分组与后向引用
    * + ? {}           重复前置的正则表达式
    无符号              连续
    ^ $                 位置锚定
    |                   交替
    
    POSIX字符集:
        [:alnum:]    数字字符
        [:alpha:]    字母字符
        [:blank:]    空格或Tab
        [:cntrl:]    控制字符
        [:digit:]    数字字符
        [:graph:]    非空格字符
        [:lower:]    小写字母字符
        [:upper:]    大写字母字符
        [:print:]    可显示字符
        [:punct:]    标点符号字符
        [:space:]    空白字符
        [:xdigit:]   十六进制数字
       
    使用正则表达式会有多少文本被匹配?
    正则表达式匹配可以匹配整个表达式的输入文本中最长的,最左边的子字符串,除此之外,匹配
    的空字符串(null),则被认为是比完全不匹配的还长。POSIX标准指出,完全一致的匹配,指的是
    自最左边开始匹配,针对每一个子模式,由左至右,必须匹配到最长的可能字符串。
    
   
查找与替换

文本查找
传统上,查找整个文本有有三种方式:grep egrep fgrep
    grep:根据基本正则表达式定义的模式搜索文档,并将符合模式的文本行显示出来
        注意:搜索时属于部分搜索,只要某一行有某一部分符合模式,就会被显示出来
        模式:pattern,文本字符和正则表达式的元字符组合而成的匹配条件
    grep 选项 模式 文件
    -i:忽略大小写
  --color:高亮显示
  -v:显示没有被模式匹配的行
  -o:只显示被模式匹配到的字符串
  -E:扩展正则表达式,等于 egrep
  -F:使用固定字符串进行匹配,等于 fgrep
  
  egrep:等于 grep -E
  
  fgrep:不支持正则表达式,但搜索速度更快      
   
文本替换
    sed:流编辑器(Stream Editor),以批处理的方式而不是交互的方式来编辑文件
       它是文本处理中非常中的工具,能够完美的配合正则表达式使用。处理时,把当前处理的行存储
       在临时缓冲区中,称为“模式空间”(pattern space),接着用sed命令处理缓冲区中的内容,处
       理完成后,把缓冲区的内容送往屏幕。接着处理下一行,这样不断重复,直到文件末尾。文件内
       容并没有改变。Sed主要用来自动编辑一个或多个文件;简化对文件的反复操作;编写转换程序等。

  模式空间:在内存中
  
  语法格式:sed [选项] '地址(对哪些行进行处理)命令' FILE(s)
  
  sed的工作方式相当直接。命令行上的每个文件名会被依次打开与读取,如果没有文件,则使用标准输入
  文件名 “—” (单个破折号)可用于表示标准输入
  

  常用选项:
  -n:静默模式,不再默认显示模式空间内的内容
  -i:直接修改原文件
  -e  脚本  -e 脚本……:使用多个脚本
  -f  /文件/to/脚本/:指定一个文件,文件每一行都是脚本(地址,命令)
  -r:使用扩展正则表达式

  表示:
  1.地址:起始行,结束行
  例如,1,100
  2.命令:使用正则表达式指定的模式
  3.模式1,模式2  表示第一次被模式1匹配到的行开始到第一次被模式2匹配到的行结束,这中间的所有行
  4.行数,处理指定的行
    $表示最后一行,$-n表示倒数第n+1行
  5.起始行,+n  
  表示从起始行与其开始向后的n行,共n+1行

  命令:
  a\ 在当前行下面插入文本。
  i\ 在当前行上面插入文本。
  c\ 把选定的行改为新的文本。
  d 删除,删除选择的行。
  D 删除模板块的第一行。
  s 替换指定字符
  h 拷贝模板块的内容到内存中的缓冲区。
  H 追加模板块的内容到内存中的缓冲区。
  g 获得内存缓冲区的内容,并替代当前模板块中的文本。
  G 获得内存缓冲区的内容,并追加到当前模板块文本的后面。
  l 列表不能打印字符的清单。
  n 读取下一个输入行,用下一个命令处理新的行而不是用第一个命令。
  N 追加下一个输入行到模板块后面并在二者间嵌入一个新行,改变当前行号码。
  p 打印模板块的行。
  P(大写) 打印模板块的第一行。
  q 退出Sed。
  b lable 分支到脚本中带有标记的地方,如果分支不存在则分支到脚本的末尾。
  r file 从file中读行。
  t label if分支,从最后一行开始,条件一旦满足或者T,t命令,将导致分支到带有标号的命令处,或者到脚本的末尾。
  T label 错误分支,从最后一行开始,一旦发生错误或者T,t命令,将导致分支到带有标号的命令处,或者到脚本的末尾。
  w file 写并追加模板块到file末尾。 
  W file 写并追加模板块的第一行到file末尾。 
  ! 表示后面的命令对所有没有被选定的行发生作用。 
  = 打印当前行号码。 
  # 把注释扩展到下一个换行符以前。 
  
  s/模式/字符串/修饰符:查找并替换,将每一行中能被模式匹配到的字符串转换成指定字符串,默认只替换每一行中第一次被模式匹配到的字符串
  修饰符
  g:全局替换
  i:忽略字符大小写
  s后面的分隔符不一定需要为\,也可以为其他内容,但每个分隔符需要一样,这样可以保证对文本内容中含有分隔符字符
  s\\\,s###
  \(\),\1,\2……后向引用
  &:引用模式匹配到的整个串,即表示模式匹配到的内容
  -n选项 与 p命令一起使用,表示只显示匹配到的行
  root@ubuntu:/home/linlin/linlin/shell# sed -n 's/root/ROOT/pg' /etc/passwd
  ROOT:x:0:0:ROOT:/ROOT:/bin/bash
  
  使用sed命令产生head命令的效果
  head -# FILE = sed -n '1,#p' FILE
  root@ubuntu:/home/linlin/linlin/shell# head -3 /etc/passwd
  root:x:0:0:root:/root:/bin/bash
  daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
  bin:x:2:2:bin:/bin:/usr/sbin/nologin
  root@ubuntu:/home/linlin/linlin/shell# sed -n '1,3p' /etc/passwd
  root:x:0:0:root:/root:/bin/bash
  daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
  bin:x:2:2:bin:/bin:/usr/sbin/nologin
  
awk:
  
  
文本文件惯例:
    文本文件处理:
        cut:选中特定字段显示
        join:将多个文件连接显示,
       
       
  
文本处理工具:
    cat tac uniq sort head tail jion wc tr od more less
   
    join命令
  功能:“将两个文件中指定栏位相同的行连接起来”,即按照两个文件中共同拥有的某一列,将对应的行拼接成一行。
  join [options] file1 file2
  注:这两个文件必须在已经在此列上是按照相同的规则进行了排序。
  join选项
  -a FILENUM:除了显示匹配好的行另外将指定序号(1或2)文件中部匹配的行显示出来
  -e EMPTY:将需要显示但是文件中不存在的域用此选项指定的字符代替
  -i :忽略大小写
  -j FIELD :等同于 -1 FIELD -2 FIELD,-j指定一个域作为匹配字段
  -o FORMAT:以指定格式输出
  -t CHAR :以指定字符作为输入输出的分隔符
  join 默认以空白字符做分隔符(空格和\t),可以使用 join -t $'\t'来指定使用tab做分隔符
  -v FILENUM:与-a相似 但值显示文件中没匹配上的行
  -1 FIELD:以file1中FIELD字段进行匹配
  -2 FIELD:以file2中FIELD字段进行匹配
  --help :打印命令帮助文件
  例子:
  文件 file1.txt
  aa 1 2
  bb 2 3
  cc 4 6
  dd 3 3
  文件file2.txt
  aa 2 1
  bb 8 2
  ff 2 4
  cc 4 4
  dd 5 5
  1.join file1.txt file2.txt
  输出:aa 1 2 2 1
        bb 2 3 8 2
  默认已两个文件的第一行做匹配字段,默认以空格(不限个数)做分隔符。
  2.join -j 1 file1.txt file2.txt
  输出:aa 1 2 2 1
        bb 2 3 8 9
  -j选项 指定了以两个文件中第一列做匹配字段 等同于join file1.txt file2.txt
  3. join -1 2 -2 3 file1.txt file2.txt
  输出: 1 aa 2 aa 2
         2 bb 3 bb 8
         4 cc 6 ff 2
         4 cc 6 cc 4
  以第一个文件的第二列和第二个文件的第三列做匹配字段。由于第二个文件中第三列的两个3 都与第一个文件中第三行因此输出
   4 cc 6 ff 2
   4 cc 6 cc 4
  4 join -o 1.1 -o 1.2? -o 1.3 -o 2.1 -o 2.2 -o 2.3?? -e 'empty' -a 1? file1.txt file2.txt?
  输出: aa 1 2 aa 2 1
      bb 2 3 bb 8 2
      cc 4 6 empty empty empty
         dd 3 3 empty empty empty
  -o 指定 将file1的1,2,3列,file2的1,2,3 列都输出。-a指定将file1中不匹配的行也输出,但是file2中没有与file1后两行对应的字段,因此使用empty补齐。

  5.join -v 1 file1.txt file2.txt
   输出: cc 4 6
          dd 3 3
  -v 1 将file1中不匹配的行输出
  PS:join命令和数据库中的join命令很类似。
   虽然file1和file2都已经排序,但是由于在第三行时开始不匹配因此只匹配上了前两行,后面的行虽然字段也可以对应但是以不能匹配。join命令,对文件格式的要求非常强,如果想要更灵活的使用,可用AWK命令,参加AWK实例

  6. join 标准输入
   有时我们需要将多个格式相同的文件join到一起,而join接受的是两个文件的指令,此时我们可以使用管道和字符“-"来实现
  join file1 file2 | join - file3 | join - file4
  这样就可以将四个文件 连接到 一起了。
   
重新格式化段落:
    fmt: 重新格式化段落,供用户切分段落,是文本行数不要超出我们看的到的屏幕范围。
    常用选项:
        -s:仅切割较长的行,但不会将短行结合成较长的行
        -w #:设置输出行宽为n个字符,默认为75个左右

   
管道的神奇魔力:
    管道:把一个命令的输出作为另一个命令的输入
     体现了Linux的基本思想之一:组合小命令完成复杂任务
    使用格式:
        命令1 | 命令2 | ......
        例如,取出某文件的倒数第3行
        root@ubuntu:/home/linlin/linlin/test# cat file
      10086
      213
      3134
      21343452
      2341
      root@ubuntu:/home/linlin/linlin/test# tail -3 file | head -1
      3134

示例:统计一篇文章中的单词出现次数
tr -cs A-Za-z\' '\n' |             将非字母字符置换成换行符号
  tr A-Z a-z |                     所有大写字母转为小写
    sort |                         由小到大排序单词
      uniq -c |                    去除重复,并显示其计数
        sort -k1,lnr -k2 |         计数由大到小排序后,在按单词由小到大排序
          sed ${1:-25}q            显示前n行
         

变量,判断,重复动作:

变量与算术:
    变量管理:readonly,export
    export:用于修改或打印环境变量
    root@ubuntu:/usr# type export
    export is a shell builtin
    readonly:是变量变为只读,变量值无法修改
    语法格式:export [-p] name[=word] ...
              readonly [-p] name[=word] ...
    选项:
        -p:打印命令的名称以及所有被导出(只读)变量的名称与值,这种方式可使Shell重新读取输入,以便重新建
            立环境(只读设置)
    行为模式:使用-p选项,这两条命令都会分别的打印它们的名称以及被导出的或只读的所有变量与值,否则,把适
              当的属性应用到指定的变量。
    较为常见的是export命令,其用法是将变量放进环境里。环境是一个名称与值的简单;列表,可供所有执行中的程
    序使用,新的进程会从其父进程继承环境,也可以在建立新的子进程之前修改它。
    export也可用于显示当前环境。
   
    env:从程序的环境中删除变量
    语法格式:env [-i] name [command_name [arguement]]
    当command_name被env执行时,可针对command_name继承而来的环境有更细致的控制
    选项:
        -i:用来初始化环境变量,丢弃任何继承值,仅传递命令行上指定的变量给程序使用
    为提供command_name时,显示环境中所有变量的名称与其值,否则,在命令行上使用变量赋值,在引用command_name
    之前,以修改继承的环境,且只是用所提供的变量与值
    警告:打印时,env不会正确的为环境变量值加上引号,以供重新输入到Shell中,如果需要此功能,使用-p选项
   
    unset:从执行中的Shell中删除变量与函数,默认情况下,它会解除变量设置,也可以加-v来完成.
    选项:
        -f:删除(解除)指定函数
        -v:删除(解除)指定变量
   

参数展开:
    参数展开是Shell提供变量值在程序中使用的过程。
    默认情况下,未定义的变量会展开为null(空字符串)。
   
展开运算符:
    替换运算符:
      ${varname:-word}  如果varname存在且非null,则返回其值,否则返回word。
                        常用于如果变量未定义,则返回固定值  
      linlin@ubuntu:/usr$ testpar=world
    linlin@ubuntu:/usr$ echo ${testpar:-4}
    world
    linlin@ubuntu:/usr$ testpar1=
    linlin@ubuntu:/usr$ echo ${testpar1:-4}
    4
    
    ${varname:=word} 如果varname存在且不是null,则返回它的值,否则设置它为word,并返回其值
                     常用于如果变量未定义,则设置变量未默认值
     linlin@ubuntu:/usr$ echo ${testpar1:=4}
    4
    linlin@ubuntu:/usr$ echo $testpar1
    4

     ${varname:?message} 如果varname存在且非null,则返回它的值,否则显示varname:message,并
                         退出当前命令或脚本,省略message会出现默认信息parameter null or not set
                         注意:在交互式Shell下不需要退出(在不同Shell间会有不同的行为,用户需自行注意)
                         常用于捕捉由于变量未定义导致的错误
     linlin@ubuntu:/usr$ varname=
    linlin@ubuntu:/usr$ echo ${varname:?}
    bash: varname: parameter null or not set
    linlin@ubuntu:/usr$ echo ${varname:?undefine}
    bash: varname: undefine
    
    ${varname:+word} 如果varname存在且非null,则返回word,否则返回null
                     常用于测试变量是否存在
    linlin@ubuntu:/usr$ echo ${varname:+exist}

    linlin@ubuntu:/usr$ varname=define
    linlin@ubuntu:/usr$ echo ${varname:+exist}
    exist
    以上每个运算符内的冒号(:)都是可选的,如果省略冒号,则将每个定义中的"存在且非null"部分改为"存在",也就是
    说,用算符仅用于测试变量是否存在。
   
   
    模式匹配运算符:
        ${variable#pattern} 如果模式匹配于变量值的开头处,则删除匹配的最短部分,并返回剩下的部分。
        ${variable##pattern} 如果模式匹配于变量值的开头处,则删除匹配的最长部分,并返回剩下的部分。
        ${variable%pattern} 如果模式匹配于变量值的结尾处,则删除匹配的最短部分,并返回剩下的部分。
        ${variable%%pattern} 如果模式匹配于变量值的结尾处,则删除匹配的最长部分,并返回剩下的部分。
       
   
    位置参数:
        位置参数是指Shell脚本的命令行参数,同时也表示在Shell函数内的函数参数,它们的名称是以单个的整数来命名
        $1 $2 ... $n 当这个参数大于9时,就应该用花括号{}括起来
       
        $#:表示传递到Shell脚本或函数的参数总数。
        while [ $# != 0 ]      以shift逐渐减少$#,循环将会终止
        do
          case $1 in
          ...                  处理第一个参数
          esac
          shift                移开第一个参数
        done
       
        $*,$@:一次表示所有命令行参数,这两个参数可用来把命令行参数传递给脚本或函数所执行的程序
       
        "$*":将所有命令行参数视为单个字符,也就是单独字符串,等同于"$1 $2 ..." $IFS的第一个字符用来作为分隔
        符,以分隔不同的值来建立字符串。
        root@ubuntu:/home/linlin/linlin/shell# ./canshu.test.sh linlin wangml
    The arguement were linlin wangml
    root@ubuntu:/home/linlin/linlin/shell# cat canshu.test.sh
    #!/bin/bash
    #
    printf "The arguement were %s\n" "$*"
    
    "$@":将命令行参数视为单独的个体,也就是单独字符串,等同于"$1" "$2"...
          这是将参数传递给其它程序的最佳方式,因为它会保留所有内嵌在每个参数里的任何空白
   

root@ubuntu:/home/linlin/linlin/shell# set -- hi
root@ubuntu:/home/linlin/linlin/shell# set -- hello "hi there" greetings
root@ubuntu:/home/linlin/linlin/shell# echo there are $# total arguements
there are 3 total arguements
root@ubuntu:/home/linlin/linlin/shell# for i in $*
> do echo i is $i
> done
i is hello
i is hi
i is there
i is greetings
root@ubuntu:/home/linlin/linlin/shell# for i in $@
> do echo i is $i
> done
i is hello
i is hi
i is there
i is greetings
root@ubuntu:/home/linlin/linlin/shell# for i in "$*"
> do echo i is $i
> done
i is hello hi there greetings
root@ubuntu:/home/linlin/linlin/shell# for i in "$@"
> do echo i is $i
> done
i is hello
i is hi there
i is greetings
root@ubuntu:/home/linlin/linlin/shell# shift
root@ubuntu:/home/linlin/linlin/shell# echo there are $# total arguements
there are 2 total arguements
root@ubuntu:/home/linlin/linlin/shell# for i in "$@"
> do echo i is $i
> done
i is hi there
i is greetings


    
  
  特殊变量:
      #            目前进程的参数个数
      @            传递给当前进程的命令行参数,置于双引号内会展开为个别的参数
      *            当前进程的命令行参数,置于双引号内,则展开为以单独参数
      -(连字号)    在引用时给予Shell的选项
      ?           前一命令的退出状态
      $            Shell进程的进程编号
      0(零)       Shell进程的名称
      !           最近一个后台命令的进程编号,以此方式储存进程编号,可通过wait命令以供稍后使用
      ENV          一旦引用,则仅用于交互式Shell中,$ENV的值时可展开的参数,结果应为要读取和在启动时
                   要执行行的一个文件的完整路径名称,这是一个XSI必须的变量
      HOME            根(登录)目录
      IFS             内部的字段分隔器,例如:作为单词分隔器的字符列表,一般设为空格,制表符以及换行符
    LANG            未设置任何LC_***变量时使用默认值
    LC_ALL          用来覆盖掉所有其他LC_***变量的值
    LC_COLLATE      使用指定地区的排序规则
    LC_CTYPE        使用所指定地区的字符集(字母,数字,标点符号等)
    LC_MESSAGES     使用指定地区的响应与信息,仅POSIX适用
    LC_MONETYPE     使用所指定地区的货币格式
    LC_NUMERIC      使用所指定地区的数字格式
    LC_TIME         使用所指定地区的日期与时间格式
    LINENO          刚执行过得行在脚本中的行编号
    NLSPATH         在LC_MESSAGES(XSI)所给定的信息语言里,信息目录的位置
    PATH            命令的查找路径
    PPID            父进程的进程编号
    PS1             主要的命令提示字符串,默认为$
    PS2             行继续的提示字符串,默认为>
    PS4             以set -x设置的执行跟踪的提示字符串,默认为+
    PWD             当前工作目录
   
   
    算术展开:
        Shell的算术运算符与C语言里的差不多,优先级与顺逆也相同
  

()
++ --
+ - ! ~
* / %
+ -
<< >>
< <= > >=
== !=
&
^
|
&&
||
?:
= += -= *= /= %= $= ^= <<= >>= |=


            
退出状态:
    每一条命令,不管是内置的,Shell函数,还是外部的,当他退出时,都会返回一个小的整数值给引用它的程序,这就是
    程序的退出状态。

    一般来说,退出状态为0表示退出成功。其他任何状态都表示它的退出状态是失败。
    可以使用内置变量$?来访问它。
   
    POSIX的结束状态:
    0            命令成功退出
    >0           再重定向或单词展开时失败
    1-125        命令不成功退出,特定的退出值的含义是由单独的命令定义的  
    126          命令找到了,但文件无法执行
    127          命令找不到
    >128         命令因收到信号而死亡
   
逻辑的
    非NOT: ^
    与AND: &&
    或OR: ||

test: 测试Shell脚本里的条件,通过推出状态返回其结果。
用法:
    test [ expression ] 或 [ [ expression ] ]
    注意空格
test表达式:是则为真
    字符串测试:
      string     测试字符串不为空
      -n string  测试字符串非null
      -z string  测试字符串为null
      s1 = s2    两字符串相同
      s1 != s2   两字符串不相同
  
  文件测试:
      -b file    文件是块设备
      -c file    文件是字符设备
      -d file    文件是目录
      -e file    文件是一般文件
      -g file    文件有设置它的setgid位
      -u file    文件有设置它的setuid位
      -h/L file  文件是符号链接
      -p file    文件是管道文件
      -r file    文件可读
      -w file    文件可写
      -x file    文件可执行,或者是可被查找的目录
      -s file    文件是socket
      -s file    文件不是空的
      -t n       文件描述符指向一终端
       
    整数测试:
      n1 -eq n2  整数n1 n2相等
      n1 -ne n2  整数n1 n2不相等
      n1 -lt n2  n1小于n2
      n1 -gt n2  n1大于n2
      n1 -le n2  n1小于等于n2
      n1 -ge n2  n1大于等于n2
   

在测试多个条件是否成立时可以使用 -a(做逻辑AND)
在测试在多个条件中是否有某一个成立时可以使用 -o(做逻辑或)
-a -o是test运算符,它们与&& ||(Shell运算符)之间有一个差异点
if [ -n "$str" -a -f "$file" ]
if [ -n "$str" ] && [ -f "$file" ]判断语句:
if 判断条件
then
语句
[elif]
[语句]
...
[else
语句]
fi

#!/bin/bash
if [ $# -eq 0 ]
then
echo "Error"
echo "You can use this COMMAND like Command file_name"
exit 4
fi TYPE=`file $1 | cut -d' ' -f2`
echo $1 is a $TYPE
case语句:
case 测试值 in
选项1)
...
;;
选项2)
...
;; ...
*)
...
esac

#!/bin/bash
# This is a simple test for case
#
case $# in
0)
echo "No option"
;;
1)
echo "You used $# option"
;;
2)
echo "You use $# options"
;;
*)
echo "Options is too ..."
esac循环语句:
for循环:
for I in 参数可能性列表
do
循环体
done

#!/bin/bash
#
declare -i SUM=0;
declare -i I; for I in {1..100}
do
let SUM+=$I;
done

echo "SUM = $SUM"

列表到的生成:
{m..n}: m到n的整数列表
seq 起始数 步径长度 结束数:默认步径长度为1,起始数为1,起始数、步径长度可以省略,结束数不能省略

while循环:
while 循环条件
do
循环体
dione

#!/bin/bash
#
declare -i SUM=0;
declare -i I=1; while [ $I -le 100 ]
do
let SUM+=$I
let I++
done echo "SUM = $SUM"


until循环:
until 循环条件
do
循环体
done

#!/bin/bash
#
echo "Enter username: "
read user
until who | grep "$user" &> /dev/null
do
sleep 30
done echo "$user is on"


     
    对于while循环,只要满足循环条件,就会继续循环
    对于until循环,只要循环条件未成功结束,就会执行循环
    实际上until循环比while循环用的少,不过如果是在等待某个事件的发生,它就很有用了
   

break: 用于退出循环
continue:用于提早开始下一段重复的循环
break和continue命令都接受可选的数值参数,可分别用来指出要中断或继续多少个被包含的循环。

shift:它用来处理命令行参数时,一次向左位移一位(或更多位),在执行shift时,原来的$1会消失,被$2取代,$2被
       $3取代,以此类推,而$#的值也会逐次减少,shift还接受一个可选参数,也就是一次移动几位,默认为1
      

getopts: 简化参数处理,让Shell脚本可以匹配POSIX参数处理惯例。
    语法:getopts option_spec variable [arguments...]
    行为模式:它在被重复调用时,会依次通过给定的命令行参数或者未提供则是"$@",在--或第一个非选项参数处,
          或是碰到错误时,会以非零值退出。option_spec用来描述选项及它们的参数。
          对每个合法的选项,设置variable为选项字母,如果选项有个参数,则参数值会置于OPTARG里。在处理结尾
          处,OPTIND会设置为第一个非选项参数的编号。
         
         
函数:指一段单独的程序代码,用以执行一些定义完整的单项工作。
      函数在使用之前必须先定义,这可通过在脚本的起始处,或是将它们放在另一个独立文件里且以点好(.)命令来取
      用(source)它们。
 

#!/bin/bash
#
foo() {
echo "This is a testing program for function!"
} foo
linlin@ubuntu:~/linlin/shell$ ./function.test.sh
This is a testing program for function!

return:
    返回由Shell函数得到的退出值给调用它的脚本。
    语法:return [exit_value]      
    行为模式:如果未提供参数,则使用默认退出状态,也就是最后一个执行的命令的退出状态。
              如果本意就是需要最后一个命令的退出状态,则严谨的Shell函数写法为:return $?
    警告:有些Shell允许再脚本里使用return,但如果用于函数体之外,则视为等同于exit。这种用法并不
          建议,因为可能出现可移植性问题
   

输入、输出、文件与命令执行

    标准输入、标准输出、标准错误输出
   
    read:将信息读入一个或多个Shell变量
    语法格式:read [-r] 变量名
    选项:
        -r:原始读入,不做任何处理,不将结尾结尾处的反斜杠解释为续行字符
    行为模式:自标准输入读取数据后,通过Shell字段分隔的功能(使用$IFS)进行切分,第一个单词赋给第一个变量,第二
              个单词赋给第二个变量,以此类推。如果单词多与变量,则剩下的单词,全赋给最后一个变量。read一旦遇到
              文件结尾(end-of-file),会以失败值退出。
              如果输入行以反斜杠结尾,则read会丢弃反斜杠和换行字符,然后继续读取下一行。如果有-r选项,那么read
              就会以字面含义读取最后的反斜杠。
              linlin@ubuntu:~$ read TOM
       cat
       linlin@ubuntu:~$ echo $TOM
       cat
       linlin@ubuntu:~$ read JERRY MARRY
       dog pear apple
       linlin@ubuntu:~$ echo $JERRY
       dog
       linlin@ubuntu:~$ echo $MARRY
       pear apple
 
    警告:当将read使用在管道时,许多Shell会在一个分开的进程内执行它,在这种情况下,任何以read设置的变量,都不会
          保留它们在父Shell里的值,对管道中间的循环,也是这样。
         
    read最典型的用法是处理/etc/passwd文件,可以使用简单的循环逐行处理:
    #!/bin/bash
  #
  while IFS=: read USER PASS UID GID FULLNAME HOMEDIR SEHLE
  do
    ... 处理每个用户
  done < /etc/passwd
  这个循环并不是说“当IPS等于冒号时,便读取。。。”,而是通过IFS设置,让read使用冒号作为分隔字符,而并不影响IFS
  的值,它只改变read所继承的环境内的IFS值。
  当遇到文件尾时,read会以非零值退出,这个操作会使while循环终止。
  将/etc/passwd的重定向放置于循环体的结尾有点奇怪,不过这是必须的,这样才能使read在每次循环的时候看到后续的行,
  如果循环写成:
    while IFS=: read USER PASS UID GID FULLNAME HOMEDIR SEHLE < /etc/passwd
    do
      ... 处理每个用户
    done
    循环就永远不会终止,每次循环时,Shell都会在打开/etc/passwd一次,且read只读取文件的第一行。
    这种循环还有另一种替代的方式,就是在管道里把cat和循环一起使用:
       cat /etc/passwd |
      while IFS=: read USER PASS USERID GROUPID FULLNAME HOMEDIR SEHLE
      do
        处理方式
      done
    小技巧:任何命令都能用来将输入通过管道传送给read。当read用在循环中时,这个方法格外有效。
   
    Shell在处理重定向时,由左至右。
      make 2>&1 > results
      上述命令,Shell会先传送标准错误信息到文件描述符1,然后文件描述符1(标准输出)被改为results
  
    Shell会在文件描述符重定向之前处理管道,将标准输出和标准错误输出都传递到相同的管道。
    make 2>$1 | ...
   
   
    exec: 以新的程序取代Shell,或改变Shell本身的I/O设置
    语法格式:exec [ program [ arguments... ] ]
    行为:搭配参数 - ,也就是使用指定的程序取代Shell,以传递参数给它。如果只是用I/O重定向,则会改变Shell
    本身的文件描述符。
    搭配参数,exec还可以在当前Shell下执行指定的程序,即Shell在其当前进程中启动新程序。
   

波浪号(~)展开:
    如果命令行字符串的第一个字符为波浪号,或变量指定的值里任何未被引号括起来的冒号之后的第一个字符为波浪号
    时,Shell便会执行波浪号展开。
    波浪号展开是要讲用户根目录的符号型表示方法,改为实际的目录路径,可以直接或间接地方式指定执行此程序的用
    户,如未显示的指定,则为当前用户。
    linlin@ubuntu:~/linlin/shell$ ls ~
  Desktop  Documents  Downloads  examples.desktop  linlin  Music
  linlin@ubuntu:~/linlin/shell$ ls /home/linlin
  Desktop  Documents  Downloads  examples.desktop  linlin  Music
  

expr:主要用于Shell算术运算,由于expr的语法十分麻烦,运算数和运算符必须是单个的命令行参数,因此建议使用
      大量的空格间隔它们。很多的expr的运算符同时也是Shell的meta字符,所以必须小心的使用引号。
     
      expr的运算度:(优先级从小到大)  
        e1 | e2      如果e1的值是非0或非null,则使用它的值,否则如果e2的值是非0或非null,则使用它的值.如果
                     两者都不是,则最后值为0
        e1  &  e2    如果e1与e3的值都是非0或非null,则使用e1的值,否则为0

=
!=
<
<=
>
>=
+
-
*
/
%
e1 : e2 e1与e2的BRE匹配
( expression )
integer 一个只包含数字的数目,允许前置负号,但却不一定支持一元的正号
string 字符串值,不允许被勿用为数字或运算符


       
引用:用来防止Shell将某些你想要的东西解释成不同的意义。
    引用的方式:
        反斜杠转义( \ ):
            用来告知Shell该字符即为其字面上的意义。这是引用单一字符最简单的方式。
      linlin@ubuntu:~/linlin/shell$ echo here is a real star: \* and a real question mark: \?
      here is a real star: * and a real question mark: ?
      linlin@ubuntu:~/linlin/shell$ echo here is a real star: * and a real question mark: ?
      here is a real star: case.test.sh for.test.sh function.test.sh if_else.test.sh and a real question mark: ?
    
    单引号( '...' ):
        强制Shell讲一对引号之间的所有字符都看做其字面上的意义。Shell脚本会删除这两个引号,只留下被
        括起来的完整文字内容
        linlin@ubuntu:~/linlin/shell$ echo $?
      0
      linlin@ubuntu:~/linlin/shell$ echo '$?'
      $?
      不可以在一个单引号引用的字符串里再内嵌一个单引号,即便是反斜杠在单引号里也没有特殊意义,
      
     双引号( "..." ):
            就像单引号那样,将括起来的文字视为单一字符串。只不过,双引号会确切的处理括起来文字中的转义
            字符、变量、算术和命令替换。
      linlin@ubuntu:~/linlin/shell$ USERNAME=kattry
      linlin@ubuntu:~/linlin/shell$ echo "The name of the is $USERNAME!"
      The name of the is kattry!
      在双引号里,字符 $ " ` \ ,如果需要用到字面上的意义,都必须使用反斜杠转义。任何其他字符前面
      的反斜杠时不带特殊意义的。
    
    一般来说,使用单引号时,是你希望完全不处理的地方。否则,当希望将多个单词视为单一字符串,但又需要
    Shell为你处理部分数据时,最好使用双引号。

Shell对每个命令执行的各项操作顺序:
    1.将命令分割成token,以固定的一组meta字符分隔,有空格,换行字符,制表字符,分号,逗号,尖括号,| &。token的
    种类包括单词,关键字,输入、输出重定向器。
    2.检查第一个token,如果是开放的关键字(if与其他控制结构的开始符号,如 { 或() ,则这个命令其实是一个复合命令,
    Shell为复合命令进行内部的设置,读取下一条命令,并再次启动进程。如果关键字非复合命令的开始符号,则Shell会发出
    语法错误信号。
    3.将每一个命令的第一个单词与别名列表对照检查,如果匹配,它便代替别名的定义,回到步骤1,否则进行步骤4,此处需
    要注意的是Shell不会执行递归的别名展开,反而将别名展开为相同的命令时它会知道,并停止潜在的递归操作。
    4.波浪号展开
    5.变量替换
    6.命令替换
    7.算术表达式替换
    8.对展开的文本进行单词分割,使用$IFS里的字符作为定界符
    9.通配符展开
    10.命令查找: 特殊內建命令,函数,內建命令,可执行文件
   
eval语句:告知Shell取出eval参数,并再执行它们一次,使它们进过整个命令行的处理步骤。
   
subShell:
    一组被括在圆括号里的命令,这个命令会在另外的进程中执行(POSIX的标准说法是subShell环境,即命令不需要真的在
    另外的进程中执行)。
    将某个目录树复制到另一个地方:
        tra -cf - . | (cd /newdir; tar -xpf -)
   
代码块:
    在概念上与subShell类似,只不过它不会建立新的进程。代码块里的命令以花括号括起来,且对主脚本的状态会造
    成影响(例如它的当前目录)。一般来说,花括号被视为Shell关键字,它们只有在出现命令的第一个符号时会被识别
    实际上,必须将结束花括号放在换行字符或分号之后。
    例如:cd /some/dir || {
            echo something >&2
            exit 1
          }

要使用subShell还是代码块需要根据个人喜好,主要差异是代码块会与主脚本共享状态。这么一来,cd命令会对主脚本造成影
响,也会对变量赋值产生影响。如果你需要将一组命令括起来执行,而不影响主脚本,则应该使用subShell。否则使用代码块。

內建命令:
    :      只做参数展开
    .       读取文件并在当前Shell中执行它的内容
    alias   设置命令或命令行的捷径
    unalias 删除已定义的别名
    bg      将工作置于后台
    break   从循环中退出
    continue跳到下有一个循环
    eval    将参数当做命令行处理
    exec    以给定的程序取代Shell或或为Shell改变I/O
    exit    退出Shell
    export  建立环境变量
    false   表示失败状态
    true    表示成功状态
    fc      与命令历史一起使用
    fg      将后台工作置于前台
    getopts 处理命令行的选项
    jobs    列出后台工作
    kill    传送信号
    pwd     打印工作目录
    read    从标准输入读取一行
    readonly让变量为只读模式
    return  从函数中返回
    set     设置选项或位置参数
    unset   删除变量或函数定义
    shift   位移命令行参数
    times   针对Shell或其子Shell,显示用户与其系统CPU时间的累计
    trap    设置信号捕捉程序
    umask   掩码
    wait    等待后台工作完成
   
产生脚本:
   
    路径查找:   
       
    IFS默认值:空格,定位字符,换行字符
   

好习惯:

1.由于密码文件是所有人都可读取,任何由此文件导出的数据也是如此,因此事实上,我们并不需要限制这个程序中间
文件的访问权限。不过,对于我们绝大多数处理的都是敏感性数据,所以在开放程序时应养成一个好习惯:仅允许需要
用到这个文件的用户或进程访问它,我们可以把umask重新设置为077      

2.为了了解任务且便于调试,让临时性文件具有部分共通名称会比较方便,这样也可以因为这些文件弄乱当前目录:我
们在命名是将它们的文件名前都放置/tmp/pd。此外,为了避免程序的多个实例在同一时间执行时发生名称冲突,我们也
必须使其名称具有唯一性,在这里也就是使用进程编号:可以在Shell变量$$里使用,将其放置在结尾,可通过字尾区别
它们,因此我们定义这些Shell变量表示临时性的文件。
    PERSON=/tmp/pd.key.person.$$
当工作终止时,无论是正常或是异常终止,我们都要让临时文件消失:
使用trap命令:
    trap "exit 1"
    trap "rm -f $PERSON"
在开放时,我们将第二个命令加上注释,以便保留临时性文件以供稍后检查。

3.为了可移植性,POSIX标准里建议对多重条件使用Shell层级测试,而非使用-a -o 运算符

 

转载请注明出处