Shell 编程

Ubuntu Bash Shell 必备快捷键

命令 说明
Ctrl + A 相当于HOME键,用于将光标定位到本行最前面,回到命令行开始
Ctrl + E 相当于End键,即将光标移动到本行末尾,转到命令行尾
Ctrl + B 相当于左箭头键,用于将光标向左移动一格
Ctrl + F 相当于右箭头键,用于将光标向右移动一格
Ctrl + X + X 光标移动到最后和当前位置两个地方跳转
Ctrl + D 相当于Del键,如果命令行没有命令字符,就退出shell
Ctrl + H 删除光标所在位置之前的一个字符(H是vi里的向左的意思嘛)
Ctrl + K 删除光标所在位置之后(右边)的所有字符
Ctrl + U 删除光标所在位置之前(左边)的所有字符 (密码输入错误的时候比较有用)
Ctrl + W 用于删除当前光标左侧的一个单词
Ctrl + P 相当于上箭头键,即显示上一个命令
Ctrl + N 相当于下箭头键,即显示下一个命令
Ctrl + T 用于颠倒光标所在处字符和前一个字符的位置。(用于打错了2个字符吧)
Ctrl + J 相当于回车键
Ctrl + C 终止正在执行的命令
Ctrl + Z 挂起/停止正在执行的命令
Ctrl + L 清屏,类似 clear 命令
Ctrl + R 查找之前执行过的命令(Ctrl+Shift+R是反向查找)
Ctrl + Y 粘贴或者恢复上次的删除 (^k,^u等命令所删除的)
 
Alt + ? 显示命令补全的候选项(与Tab不同在于只显示不插入)
Alt + * 插入命令补全的所有候选项,可以快速输入文件夹下所有文件名
Alt + . 插入前一个命令的最后一个单词 (这个很好很强大)
Alt + B 光标往左移动一个单词
Alt + F 光标往右移动一个单词(诶,和显示菜单的快捷键冲突了)
Alt + C 将光标所在的字符变成大写
Alt + D 删除光标所在位置的单词
Alt + L 将单词中光标位置之后的字符变成小写
Alt + U 将单词中光标位置之后的字符变成大写
Alt + T 交换当前光标所在单词和上一个单词
Alt + Backspace 向前删除单词,与Ctrl+W类似,分隔符有些区别
 
Shift + Insert 插入选中的内容
Ctrl + Shift + C 复制到系统剪切板
Ctrl + Shift + V 从系统剪切板粘贴
Ctrl + Shift + T 新建标签页
Ctrl + Shift + W 关闭标签页
Ctrl + PageUp 前一标签页
Ctrl + PageDown 后一标签页
Ctrl + Shift + PageUp 标签页左移动
Ctrl + Shift + PageDown 标签页右移动

基本 Shell 命令

  • Bash Shell中的通配符

    • ? 匹配任意单个字符。
    • * 匹配任意字符串。
    • [set] 匹配set中的任意字符。[!set]是取反操作,匹配不在set中的任意字符,如:
      • [0-9] 匹配所有数字
      • [akz] 匹配a,k,z三个字符
      • [!abc] 匹配除a,b,c之外的所有字符
  • Bash Shel中的特殊符号:

    • # 注解符号:这个最常被使用在script当中,视为注释;
    • \ 逃脱符号:将『特殊字符或万用字符』还原成一般字符
    • | 管线(pipe):分隔两个管线命令的界定(后两节介绍);
    • ; 连续指令下达分隔符号:连续性命令的界定(注意!与管线命令并不相同)
    • ~ 使用者的家目录
    • $ 取用变量前置字元:亦即是变量之前需要加的变量取代值
    • & 工作控制(job control):执行命令时将进程放到后台执行[ dojob & ]
    • ! 逻辑运算意义上的『非』 not的意思!
    • / 目录符号:路径分隔的符号
    • >, >> 资料流 重导向 :输出导向,分别是『取代』与『累加』
      (注意tee命令:也可以实现重导向,同时还在standard out输出一份)
    • <, << 资料流 重导向 :输入导向(使用文件作输入 与 指定结束符)
      指定结束符功能:从标准输入中读取输入,直到遇到指定的结束符。
    • ‘ ‘ 单引号,不具有变量置换的功能的字符串
    • ” ” 具有变量置换的功能的字符串!
    • ` ` 两个『 ` 』中间为可以先执行的指令,亦可使用$( )
    • ( ) 在中间为子shell的起始与结束
    • { } 在中间为命令区块的组合!
  • shell中的简单的条件判断,利用短路求值。&?(命令传回码), &&, ||当使用cat adsfa && ll时,什么都不会执行,因为短路求值

  • 数据流重定向:

    • 标准输入:代码为0,使用<或<<;
    • 标准输出:代码为1,使用>或>>;
    • 标准错误输出:代码为2,使用2>或2>>。
    • 当同时重定向标准输出与标准错误输出时使用 > list.txt 2>&1&>list.txt
    • 双向重定向tee:tee会同时将数据流送到文件与屏幕! ls | tee log.log
    • 一个技巧:清空一个文件 > a.txt,将空定向到a.txt就把它清空了。
  • 管道命令pipe(|):

    • 仅会处理standard output,对于standard error output不能直接处理
    • 后一个命令需要能够接收前一个命令的输出作为输入才行
    • 号在管道中的重要作用:tar -cvf - /home | tar -xvf -,第一个”-“使用stdout代替输出文件,第二个”-“代替上一步所输出的stdout,并将这个输出流视为下次tar的输入文件。
  • Shell script是利用shell的功能所写的一个脚本程序,将一些shell语法与命令写在里面,搭配正则表达式、管道命令与数据流重定向等功能,以达到我们所想要的目的。

  • shell script是通过使用bash abc.sh或sh abc.sh来执行的 或者以#!/bin/sh 开始,然后赋予执行权限,再直接执行就可以了。

  • 写shell script的一个好的习惯就是在一开头就设置好执行需要的环境变量预先声明和设置,经常是指PATH和LANG这2个环境变量

  • shell script执行方式:在子进程中执行 和 在当前进程中执行

    • bash abc.sh 在子进程中执行shell
    • source abc.sh 在当前进程中执行shell
  • test测试命令:test -e /dmtsai && echo “exist” || echo “Not exist”;
    test 命令用于测试字符串,文件状态和数字。
    test 一般有两种格式,即:test condition 或 [ condition ] (使用方括号时,要注意在条件两边加上空格)
    []作为判断命令:[ “$HOME” == “$MAIL” ]
    • 中括号[]内的每一个组件都必须要有空格键来分隔
    • 中括号内的变量,最好都以双引号括起来
    • 中括号内的常量,最好都以单或双引号括起来
    • test命令非常的强大,支持各种类型的测试(是不是文件,是什么类型的文件,权限如何,文件新旧比较,字符串比较,数字比较等等)
    • 测试里还支持逻辑运算:使用-a, -o, !(&&, ||, !)运算符
  • expr命令:用于数值和字符串运算
    Usage: expr expression
    命令运算完毕后,除了会返回表达式运算的结果外,还会生成一个 expr 执行状态码表示 expr 的执行状态(0 : 结果不是 0 或 null; 1 : 结果是 0 或 null; 2 : 表达式无效)
    • expr 的 expression 中,运算符前后都要留一个空格,并且一般最好加上单引号。
    • expr 的 expression 中运算数只能是整数不能是小数。
    • expr 还支持常见的字符串运算
    • 字符串模式匹配:expr match STR REGEXP : STR 是字符串,REGEXP 是正则表达式。返回 STR 中匹配 REGEXP 的字符个数。
    • expr STR:REGEXP : 相当于 expr match STR REGEXP。
  • 条件判断式::

    if [ 条件判断式一 ]; then
        do A
    elif [ 条件判断式二 ]; then
        do B
    else
        do C
    fi
    
  • case语句:模式部分可以包括元字符: *、?、[ ] 表示一类字符或范围中任意字符。:

    case $1 in
      "hello")
          echo "Hello,  how are you?"
          ;;
      "")
          echo "Input nothing"
          ;;
       *)               #通配符,匹配其它的所有可能
          echo "others"
          ;;
     esac
    
  • function功能: shell中的函数也有声明的问题,所以也应该放在调用代码的前面:

    [function] doit(){
        echo "The argument is $1"      #这样使用参数一定要知道!
    }
    
    doit arg1
    
    向函数传递参数就像在一般脚本中使用特殊变量$1、 $2 … $9 和 $@ 一样。
    从调用函数中返回,可以有两种处理方式:
    • 让函数正常执行到函数末尾,然后返回脚本中调用函数的控制部分(默认方式)。
    • 使用 return 返回脚本中函数调用的下一条语句,可以带返回值。0 为成功,非 0 为有错误。
  • 循环while, until:

    while [ condition ]
    do
        Loop Statements
    done
    until [ condition ]
    do
        Loop Statements
    done
    
  • 循环for…do…done:

    for var in con1 con2 con3 …
    do
        Loop Statements
    done
    for ((初始值;限制值;执行步长))
    do
        Loop Statements
    done
    
  • 循环控制语句:

    • break 语句允许退出循环或 case 语句。 Usage: break [n] // 跳出[n 层]循环
    • continue 语句用于跳出当前本轮循环步,重新开始新一轮的循环步。
  • 有了顺序结构、选择结构、循环结构和函数功能的shell script就已经具备了C语言类似的结构化程序设计语言的能力了,理论上已经可以做任何想要的事了。

  • shell script读取脚本参数处理时有2个相关的主要命令:

    • shift:它每次将参数位置向左偏移(缺省1位) Usage: shift N // N 为一个数字

    • getopts:getopts 用于形成命令行处理标准形式。
      Usage: getopts option_string variable_name
      getopts 读取字符串 option_string,获知脚本可以使用的有效选项(在命令行中选项应该以”-“开头)。
      option_string 由字母和冒号组成,每一个字母就是一个有效选项。
      • 如果 option_string 中一个字母后跟一个”:”,表示该字母选项后应该有一个参数。
      • 如果 option_string 以”:”开头,表示当命令行中出现了无效选项时,getopts 不打印错误信息。
      • eg: getopts :ac:h optchar // 表示脚本文件在命令行下只可以接受选项-a -h -c(必须带参数),并且在传入无效参数时仅仅忽略而不会报警!
    • 正是由于Linux中有了自带的优秀处理参数的命令getopts,所以Linux中命令的相关格式就比较统一。

    • getopts 语句一般和 while 循环、case 结构联合使用。:

      while getopts :ac:h OPT
      do
        case $OPT in
          a) statements ;; // 当出现选项-a 时,要执行的语句
          c) statements ;; // 因为选项-c 要带参数,该参数由环境变量$OPTARG 指示
          h) statements ;; // 当出现选项-h 时,要执行的语句
        esac
      done
      
    • 每次 getopts 执行时,如果在命令行的第 x(x=1…n)个参数中匹配到字母选项,则环境变量 $OPTIND 的值为x+1。在命令行脚本启动时,$OPTIND 会被初始化为 1。

  • shell script的调试:sh [-nvx] myshellscript.sh (调试时多用-x参数)
    也可以在脚本开始时将 set 选项打开,然后在结束时关闭它。(set -n -v -x)
    • -n:不要执行script,仅查询语法的问题;
    • -v:在执行script前,先将script的内容输出到屏幕上;
    • -x:将使用到的script内容显示到屏幕上,这是非常有用的参数(-x会使得进行变量替换后的,真正被kernel执行的命令及其参数显示出来,所以这是相当的有用啊!相当于C/C++里的宏展开嘛,GCC也有这个编译器选项)!
  • 在script内,$0, $1, $2, … , $@都是有特殊意义的,比如前面的function里的取参数。

  • sort 用于文本文件数据内容排序
    usage: sort [sort 选项] [input_file ....]
    • 排序模式: 对输入文件进行排序,是默认的模式。
    • 合并模式: 对两个已排序的文件进行合并。需要指定”-m”选项。 sort -m [-o 输出文件] [选项] file1 file2
    • 检查模式: 测试给定的输入文件是否已排序。需要指定”-c”选项。
    • sort 的常用数据排序选项
      • -o : 指定输出文件。Eg: sort -o out.txt in.txt
      • -n : 当指定位置上是数字字符时,按数值大小来排序,而不是逐字符比较。
      • -b : 忽略前置空白。
      • -r : 颠倒输出排序的结果(即逆序输出)。
      • -d : 在排序时忽略所有除英文字母、数字及空白之外的字符。
      • -f : 在排序时将字母大小写视为相同。
      • -i : 在排序时忽略超过 ASCII 可打印范围(8 进制 040-0174)的字符。
      • -M : 对表示月份的三个大写字母进行比较,”无效名称” < “JAN” < “FEB” <…< “DEC”。
  • uniq: uniq 认为持续不断重复出现的行(中间不包括其它文本)才是重复行,因此,它和C++中的uniq很像,一般和sort搭配使用。

    • -u 只显示没有重复的行。不使用此选项时,uniq 会把连续重复行的内容显示一次

    • -d 只显示有重复数据行,每种重复行只显示其中一行。

    • -c 打印每一重复行出现次数。

    • -i 忽略字母的大小写

    • -fx x 为数字(x=1.2..),先跳过 x 个域再开始比较,与”-x”等效。有的系统使用”-nx”选项。

    • -sx 跳过 x 个字符后再开始比较(x=1.2..),与”+x”等效。与”-fx”连用时,一般放在”-fx”之后。
      uniq -f2 -s4 parts.txt // 从文件 parts.txt 行中的第 3 个字段第 5 字符开始执行
  • join: 寻找2个文件里相同的行,并输出这个相同的行(默认只取每一行的第一个字段)

    • -ax : 将文件编号为 x(x=1,2)的文件中未被匹配连接的行额外打印出来。
    • -o x.y[,x.y,…] : 在输出中只打印文件 x 的第 y(y=1,2…)个字段。
    • -j m : 指定两个文件都用第 m(m=1,2…)个字段作为 join 字段。
    • -j1 m 或 -1 m : 指定文件 1 使用第 m(m=1,2…)个字段作为 join 字段。
    • -j2 m 或 -2 m : 指定文件 2 使用第 m(m=1,2…)个字段作为 join 字段。
    • -t char: 指定 char 为输入输出字段的分隔符,省略时使用 shell 环境变量 IFS。
    • -i: 忽略字母的大小写。
    • -vx : 只打印文件 x(x=1,2)中未被匹配连接的行,而不打印连接的结果。
  • cut 命令主要用于选择性的打印输入文件行的部分内容。
    usage: cut [options] input_file(s)
    • 输入文件可以是一个文件列表。如果没有指定输入文件,或设置为”-“,将会使用标准输入为输入。
    • -d char : 指定 char 为输入行字段分隔符(预设使用 shell 环境变量 IFS)。输出使用相同的域分隔符。
    • -f 字段列表 : 只打印在字段列表中的字段。用数字表示要打印的字段,列表可以使用”-“和”,”。数字都是从 1开始编号。”-m”表示 1-m,”n-“表示从 n 到最末。
    • -c 字符列表 : 用数字列表指定要剪切的字符位置

    eg: echo "a b c d e g e f asd fas d" | cut -d ' ' -f 3,5 结果为:c e

  • paste 将两个输入文件的每一行连接成一个新行并输出。file1 的行内容位于 file2 之前。
    Usage: paste [options] [file1 file2]
    粘贴两个不同来源的数据时,首先需应该将其分类(即要求已经排序),并确保两个文件行数相同。
    • -d char : 指定 char 作为字段分隔符。例如用@分隔域,使用-d@。
    • -s: 将每个文件的内容作为一行输出,有 n 个输入文件就输出 n 行。
  • split 用来将大文件分割成小文件,split 预设为 100 行生成一个新的文件。
    Usage: split [选项] input-filename [输出文件前缀]
    • -x、-lx、–lines=x : 每 x 行生成一个新的输出文件
    • -b n、–bytes=n: 每 n 字节生成一个新的输出文件,可用后缀 b k m 表示以 block KB MB 为单位。
    • –verbose : 每生成一个新的输出文件时,就打印一行信息到标准错误。
  • tr 主要用来从标准输入中通过三种操作: 字符替换、压缩重复字符和删除字符,然后打印输出到标准输出。
    Usage: tr [OPTION] String1 [String2]  (string支持[a-z][a*n])
    tr 有 3 个常用选项(c d s),tr可以视为sed的一个极其简化版本,可以更简洁的实现sed的部分功能。
    • -c:用字符串1中字符集的补集替换此字符集,要求字符集为ASCII。
    • -d:删除字符串1中所有输入字符。
    • -s:删除所有重复出现字符序列,只保留第一个;即将重复出现字符串压缩为一个字符串。
  • Shell中的各种引号:

    • 双引号(” “):使用双引号可引用除字符 $(美元符号)、`(反引号)、\(反斜线) 外的任意字符或字符串。 双引号不会阻止shell 对这 3 个字符作特殊处理(标示变量名、命令替换、反斜线转义 )。
    • 单引号(‘ ‘):如果用单引号把字符串括起来,则单引号内字符串中的任何特殊字符的特殊含义均被屏蔽。
    • 反引号(` `):shell 将反引号中的内容作为一个系统命令,并执行其内容。使用这种方法可以替换输出为一个变量。
    • 反斜线(\):转义字符,与大多数编程语言中的转义字符相同。
  • tput 命令可以增强应用外观及脚本的控制 (显示丰富多彩的输出时使用)
    Usage: tput [-V] [-S] [-Ttermtype] capname
    tput 产生三种不同的输出:字符型、数字型和布尔型(真/假)。即: capname 参数
    • 大部分常用字符串:
      bel警铃;blink 闪烁模式;bold粗体;civis 隐藏光标;clear 清屏;cnorm 不隐藏光标;cup x y 移动光标到屏幕位置(x,y);el 清除到行尾;ell 清除到行首;smso 启动突出模式;rmso 停止突出模式;smul 开始下划线模式;rmul 结束下划线模式;sc 保存当前光标位置;rc 恢复光标到最后保存位置;sgr0 正常屏幕;rev逆转视图
    • 大部分常用数字输出:cols 列数目;ittab 设置宽度;lines 屏幕行数

    • 两种布尔操作符:chts 光标不可见;hs具有状态行

    • 所有控制字符均以一个转义序列033 开始,然后后紧跟字符[,最后是表示控制字符的实际序列,用于打开或关闭某终端属性
      eg: echo -e “033[40;32m” // 产生一个黑色背景加绿色前景色
  • eval 命令将会首先扫描命令行进行所有的变量置换,然后再执行该命令。该命令适用于那些一次扫描无法实现其功能的变量。该命令对变量进行两次扫描。

  • logger 向系统全局的日志文件 messages(/var/log 或/var/adm 目录下)中记录信息。
    Usage: logger [ -p pri ] [ -i ] message
    合理的用途就是用于脚本非正常退出时。如果希望向日志文件中发送消息,只要在捕获信号的退出函数中包含 logger 命令即可。
  • xargs 从管道提取信息作为参数给命令。
    Usage: xargs [opt] [ command [initial-arguments] ]
    他从stdin读取由空格分割的字符串(假设为arg0 arg1 … argN),并执行command [initial-arguments] arg0 arg1 … argN,如果参数太多的话,xargs保证参数大小在不超过系统限制的ARG_MAX bytes大小的前提下,一次或多次执行command命令。
    注意是作为command的最后一个参数运行的!
    因为find命令里使用CMD时在不同的系统中可能会参数太长溢出或新开线程太多而产生错误,使用xargs搭配find命令更加的好用和高效。
    • -i 选项告诉 xargs 用每项的名称替换{},同时限定每次只传递一个参数, ls | xargs -i mv {} {}.bak 进行同名备份。
    • -t 选项指示 xargs 先打印命令,然后再执行。
    • -n 选项限制单个命令行的参数个数, file * | cut -d":" -f1 | xargs -t -n 2 ls -ltr 限定每次只传递2个参数。
  • echo [-e][-n] STRING

    • -e Linux中的echo默认不解释STRING中的转义字符,除非加上此选项(解释转义字符的最常用用途就是打印彩色字吧)
    • -n 默认在输出STRING后会再输出一个换行,加了些选项将不输出换行
  • read varible1 varible2 varible3…
    注意当输入的单词数大于变量数时,read会把所有的超长部分都赋予最后一个变量。
  • 查看文件内容常用的 cat more less

    cat:简单的显示一个文件的内容
    more:分页查看文件内容
    less:分页可控制查看文件内容

    通俗点说:

    • cat一次性把文件内容全部显示出来,管你看不看得清,显示完了cat命令就返回了,不能进行交互式操作,适合察看内容短小、不超过一屏的文件;
    • more比cat强大一点,支持分页显示,你可以ctrl+B ctrl+F …..上下滚屏,但是不支持像shift+G(跳到文件尾)这种操作;
    • less比more更强大一点,支持各种命令,随便翻页、跳转、查找…..想怎么看,就怎么看,爱怎么看,就怎么看。
  • tee命令把一个副本送到标准输出,同时还拷贝到相应的文件中去
    Usage: tee -a filename (-a表示追加到文件尾,tee应与管道一起使用)
    Eg: who | tee -a a.log
  • 标准输入(文件描述符0),标准输出(文件描述符1),标准错误(文件描述符2)。如果打开了其它的文件进行输入输出,则其被设置为文件描述符3-9

  • 反转文件 tac, rev

    tac 反转文件的行序,使之从尾到头排列; rev 反转文件每行的内容,使之从行尾到行首倒转。

    >>> cat tt
    12345
    23456
    34567
    >>> tac tt
    34567
    23456
    12345
    >> rev tt
    54321
    65432
    76543
    

强力命令: find,grep,sed,awk

  • find 命令 :find命令是最强大的查找命令,几乎可以做到一切想做的事情。它太强大了,以至于它的参数有点复杂。
    Usage: find pathname -options [-print -exec -ok ...]
    path_name很好理解,它是指要搜索的根目录,默认情况下是当前路径。
    expression有点复杂,因为它支持的功能太多,弹性太大,所以它必须由4部分组成:
    • OPTION:指定搜索方式,一般放在开头,但是一般可以省略
    • TEST:指定检索条件,只有符合检索条件的才会被进行下一步的ACION处理。TEST支持的参数非常多,我看了下,常用的应该有以下几种:
      • -name, -lname, -iname, -ilname:i表未忽略大小写,l表示链接文件
      • -regex “…” :使用正则表达式查找文件
      • -type C:(bcdplsf)分别表示(块设备,字符设备,目录,命令,管道,符号链接,socket,正规文件)
    • ACTION:对TEST的结果执行的操作,默认为-print
      • -print:一行一行打印文件名
      • -ls:以ll命令的方式打印出来,包含丰富的信息
      • -exec CMD {} \; 把TEST的结果作为CMD命令的参数,并执行该CMD
      • -ok CMD {} \; 与-exec命令相同,但是在每次执行命令之前询问Y/N?
    • OPERATOR:对TEST或ACTION执行 “与,或,非” 操作,完整支持逻辑运算:
      • 与:EXPR1 EXPR2 或 EXPR1 -a EXPR2 或 EXPR1 -and EXPR2
      • 或:EXPR1 -o EXPR2 或 EXPR1 -or EXPR2
      • 非:! EXPR 或 -not EXPR
      • 支持括号():(EXPR)支持括号就有了一切复杂逻辑的可能。
  • grep 命令:Linux中使用最广泛的字符串处理命令,grep将找到并打印匹配模式的行!
    姐妹命令:egrep扩展grep, fgrep快速grep(查找字符串而不是模式)
    Usage:grep [OPTION] regular_expression [file_name ...]
    • 如果指定了file_name,grep将对这些文件的内容进行逐行匹配,否则对标准输入的内容进行匹配
    • OPTION
      • -c 只输出匹配的行数
      • -i 不区分大小写
      • -h 查询多个文件时,不显示文件名
      • -l 查询多个文件时,只显示文件名
      • -n 显示匹配的行及行号
      • -s 不显示不存在或无匹配文件等错误信息
      • -v 只显示不包含匹配模式的行(非常重要、常用!)
      • -r 递归地读取每个资料夹下的所有档案,此相当于 -d recsuse 参数。
      • -o 只输出匹配的部分(非常常用的)!
    • 常见的匹配模式的类名形式:
      • [[:upper:]] 等价于 [A-Z]
      • [[:lower:]] 等价于 [a-z]
      • [[:digit::]] 等价于 [0-9]
      • [[:alnum:]] 等价于 [0-9a-zA-Z]
      • [[:space:]] 等价于 空格或Tab键
      • [[:alpha:]] 等价于 [a-zA-Z]
  • sed 也是一个与awk非常类似的工具,可以看作是awk的功能简化版,它也是一行一行的处理文本。
    Usage: sed [OPTION] 'sed_command' [input_file]
    • OPTION:

      • -n sed 默认显示每一行,无论它是否将被处理,-n彻底不显示任何一行,除非明确被p出来的内容
      • -e 连接多条sed_command,”-e” 选项不常被大范围使用,更好的方法是用分号来分隔命令
      • -f 指定sed脚本文件名
      • -i[SUFFIX] 修改原文件,如果提供suffix,作备份(新版本支持!)
      • -r 使用扩展正则表达式
    • sed_command的格式为:[address] sed_edit_cmd:

      • address表示要编辑的行,缺省为所有的行,通过行号或正则表达式指定要改变的文本行

        • x 一行号
        • x,y 行号范围
        • /pattern/ 查询包含模式的行
        • /pattern/pattern/ 查询包含两个模式的行
        • /pattern/,x 在指定行号上查询匹配模式的行
        • x,/pattern/ 通过行号和模式查询匹配行
        • x,y! 查询不包含行号x,y的行
        • $ 表示最后一行
      • sed_edit_cmd 表示对要进行编辑的行需要执行的命令:

        • d 删除行

        • p 打印文本(注意会覆盖-n选项,这非常重要)

        • ! 对所选行之外的所有行应用命令

        • = 打印行号

        • s 替换命令( 's/^west/north/g' )注意分隔符不必是’/’可以是换行符和反斜线外的任何字符( s#^west#north#g
          [gpw]参数:g指全部替换(缺省只第1次替换匹配);p显示被替换后的行;w将被替换后的行内容写到文件中
          sed -n 's/abc/hello &/p' txt.txt 等效于在abc前插入hello,其中 &表示原文本
        • r 读文件附加到该行的未尾

        • w 写文件

        • a 在当前行后添加一行或多行

        • i 在当前行之前插入文本

        • c 用新文本代替当前行中的文本

        • l 显示模式空间中的内容,显示非打印字符为两字节ASCII码

        • y 将一字符转换为另一字符(如:[address]y/abc/xyz/)

        • n 读入下一行,并将其读入模式缓冲区中,任何命令都将应用于下一行

        • q 结束或退出sed

        • D 删除多行pattern space中到n为止

        • N 将下一行附加到上一行

        • P 打印到多行pattern space的n为止

    • input_file指定sed的输入文件,可以是一个文件列表,缺省为标准输入。注意sed不与初始文件打交道,而只与它的一个拷贝打交道,如果操作结果没有重定向到文件(保存结果的方式),那么将输出到屏幕(不会影响原文件!除非使用-i选项)。

    • sed 使用示例:

      sed -i 's/old pattern/new pattern/g' file.txt       # 文本替换
      sed '/pattern/ s/old/new/' sample_one               # 对匹配pattern的行使用new替换old
      sed '5,6 s/1/2/' sample_one                         # 只对5-6行进行替换操作
      sed -n '/abc/ p' top100k_celebrity_2.txt            # 仅显示包括 abc 的行
      sed '/abc/ d' top100k_celebrity_2.txt               # 删除包括 abc 的行
      sed '/abc/ !d' top100k_celebrity_2.txt              # 删除不包括 abc 的行
      sed '/two/ s/1/2/; /three/ s/1/3/; /^$/ d' file     # 连接使用2条命令,使用;号分开
      sed '$a This is tail' file.txt                      # 把"This is tail"插入到file.txt未尾
      sed -i "s/old/new/g" `grep new -rl ~/dir`           # sed+grep批量替换多个文件中的字符串
      
  • 重量级选手: awk
    awk是一个优秀的样式扫描与处理工具,完整的awk脚本通常用来格式化文本文件中的信息。这有一篇不错的 学习笔记
    • Usage: awk [opion] 'awk_script' input_file1 [input_file2 ...]

      • -F fs 使用 fs 作为输入记录的字段分隔符,如果省略该选项,wak 使用环境变量 IFS 的值
      • -f filename 从文件 filename 中读取 awk_script
      • -v var=value 为 awk_script 设置变量
    • 另外两种使用awk程序文件来使用awk的方法:

      • 将awk程序写入到一个文件中,执行awk -f file_name [input_file];
      • 在一个文件的第一行写入#!/bin/awk -f 然后把这个文件赋予执行权限,就可以像一个可执行文件一样进行调用了。
    • awk 命令的一般形式 : (其中 BEGIN { actions } 和 END { actions } 是可选的。):

      awk '
            BEGIN { actions }\
            awk_pattern1 { actions }\
            ............  > awk_script
            awk_patternN { actions }\
            END { actions }\
          '
      inputfile(inputfile可以是一个文件列表)
      
    • 像一个数据库一样的工作 ,这是awk最大的特征:
      awk处理的工作与数据库的处理方式有相同之处,其相同处之一就是awk支持对记录和字段的处理,其中对字段的处理是grep和sed不能实现的,这也是awk优于二者的原因之一。
      在awk中,缺省的情况下总是将文本文件中的一行视为一个记录,而将一行中的某一部分作为记录中的一个字段。于是,awk把一个文件变成了一个数据库。
    • awk 的运行过程 :(明白了AWK的运行过程就对它完全了解了,awk不再神秘了)

      1. 如果 BEGIN 区块存在,awk 执行它指定的 actions。
      2. awk 从输入文件中读取一行,称为一条输入记录。(如果输入文件省略,将从标准输入读取)
      3. awk 将读入的记录分割成字段,将第 1 个字段放入变量$1 中,第 2 个字段放入$2,以此类推。$0 表示整条记录。字段分隔符使用 shell 环境变量 IFS 或由参数指定。
      4. 把当前输入记录依次与每一个 awk_cmd 中 awk_pattern 比较,看是否匹配,如果相匹配,就执行对应的actions。如果不匹配,就跳过对应的 actions,直到比较完所有的 awk_cmd。
      5. 当一条输入记录比较了所有的 awk_cmd 后,awk 读取输入的下一行,继续重复步骤3和4,这个过程一直持续,直到 awk 读取到文件尾。
      6. 当 awk 读完所有的输入行后,如果存在 END,就执行相应的 actions。
    • awk 的模式 (pattern部分):

      • /pattern/ 使用通配符的扩展集。
      • /p1/ && /p2/ 可以用关系运算符进行操作。
      • $1 ~/[0-9]+/ 模式匹配表达式 用运算符~(匹配)和~!(不匹配)。
      • p1, p2 范围模板 ,指定一个行的范围。该语法不能包括BEGIN和END模式。
      • BEGIN 让用户指定在第一条输入记录被处理之前所发生的动作,通常可在这里设置全局变量。
      • END 让用户在最后一条输入记录被读取之后发生的动作。
    • awk 的命令 (action部分):

      1. print 参数列表 print 可以打印字符串(加双引号)、变量和表达式,是 awk 最基本的命令,参数列表要用逗号(,)分隔。
      2. printf ([格式符],参数) 格式化打印命令(函数),与 C 语言的 printf 函数类似。
      3. next 强迫 awk 立刻停止处理当前的记录,而开始读取和处理下一条记录。
      4. nextfile 强迫 awk 立刻停止处理当前的输入文件而处理输入文件列表中的下一个文件
      5. exit 使 awk 停止执行而跳出。如果有 END 存在,awk 会去执行 END 的 actions。
    • 几个实例:

      awk '/^(no|so)/' test                                 #打印所有以模式no或so开头的行。
      awk '/^[ns]/{print $1}' test                          #如果记录以n或s开头,就打印这个记录。
      awk '$1 ~/[0-9][0-9]$/(print $1}' test                #如果第一个域以两个数字结束就打印这个记录。
      awk '$1 == 100 || $2 < 50' test                       #如果第一个或等于100或者第二个域小于50,则打印该行。
      awk '$1 != 10' test                                   #如果第一个域不等于10就打印该行。
      awk '/test/{print $1 + 10}' test                      #如果记录包含正则表达式test,则第一个域加10并打印出来。
      awk '{print ($1 > 5 ? "ok "$1: "error"$1)}' test      #根据第一个域是否大于5则打印不同的消息
      awk '/^root/,/^mysql/' test                           #打印以正则表达式root开头的记录到以正则表达式mysql开头的记录范围内的所有记录。
                                                            #如果找到一个新的正则表达式root开头的记录,则继续打印直到下一个以正则表达式mysql开头的记录为止,或到文件末尾。
      
    • awk 中的环境变量:

      变量 描述
      $n 当前记录的第n个字段,字段间由FS分隔。
      $0 完整的输入记录。
      ARGC 命令行参数的数目。
      ARGIND 命令行中当前文件的位置(从0开始算)。
      ARGV 包含命令行参数的数组。
      CONVFMT 数字转换格式(默认值为%.6g)
      ENVIRON 环境变量关联数组。
      ERRNO 最后一个系统错误的描述。
      FIELDWIDTHS 字段宽度列表(用空格键分隔)。
      FILENAME 当前文件名。
      FNR 同 NR, 但相对于当前文件。
      FS 字段分隔符(默认是任何空格)。
      IGNORECASE 如果为真,则进行忽略大小写的匹配。
      NF 当前记录中的字段数。
      NR 当前记录数。
      OFMT 数字的输出格式(默认值是%.6g)。
      OFS 输出字段分隔符(默认值是一个空格)。
      ORS 输出记录分隔符(默认值是一个换行符)。
      RLENGTH 由match函数所匹配的字符串的长度。
      RS 记录分隔符(默认是一个换行符)。
      RSTART 由match函数所匹配的字符串的第一个位置。
      SUBSEP 数组下标分隔符(默认值是034)。
    • awk 编程 :

      • 变量:变量不需要定义就可以直接使用,还可以使用从命令行传递的变量,以及内置的环境变量。

      • 重定向:可以使用shell的重定向符进行重定向输出。

      • getline函数:它从输入中获得下一行的内容,并给NF,NR和FNR等内建变量赋值。如果得到一条记录,getline函数返回1,如果到达文件的末尾就返回0,如果出现错误,例如打开文件失败,就返回-1。
        awk ‘BEGIN{while( “ls” | getline f) print f}’,ls的输出传递给geline作为输入,循环使getline从ls的输出中读取一行,并把它打印到屏幕,直到最后一个文件。
        于是这条命令相当于显示当前目录下所有文件名。
      • system函数:可以在awk中调用linux系统命令。

      • 支持while循环、for循环、for in循环、完整的if/else语法、下标可以为数字和字母的数组。

      • awk的函数:

        • 内建函数:

          • 字符串函数:

            • sub(regular expression, substitution string, target string) 替换操作
            • gsub() 作用如sub,但它在整个文档中进行匹配。
            • index(string, substring) 返回子字符串第一次被匹配的位置,偏移量从位置1开始。
            • length(string) 求字符串长度
            • substr( string, starting position, length of string ) 返回从位置1开始的子字符串。
            • match( string, regular expression ) 返回在字符串中正则表达式位置的索引,如果找不到指定的正则表达式则返回0。
            • toupper和tolower函数可用于字符串大小间的转换
            • split( string, array, field separator ) 可按给定的分隔符把字符串分割为一个数组,默认为当前FS值进行分割。
          • 时间函数:

            • systime()返回从1970年1月1日开始到当前时间(不计闰年)的整秒数。
            • strftime()使用C库中的strftime函数格式化时间。
          • 数学函数:

            函数名称 返回值
            atan2(x,y) y,x范围内的余切
            cos(x) 余弦函数
            exp(x) 求幂
            int(x) 取整
            log(x) 自然对数
            rand() 随机数
            sin(x) 正弦
            sqrt(x) 平方根
            srand(x) x是rand()函数的种子
            int(x) 取整,过程没有舍入
            rand() 产生一个大于等于0而小于1的随机数
        • 自定义函数:

          function name ( parameter, parameter, parameter, ... ) {
            statements
            return expression  # the return statement and expression are optional
          }
          
    • 需要注意的几点

      • 一条 awk_cmd 的 awk_pattern 可以省略,省略时不对输入记录进行匹配比较就执行相应的 actions。一条awk_cmd 的 actions 也可以省略,省略时默认的动作为打印当前输入记录(print $0) 。
      • awk_script 中只有 END 区块或 BEGIN区块是被允许的。如果 awk_script 中只有 BEGIN { actions } ,awk 不会读取 input_file。
      • awk 把输入文件的数据读入内存,然后操作内存中的输入数据副本,awk 不会修改输入文件的内容。
      • awk 的总是输出到标准输出,如果想让 awk 输出到文件,可以使用重定向,awk ‘$1 > 100 {print $1 > “output_file” }’ test。

《高级Bash脚本编程指南3.9.1》 参考卡片

表格 B-1. 特殊的shell变量

变量 含义
$0 脚本名字
$1 位置参数 #1
$2 - $9 位置参数 #2 - #9
${10} 位置参数 #10
$# 位置参数的个数
$* 所有的位置参数(作为单个字符串, “$1 $2 $3 … $n”)
$@ 所有的位置参数(每个都作为独立的字符串, “$1” “$2” “$3” … “$n”)
${#*} 传递到脚本中的命令行参数的个数
${#@} 传递到脚本中的命令行参数的个数
$? 返回值
$$ 脚本的进程ID(PID)
$- 传递到脚本中的标志(使用set)
$_ 之前命令的最后一个参数
$! 运行在后台的最后一个作业的进程ID(PID)

表格 B-2. 测试操作: 二元比较

操作 描述   操作 描述   操作 描述
算术比较     字符串比较     算术比较 双括号(( … ))结构
-eq 等于   = 等于      
    == 等于      
-ne 不等于   != 不等于      
-lt 小于   < 小于 (ASCII)*   < 小于
-le 小于等于         <= 小于等于
-gt 大于   > 大于 (ASCII)*   > 大于
-ge 大于等于         >= 大于等于
    -z 字符串为空      
    -n 字符串不为空      

* 如果在双中括号 [[ … ]] 测试结构中使用的话, 那么就不需要使用转义符了.


表格 B-3. 文件类型的测试操作

操作 测试条件 操作 测试条件
-e 文件是否存在 -s 文件大小不为0
-f 是一个标准文件    
-d 是一个目录 -r 文件具有读权限
-h 文件是一个符号链接 -w 文件具有写权限
-L 文件是一个符号链接 -x 文件具有执行权限
-b 文件是一个块设备    
-c 文件是一个字符设备 -g 设置了sgid标记
-p 文件是一个管道 -u 设置了suid标记
-S 文件是一个socket -k 设置了”粘贴位”
-t 文件与一个终端相关联    
     
-N “从这个文件最后一次被读取之后, 它被修改过” F1 -nt F2 文件F1比文件F2新 *
-O 这个文件的宿主是你 F1 -ot F2 文件F1比文件F2旧 *
-G 文件的组id与你所属的组相同 F1 -ef F2 文件F1和文件F2都是同一个文件的硬链接 *
     
! “非” (反转上边的测试结果)    

* 二元操作符(需要两个操作数).


表格 B-4. 参数替换和扩展

表达式 含义
${var} “变量var的值, 与$var相同”
 
${var-DEFAULT} “如果var没有被声明, 那么就以$DEFAULT作为其值*”
${var:-DEFAULT} “如果var没有被声明, 或者其值为空, 那么就以$DEFAULT作为其值*”
 
${var=DEFAULT} “如果var没有被声明, 那么就以$DEFAULT作为其值*”
${var:=DEFAULT} “如果var没有被声明, 或者其值为空, 那么就以$DEFAULT作为其值*”
 
${var+OTHER} “如果var声明了, 那么其值就是$OTHER, 否则就为null字符串”
${var:+OTHER} “如果var被设置了, 那么其值就是$OTHER, 否则就为null字符串”
 
${var?ERR_MSG} “如果var没被声明, 那么就打印$ERR_MSG*”
${var:?ERR_MSG} “如果var没被设置, 那么就打印$ERR_MSG*”
 
${!varprefix*} 匹配之前所有以varprefix开头进行声明的变量
${!varprefix@} 匹配之前所有以varprefix开头进行声明的变量

* 当然, 如果变量var已经被设置的话, 那么其值就是$var.


表格 B-5. 字符串操作

表达式 含义
${#string} $string的长度
 
${string:position} “在$string中, 从位置$position开始提取子串”
${string:position:length} “在$string中, 从位置$position开始提取长度为$length的子串”
 
${string#substring} “从变量$string的开头, 删除最短匹配$substring的子串”
${string##substring} “从变量$string的开头, 删除最长匹配$substring的子串”
${string%substring} “从变量$string的结尾, 删除最短匹配$substring的子串”
${string%%substring} “从变量$string的结尾, 删除最长匹配$substring的子串”
 
${string/substring/replacement} “使用$replacement, 来代替第一个匹配的$substring”
${string//substring/replacement} “使用$replacement, 代替所有匹配的$substring”
${string/#substring/replacement} “如果$string的前缀匹配$substring, 那么就用$replacement来代替匹配到的$substring”
${string/%substring/replacement} “如果$string的后缀匹配$substring, 那么就用$replacement来代替匹配到的$substring”
 
“expr match “”$string”” ‘$substring’” 匹配$string开头的$substring*的长度
“expr “”$string”” : ‘$substring’” 匹配$string开头的$substring*的长度
“expr index “”$string”” $substring” 在$string中匹配到的$substring的第一个字符出现的位置
expr substr $string $position $length, 在$string中从位置$position开始提取长度为$length的子串
“expr match “”$string”” ‘($substring)’” 从$string的开头位置提取$substring*
“expr “”$string”” : ‘($substring)’” 从$string的开头位置提取$substring*
“expr match “”$string”” ‘.*($substring)’” 从$string的结尾提取$substring*
“expr “”$string”” : ‘.*($substring)’” 从$string的结尾提取$substring*

* $substring是一个正则表达式.


表格 B-6. 一些结构的汇总

表达式 含义
中括号  
if [ CONDITION ] 测试结构
if [[ CONDITION ]] 扩展的测试结构
Array[1]=element1 数组初始化
[a-z] 正则表达式的字符范围
大括号  
${variable} 参数替换
${!variable} 间接变量引用
{ command1; command2; … commandN; } 代码块
“{string1,string2,string3,…}” 大括号扩展
圆括号  
( command1; command2 ) 子shell中执行的命令组
Array=(element1 element2 element3) 数组初始化
result=$(COMMAND) “在子shell中执行命令并将结果赋值给变量”
>(COMMAND) 进程替换
<(COMMAND) 进程替换
双圆括号  
(( var = 78 )) 整型运算
var=$(( 20 + 5 )) “整型运算并将结果赋值给变量”
引号  
“$variable” “弱”引用
‘string’ “强”引用
后置引用  
result=`COMMAND` “在子shell中运行命令并将结果赋值给变量”