一年级团队课教案:shell programing

来源:百度文库 编辑:中财网 时间:2024/05/01 04:43:34
学习方法:语法+命令(内、外)+工具
-----------------------------
前奏:guard_serv.sh    guard_sys.sh
### 脚本的框架
### 运行脚本的方式和区别 
### 语法
    1、注释
    2、变量(分类(环境变量,内部变量,用户变量)定义、赋值、替换、无类型)
    3、echo 引用变量、转义 \n \a \v \t
    4、退出码
    5、条件判断
    6、循环分支 for while util 嵌套循环 循环控制(break continue) case
    7、操作符
 
 
-----------------------------
#### shell的作用,分类,作用
 
## 脚本框架
    你的系统这个文件的执行需要指定一个解释器.#!实际上是一个2 字节[1]的魔法数字,这是指定一个文件类型的特殊标记, 表明这是是一个可执行的脚本。在#!之后接着是一个路径名.这个路径名指定了一个解释脚本中命令的程序,这个程序可以是 shell,程序语言或者是任意一个通用程序.这个指定的程序从头开始解释并且执行脚本中的命令(从#!行下边的一行开始)
 
 
## 运行脚本的方式和区别
 
. test.sh  <--当前shell执行,不需要x权限
./test.sh <----需要X权限
bash  <--当前shell执行,不需要x权限
 
## 注释 
 
# comment
echo "hello" #comment <---注意#前的空格
echo "#hi"  <--not comment
 
#### 变量
 
 ## 变量分类
     
环境变量:给系统或者应用程序设置的一些变量,以便告诉他们该如何工作
    PATH  HOME LANG
 
用户变量:用户在编写shell过程中,可以在shell程序内任意使用和修改的变量。(以字母或下划线开头) 
 
 
内部变量:保留的变量,不可改变它所代表的值: !$ $0-$9 $* $@ $? $# $$等
 
 
 
 ## 变量的声明和赋值
 
    a=    <----空值,null,不等于0
    a=1    <--- =号前后不能有空格,为什么? a =1   a= 1   a = 1 
    a="hello"
    a="hello tom"
 
    set 显示当前shell的变量,包括当前用户的变量
    env 显示当前用户的变量
    export 显示当前导出成用户变量的shell变量
 
    每个shell有自己特有的变量(set)显示的变量,这个和用户变量是不同的,当前用户变量和你用什么shell无关,不管你用什么shell都在,比如HOME,SHELL等这些变量,但shell自己的变量不同shell是不同的,比如BASH_ARGC, BASH等,这些变量只有set才会显示,是bash特有的,export不加参数的时候,显示哪些变量被导出成了用户变量,因为一个shell自己的变量可以通过export “导出”变成一个用户变量。
 
    export a=10  <---到处为环境变量,让以后的子程序能读取它。
 
    unset a取消变量a的设置
 
有时候变量名可能会和其它文字混淆,比如: 
 
    num=2
    echo "this is the $numnd"
 
上述脚本并不会输出"this is the 2nd"而是"this is the ";这是由于shell会去搜索变量numnd的值,而实际上这个变量此时并没有值。这时,我们可以用花括号来告诉shell要打印的是num变量: 
 
num=2
echo "this is the ${num}nd"
 
其输出结果为:this is the 2nd 
 
Shell脚本中有许多变量是系统自动设定的,我们将在用到这些变量时再作说明。除了只在脚本内有效的普通shell变量外,还有环境变量,即那些由export关键字处理过的变量。本文不讨论环境变量,因为它们一般只在登录脚本中用到。 
 
 
     
 ## 变量替换 <---取得变量var的值
 
    $a ;${a}  echo "\$a=$a"  ; a=$(uname -a) <---$()和 ``都是命令替换
    建议引用变量的时候用双引号 echo "$a"
 
    内部变量的例子: $1 $2 ${10} $# $* $@ $? $# $$
 
    特殊用法:
    echo ${var1-hello} <--当变量var1没声明,取默认值
    total=${2:-5}  <--当$2没定义,使用默认值,通常采用者,适合第一种情况
 
    a=3355
    b=${a/33/ff} <---ff替换33
    echo $b  # ff55
 
### 无类型
    a=1
    let "a += 1"
    b=aabb
    let "a += b"
    let "b += a" <===> b=`expr $b + $a`<===>  b=$(( $b + $a )) <===> b=$(( b + a ))
    c=  #null 参与运算时转为整型
    let "c += a"
 
 
 ## echo 引用变量
    echo "$a"
    echo "\$a"  # \转义符
    echo -e "$a\n$a"    # \n \a \v \t
     
    双引号的作用,避免怪异错误,弱引用,保留所有字符串,包括不可见,课可输出变量的值
    echo "$(uname -a)"
    单引号,强引用 <---一切当做字符
 
    常见参数 -n 不换行 <--结合使用-ne
    直接调用,用来换行
 
    另外一个用途就是:格式化输出,一般用处输出脚本用法或者提示等,菜单等
 
#### 退出码
    当脚本以不带参数的exit命令来结束时, 脚本的退出状态码就由脚本中最后执行的命令来决定(就是exit之前的命令).
不带参数的exit命令与 exit $?的效果是一样的, 甚至脚本的结尾不写exit, 也与前两者的效果相同.
 
#### 条件判断
 
 ## 简单的结构 if/then
    if [ condition ]
        then
        command1;
        command2;
    fi(有if开头,就有fi结束)
eg:
            if [ a=b ]
                    then
                        echo "a is equal b"
            fi
 
 ## 测试条件 man test
    true false ! -a -o
    字符串用 = ,!=  -n -z  ;表达式 
    数字用 -le等
    文件-e -d -f  -x -r -w
 
@@@@例子:chk_perm.sh 判断你对文件的权限
    大多数情况下,可以使用测试命令来对条件进行测试,比如可以比较字符串、判断文件是否存在及是否可读等等……通常用" [ ] "来表示条件测试,注意这里的空格很重要,要确保方括号前后的空格。 
 
[ -f "somefile" ] :判断是否是一个文件 
[ -x "/bin/ls" ] :判断/bin/ls是否存在并有可执行权限 
[ -n "$var" ] :判断$var变量是否有值 
[ "$a" = "$b" ] :判断$a和$b是否相等 
执行man test可以查看所有测试表达式可以比较和判断的类型。下面是一个简单的if语句: 
 
#!/bin/sh
 
if [ ${SHELL} = "/bin/bash" ]; then
   echo "your login shell is the bash (bourne again shell)"
else
   echo "your login shell is not bash but ${SHELL}"
fi
变量$SHELL包含有登录shell的名称,我们拿它和/bin/bash进行比较以判断当前使用的shell是否为bash。
 
 
 
 
#### 分支:if/then/else/fi
 
#### 嵌套:
    if [ condition ]
        then
        command1;
        command2;
    else if [ condition2 ];then
        command3;
        command4;
    fi
    fi     
 
 
#### select 语句 
select表达式是bash的一种扩展应用,擅长于交互式场合。用户可以从一组不同的值中进行选择: 
 
select var in ... ; do
 break;
done
.... now $var can be used ....
 
下面是一个简单的示例: 
 
#!/bin/sh
 
echo "What is your favourite OS?"
select var in "Windows Xp" "Linux" "Mac Os" "Other"; do
    break;
done
echo "You have selected $var"
 
如果 以上脚本运行出现 select :NOT FOUND 将 #!/bin/sh 改为 #!/bin/bash 找了半天才找到的答案 
该脚本的运行结果如下: 
 
What is your favourite OS?
1) Windows Xp
2) Linux
3) Mac Os
4) Other
#? 2
You have selected Linux
 
 
####让我们看一个例子,file命令可以辨别出一个给定文件的文件类型,如:file lf.gz,其输出结果为: 
 
lf.gz: gzip compressed data, deflated, original filename,
last modified: Mon Aug 27 23:09:18 2001, os: Unix
我们利用这点写了一个名为smartzip的脚本,该脚本可以自动解压bzip2, gzip和zip 类型的压缩文件: 
 
 #!/bin/sh
 
 ftype=`file "$1"`   # Note ' and ` is different
 case "$ftype" in
 "$1: Zip archive"*)
    unzip "$1" ;;
 "$1: gzip compressed"*)
    gunzip "$1" ;;
 "$1: bzip2 compressed"*)
    bunzip2 "$1" ;;
 *) echo "File $1 can not be uncompressed with smartzip";;
 esac(有case开头,就有esac结束)
你可能注意到上面使用了一个特殊变量$1,该变量包含有传递给该脚本的第一个参数值。也就是说,当我们运行: 
 
smartzip articles.zip
$1 就是字符串 articles.zip。



------------------------------------------------------------------------------------------------------------------

第二天 shell的输入和输出
1.echo    echo [option] string
            -e 解析转移字符
    注意:单引号和双引号都能关闭shell对特殊字符的处理。不同的是,双引号没有单引号严格,单引号关闭所有有特殊作用的字符,而双引号只要求shell忽略大多数,具体的说,就是①美元符号②反引号③反斜杠,这3种特殊字符不被忽略。 不忽略美元符号意味着shell在双引号内部也进行变量名替换。
[peter@uplooking 1]test=test
[peter@uplooking 1]$ echo "$test"
test
[peter@uplooking 1]$ echo '$test'
$test
           -n 回车不换行,linux系统默认回车换行

            转义字符 :
    \n    表示新行
    \r    表示回车
    \t    表示水平的制表符
    \v    表示垂直的制表符
    \b    表示后退符
    \a    表示“警告”(蜂鸣或是闪动)
    \0xx    翻译成ASCII码为八进制0xx所表示的字符
例:
   1 #!/bin/bash
   2 # escaped.sh: 转义字符
   3
   4 echo; echo
   5
   6 echo "\v\v\v\v"      # 打印出 \v\v\v\v literally.
   7 # 用带着选项-e的'echo'会打印出转义字符串.
   8 echo "============="
   9 echo "VERTICAL TABS"
  10 echo -e "\v\v\v\v"   # 打印四个垂直的制表符.
  11 echo "=============="
  12
  13 echo "QUOTATION MARK"
  14 echo -e "\042"       # 打印出字符" (引号, 它的八进制ASCII码为42).
  15 echo "=============="
  16
  17 # 当使用像$'\X'的结构时,-e选项是多余的.
  18 echo; echo "NEWLINE AND BEEP"
  19 echo $'\n'           # 新行.
  20 echo $'\a'           # 警告 (蜂鸣).
  21
  22 echo "==============="
  23 echo "QUOTATION MARKS"
  24 # 版本2开始Bash已经允许使用$'\nnn'结构了.
  25 # 注意在这里,'\0nn'表示一个八进制的值.
  26 echo $'\t \042 \t'   # Quote (") framed by tabs.
  27
  28 # 使用$'\xhh'结构也可以使用十六进制数来转义.
  29 echo $'\t \x22 \t'  # Quote (") framed by tabs.
  30
  31 # 早期的Bash版本允许用'\x022'.(但是现在不行了)
  32 echo "==============="
  33 echo
  34
  35
  36 # 用ASCII码值把字符赋给变量.
  37 # ----------------------------------------
  38 quote=$'\042'        # 引号"被赋给变量quote了.
  39 echo "$quote This is a quoted string, $quote and this lies outside the quotes."
  40
  41 echo
  42
  43 # 用连串的ASCII码把一串字符赋给变量..
  44 triple_underline=$'\137\137\137'  # 137是字符'_'的ASCII码.
  45 echo "$triple_underline UNDERLINE $triple_underline"    #(如果中间没有空格,如何实现?)
  46
  47 echo
  48
  49 ABC=$'\101\102\103'           # 101, 102, 103分别是A, B, C字符的八进制ASCII码.
  50 echo $ABC
  51
  52 echo; echo
  53
  54 escape=$'\033'                    # 033是ESC的ASCII码的八进制值
  55 echo "\"escape\" echoes as $escape"
  56 #                                   不可见的输出.
  57
  58 echo; echo
  59
  60 exit 0

eg.
#!/bin/bash
#echo
echo -e "this echo's 3 newline\n\n\n"
echo "OK"
echo
echo "this is echo's 3 newline\n\n\n"
echo "this log file have all been done">mylogfile.txt
[peter@uplooking ~]$ sh echod.sh
this echo's 3 newline

OK

this is echo's 3 ewline\n\n\n
上面可以看到有-e则可以解析转移字符,没有不能解析。echo空输出为空

\"

    表示引号(")的字面意思

       1 echo "Hello"                  # Hello
       2 echo "\"Hello\", he said."    # "Hello", he said.

\$

    表示美元符($)的字面意思(如果在\$跟上变量名将不会引用变量的值)

       1 echo "\$variable01"  # 输出是$variable01

\\

    表示反斜杠(\)的字面意思

       1 echo "\\"  # 输出是\
       2
       3
       4 echo "\"   # 在命令行,这句将会打印SP2变量值(译者注:变量SP2是输入未完成提示符),并要求你继续输入..
       5            # 在脚本文件里, 这句会出错.

Note:   
反斜杠的作用要看它是否是自我转义,被引用,或出现在命令替换结构或是在here document里.

   1                       #  简单的转义和引用
   2 echo \z               #  z
   3 echo \\z              # \z
   4 echo '\z'             # \z
   5 echo '\\z'            # \\z
   6 echo "\z"             # \z
   7 echo "\\z"            # \z
   8
   9                       #  命令替换
  10 echo `echo \z`        #  z
  11 echo `echo \\z`       #  z
  12 echo `echo \\\z`      # \z
  13 echo `echo \\\\z`     # \z
  14 echo `echo \\\\\\z`   # \z
  15 echo `echo \\\\\\\z`  # \\z
  16 echo `echo "\z"`      # \z
  17 echo `echo "\\z"`     # \z
  18
  19                       # Here document
  20 cat <  21 \z                     
  22 EOF                   # \z
  23
  24 cat <  25 \\z                    
  26 EOF                   # \z
  27
 

一个字符串赋给变量时里面的组成部分可能会被转义,但如果单独一个转义字符(\)是不能赋给变量的。

   1 variable=\
   2 echo "$variable"
   3 # 不能工作 - 给出一个错误信息:
   4 # test.sh: : command not found
   5 # 单独一个转义字符是不能正确地赋给变量的.
   6 #
   7 #  那上面语句究竟发生了什么呢?实际上转义符"\"转义了新行符,
   8 #+ 产生的作用如同       variable=echo "$variable"
   9 #+                      而这是无效的变量赋值
  10
  11 variable=\
  12 23skidoo
  13 echo "$variable"        #  23skidoo
  14                         #  这样就能工作,因为第二行的变量赋值是有效的
  15                         #
  16
  17 variable=\
  18 #        \^    转义后面的空格(译者注:粗心的读者一定要注意上面最后的空格)
  19 echo "$variable"        # 空格
  20
  21 variable=\\
  22 echo "$variable"        # \
  23
  24 variable=\\\
  25 echo "$variable"
  26 # 不能工作 - 产生一个错误:
  27 # test.sh: \: command not found
  28 #
  29 #  第一个\转义第二个\,结果只剩单独的第三个\字符,
  30 #+ 这样又会发生上面的情况.
  31
  32 variable=\\\\
  33 echo "$variable"        # \\
  34                         # 第二和第四个\字符被转义.
  35                         # 这样不会出错了.

转义一个空格可以防止一个字符串参数被分割成多个命令行参数。

   1 file_list="/bin/cat /bin/gzip /bin/more /usr/bin/less /usr/bin/emacs-20.7"
   2 # 文件列表作为参数传递给命令.
   3
   4 # 再加两个参数给命令ls,一同列出文件信息.
   5 ls -l /usr/X11R6/bin/xsetroot /sbin/dump $file_list
   6
   7 echo "-------------------------------------------------------------------------"
   8
   9 # 如果我们转义上面的一对空格会发生什么?
  10 ls -l /usr/X11R6/bin/xsetroot\ /sbin/dump\ $file_list
  11 # 出错: 开头的三个文件名被连成一个文件名并传递给了命令'ls -l'
  12 #        因为两个转义字符禁止了空格分割参数的作用。

转义符也提供了写一个多行命令的手段。一般地,每个单独的行有一个不同的命令,而在一行末尾的转义符转义新行符,命令序列则由下一行继续。

   1 (cd /source/directory && tar cf - . ) | \
   2 (cd /dest/directory && tar xpvf -)
   3 # 把Alan Cox目录树全部复制到另外一个目录里,
   4 # 但分为两行可以增加可读性.
   5
   6 # 你也可以用下面的命令达到一样的效果:
   7 tar cf - -C /source/directory . |
   8 tar xpvf - -C /dest/directory
   9 # 看看下面的注释.
  10 # (多谢,Stéphane Chazelas.)

Note:   
如果一个脚本行用一个管道线"|"结束行尾,后面可以再跟一个不必一定要的转义符"\"。然而,好的编程习惯最好加上一个转义符“\”。

   1 echo "foo
   2 bar"
   3 #foo
   4 #bar
   5
   6 echo
   7
   8 echo 'foo
   9 bar'    # 没什么不同.
  10 #foo
  11 #bar
  12
  13 echo
  14
  15 echo foo\
  16 bar     # 新行符被转义.
  17 #foobar
  18
  19 echo
  20
  21 echo "foo\
  22 bar"     # 还是一样,字符\在弱引用中还是被解释为转义字符
  23 #foobar
  24
  25 echo
  26
  27 echo 'foo\
  28 bar'     # 由于转义符"\"在强引用符里,所以只能解释为字面上的意思
  29 #foo\
  30 #bar
 

2.read 可以从键盘或文件的某一行文本中读入信息,并将其赋给一个变量
read variable1 variable2
eg.
#!/bin/bash
#readname
echo -n "first name:"
read firstname
echo -n "last name:"
read lastname
echo "this name is $firstname $lastname"

3.cat        显示文件的内容,创建内容,还可以显示控制字符
            cat [options]filename1 filename2
                   -v   显示控制字符(Windows文件)
             cat命令不会分页显示,要分页可以采用more、less

4.管道 |

5.tee     把输出的一个副本输送到标准输出,另一个副本拷贝到相应的文件中,一般与管道合用
              tee [options] files
              -a 在文件中追加
eg.
[peter@uplooking 1]$ echo |tee myfile

[peter@uplooking 1]$ cat myfile

将myfile文件置空 cat /dev/null > a.txt

6.文件重定向
command>filename                                        ---覆盖输出
command>>filename                                      ---追加输出
command>filename>&1                                 ---把标准输出和标准错误重定向
command<commandcommand<-                                                     --- 关闭标准输入
>nullfile.txt                                                       ---创建字节为0的文件

eg.
说明:myfile为空间
[peter@uplooking 1]$ df -lh>myfile
[peter@uplooking 1]$ cat myfile
Filesystem            Size Used Avail Use% Mounted on
/dev/sda1              20G 3.3G   16G 18% /
none                  2.0G     0 2.0G   0% /dev/shm
/dev/sda2              79G   17G   59G 23% /u01
/dev/sda4              28G 3.9G   22G 15% /u02
[peter@uplooking 1]$ df -lh>myfile
[peter@uplooking 1]$ cat myfile
Filesystem            Size Used Avail Use% Mounted on
/dev/sda1              20G 3.3G   16G 18% /
none                  2.0G     0 2.0G   0% /dev/shm
/dev/sda2              79G   17G   59G 23% /u01
/dev/sda4              28G 3.9G   22G 15% /u02
[peter@uplooking 1]$ df -lh>>myfile
[peter@uplooking 1]$ cat myfile
Filesystem            Size Used Avail Use% Mounted on
/dev/sda1              20G 3.3G   16G 18% /
none                  2.0G     0 2.0G   0% /dev/shm
/dev/sda2              79G   17G   59G 23% /u01
/dev/sda4              28G 3.9G   22G 15% /u02
Filesystem            Size Used Avail Use% Mounted on
/dev/sda1              20G 3.3G   16G 18% /
none                  2.0G     0 2.0G   0% /dev/shm
/dev/sda2              79G   17G   59G 23% /u01
/dev/sda4              28G 3.9G   22G 15% /u02
[peter@uplooking 1]$ cat >>myfile<> China
> Hubei
> Suizhou
> exit
[peter@uplooking 1]$ cat myfile
China
Hubei
Suizhou

7.exec        可以用来替代当前shell。现有任何环境变量都会清除(甚用!!!)

==============================================
控制流结构

1.if语句
if 条件1
then
     命令1
elif 条件2
then
     命令2
else
     命令3
fi
------------------
if 条件
then 命令
fi
eg:
#!/bin/bash
#if test
#this is a comment line
if [ "10" -lt "12" ];then
#yes 10 is less than 12
echo "yes,10 is less than 12"
else
echo "no"
fi
注意:if语句必须以fi终止
   "10" 前一个空格,“12”后也有一个空格。这个条件都是通过test命令来指定。条件表达为test expression或者[expression]

条件表达式中的比较函数
man test
NAME
       test - check file types and compare values

SYNOPSIS
       peter EXPRESSION
       [ EXPRESSION ]
       [ OPTION

DESCRIPTION
       Exit with the status determined by EXPRESSION.

       --help display this help and exit

       --version
              output version information and exit

       EXPRESSION is true or false and sets exit status. It is one of:

       ( EXPRESSION )
              EXPRESSION is true

       ! EXPRESSION
              EXPRESSION is false

       EXPRESSION1 -a EXPRESSION2
              both EXPRESSION1 and EXPRESSION2 are true

       EXPRESSION1 -o EXPRESSION2
              either EXPRESSION1 or EXPRESSION2 is true

       [-n] STRING
              the length of STRING is nonzero

       -z STRING
              the length of STRING is zero

       STRING1 = STRING2
              the strings are equal

       STRING1 != STRING2
               the strings are not equal

       INTEGER1 -eq INTEGER2
              INTEGER1 is equal to INTEGER2

       INTEGER1 -ge INTEGER2
              INTEGER1 is greater than or equal to INTEGER2

       INTEGER1 -gt INTEGER2
              INTEGER1 is greater than INTEGER2

       INTEGER1 -le INTEGER2
              INTEGER1 is less than or equal to INTEGER2

       INTEGER1 -lt INTEGER2
              INTEGER1 is less than INTEGER2

       INTEGER1 -ne INTEGER2
              INTEGER1 is not equal to INTEGER2

       FILE1 -ef FILE2
              FILE1 and FILE2 have the same device and inode numbers

       FILE1 -nt FILE2
              FILE1 is newer (modification date) than FILE2

       FILE1 -ot FILE2
              FILE1 is older than FILE2

       -b FILE
              FILE exists and is block special

       -c FILE
              FILE exists and is character special

       -d FILE
              FILE exists and is a directory

       -e FILE
              FILE exists

       -f FILE
              FILE exists and is a regular file
       -g FILE
              FILE exists and is set-group-ID

       -h FILE
              FILE exists and is a symbolic link (same as -L)

       -G FILE
              FILE exists and is owned by the effective group ID

       -k FILE
              FILE exists and has its sticky bit set

       -L FILE
              FILE exists and is a symbolic link (same as -h)

       -O FILE
              FILE exists and is owned by the effective user ID

       -p FILE
              FILE exists and is a named pipe

       -r FILE
              FILE exists and is readable

       -s FILE
              FILE exists and has a size greater than zero

       -S FILE
              FILE exists and is a socket

       -t [FD]
              file descriptor FD (stdout by default) is opened on a terminal

       -u FILE
              FILE exists and its set-user-ID bit is set

       -w FILE
              FILE exists and is writable

       -x FILE
             FILE exists and is executable

eg.
#!/bin/bash
#this is a comment line
echo "Enter your filename:"
read myfile
if [ -e $myfile ]
then
   if [ -s $myfile ];then
    echo "$myfile exist and size greater than zero"
   else
    echo "$myfile exist but size is zero"
   fi
else
echo "file no exist"
fi
[peter@uplooking 1]$ sh ifpeter.sh
Enter your filename:
11
11 exist but size is zero

2.case语句
case语句为多选择语句。
case 值 in
模式1)
    命令1
    ;;
模式2)
    命令2
    ;;
esac
eg.
#!/bin/bash
#case select
echo -n "enter a number from 1 to 3:"
read ans
case $ans in
1)
echo "you select 1"
;;
2)
echo "you select 2"
;;
3)
echo "you select 3"
;;
*)
echo "`basename $0`:this is not between 1 and 3">&2    
exit;
;;
esac

3.for 循环
for循环一般格式:
for 变量名 in 列表 (列表以空格作为分割)
do
   命令1
   命令2
done

eg1:
#!/bin/bash
#forlist1
for loop in 1 2 3 4 5
do
echo $loop
done

eg2.
证明 for 循环的使用来为每个 .txt 文件做备份。执行了这个命令之后,在你的txt文件就变得很安全了:

[carol@octarine ~/articles] ls *.txt
file1.txt file2.txt  file3.txt

[carol@octarine ~/articles] ls *.txt > list

[carol@octarine ~/articles] for i in `cat list`; do cp "$i" "$i".bak ; done

[carol@octarine ~/articles] ls *.txt*
file1.txt  file1.txt.bak  file2.txt  file2.txt.bak  file3.txt  file3.txt.bak



4.
while循环
while 命令 (可以是一个命令也可以是多个,做条件测试)
do
      命令1
      命令2
      ...
done
注意:如果从文件中读入变量eg1.
#!/bin/bash

# This script opens 4 terminal windows.

i="0"

while [ $i -lt 4 ]
do
xterm &
i=$[$i+1]
done

eg2.
嵌套while循环

下面的例子是拷贝在web目录中由摄像头生成的图片。每5分钟就生成一张图片。每1个小时,就建立一个新的目录,存放那个小时中的图片。每天一个新的包含24个子目录的目录就被创建。脚本在后台运行。

#!/bin/bash

# This script copies files from my homedirectory into the webserver directory.
# (use scp and SSH keys for a remote directory)
# A new directory is created every hour.

PICSDIR=/home/carol/pics
WEBDIR=/var/www/carol/webcam

while true; do
    DATE=`date +%Y%m%d`
    HOUR=`date +%H`
    mkdir $WEBDIR/"$DATE"
   
    while [ $HOUR -ne "00" ]; do
        DESTDIR=$WEBDIR/"$DATE"/"$HOUR"
        mkdir "$DESTDIR"
        mv $PICDIR/*.jpg "$DESTDIR"/
        sleep 3600
        HOUR=`date +%H`
    done
done

注意 true 语句的使用。意味着:直到我们强制中断否则就继续执行。(使用 kill 或者 Ctrl+C)。

这个小脚本可以用来模拟测试;生成文件:

#!/bin/bash

# This generates a file every 5 minutes

while true; do
touch pic-`date +%s`.jpg
sleep 300
done

注意使用 date 命令来生成各种文件和目录名字的用法。

eg3.
使用键盘输入来控制while循环

这个脚本可以由用户用 Ctrl+C 来中断:

#!/bin/bash

# This script provides Peter

while true; do
echo "On which topic do you want advice?"
cat << topics
politics
baby
linux-destributons
sports
war
magic
love
literature
drugs
education
topics

echo
echo -n "Make your choice: "
read topic
echo
echo "Free advice on the topic of $topic: "
echo

done

一个 here 文档用来提示用户可能的选择项。再次,true 一次又一次地重复测试 CONSEQUENT-COMMANDS 中的命令。

思考:如何修改一下这个脚本,让脚本在运行时,显示选择的条目标题号,并显示该条目的内容?
作业:从键盘循环读入十个数值,判断是否是在1-10的



-----------------------------------------------------------------------------------------------------------------

第三天 until、函数、数组等
 
echo $((1+2))   $[ 1+2 ]
      printf  格式化输出
 
basename /a/b/filename
basename /a/b/file.txt .txt
 
 
操作符 意义 
VAR++ and VAR--  <======变量自增和变量自减 
++VAR and --VAR  <======变量前置加和前置减 
- and +      <======一元减和加 
! and ~          <======逻辑和按位取反 
**          <======求幂 
*, / and %       <======乘,除,求余 
+ and -          <======加,减 
<< and >>        <======左移和右移 
<=, >=, < and >  <======比较操作符 
== and !=        <======相等和不相等 
&                <======位与 
^                <======位异或 
|                <======位或 
&&               <======逻辑与 
||                <======逻辑或 
expr ? expr : expr <======条件赋值 
=, *=, /=, %=, +=, -=, <<=, >>=, &=, ^= and |=    <======    赋值 
,      <=======表达式间的分隔符 
 
eg.
[ -f "/etc/shadow" ] && echo "This computer uses shadow passwords"
 
 
eg.
==========================================================================
 
变量的算术运算
 在Bash Shell中,只能进行两个整数间的运算,其结果仍为整数.要进行算术
运算,需要使用let命令,语法为:
  let expr
expr是一个包含项和操作符的表达式,项可以是一个变量或是一个整数常数,当使用整数常数时,其默认为十进制整数,用户可以用radio#number来指定其它形式的整数,其中radio定义了整数是几进制表示的,number是该整数的值.若radio>10,那么数字字符可从0-9和A-Z.
  在表达式中支持的操作符及其含义为:
  +,-,*,/,% 加,减,乘,除,取模
  >>,<<,&,^,| 左移,右移,位与,位异或,位或
  ?: 三元运算符.与C语言中的定义一致
  ~ 取补码
  !,>=,<=,>,<,==,!=,&&,||
  =,+=,-=,*=,/=,%=,<<=,>>=,&=,^=,|=
表达式式中也可以使用括号.括号或运算优先级的定义与一般计算机语言中的相同.
  let命令具有返回值.当计算结果(若有多个表达式时,以最后一个为准)为0时,
返回值为1,否则为0.
  当表达式中含有shell的特殊字符(如|)时,需要用引用符('或")将其引用起来.
使用let时还需要注意的时,对于let x+y这样的式子,shell虽然计算了x+y的值但却将结果丢弃,若不想这样,可以使用let sum=x+y将x+y的结果保存在变量sum中,另外还可以使用((和))操作符取代let命令,而且这样的话,还可以省去对算术表达式的引用,如果想返回表达式的值,则需用$(())的格式.
  
  if的条件中,“=”用于比较字符串;“-eq”用于比较整型数。
============================================================================
expr命令是一个手工命令行计数器,用于在UNIX/LINUX下求表达式变量的值,一般用于整数值,也可用于字符串。
–格式为:
expr Expression(命令读入Expression 参数,计算它的值,然后将结果写入到标准输出)
–参数应用规则:
用空格隔开每个项;
用 \ (反斜杠) 放在 shell 特定的字符前面;
对包含空格和其他特殊字符的字符串要用引号括起来
 
–expr用法实例讲解:
(1)、计算字串长度
 > expr length “this is a test”
 14
(2)、抓取字串
 > expr substr “this is a test” 3 5
 is is
(3)、抓取第一个字符数字串出现的位置
 > expr index “sarasara”  a
 2
(4)、字串真实重现
 > expr quote sara
 sara
(5)、整数运算
 > expr 14 % 9
 5
 > expr 10 + 10
 20
 > expr 1000 + 900
 1900
 > expr 30 / 3 / 2
 5
 > expr 30 \* 3 (使用乘号时,必须用反斜线屏蔽其特定含义。因为shell可能会误解显示星号的意义)
 90
 > expr 30 * 3
 expr: Syntax error
(6)、增量计数
说明:expr在循环中用于增量计算。先将变量初始化为0,然后循环值加1,反引号的用法为命令替代。
> LOOP=0
> LOOP=`expr $LOOP + 1`
(7)、数值测试
说明:用expr测试一个数。如果试图计算非整数,则会返回错误。
> rr=3.4
> expr $rr + 1
expr: non-numeric argument
> rr=5
> expr $rr + 1
6
(8)、模式匹配
说明:expr也有模式匹配功能。可以使用expr通过指定冒号选项计算字符串中字符数。.*意即任何字符重复0次或多次。
> VALUE=account.doc
> expr $VALUE : ‘.*’
8
在expr中可以使用字符串匹配操作,这里使用模式抽取.doc文件附属名。
$expr $VALUE : ‘\(.*\).doc’
accounts
 
 
==============================================================================
条件变量替换: 
  Bash Shell可以进行变量的条件替换,既只有某种条件发生时才进行替换,替换
  条件放在{}中.
  (1) ${value:-word}
  当变量未定义或者值为空时,返回值为word的内容,否则返回变量的值.
  (2) ${value:=word}
  与前者类似,只是若变量未定义或者值为空时,在返回word的值的同时将
  word赋值给value
  (3) ${value:?message}
  若变量已经赋值的话,正常替换.否则将消息message送到标准错误输出(若
  此替换出现在Shell程序中,那么该程序将终止运行)
  (4) ${value:+word}
  若变量以赋值的话,其值才用word替换,否则不进行任何替换
  (5) ${value:offset}
  ${value:offset:length}
  从变量中提取子串,这里offset和length可以是算术表达式.
  (6) ${#value}
  变量的字符个数 (变量的字符个数,并不是变量个数)
  (7) ${value#pattern}
  ${value##pattern}
  去掉value中与pattern相匹配的部分,条件是value的开头与pattern相匹配
  #与##的区别在于一个是最短匹配模式,一个是最长匹配模式.
  (8) ${value%pattern}
  ${value%%pattern}
  和(7)类似,只是是从value的尾部于pattern相匹配,%与%%的区别与#与##一样
  (9) ${value/pattern/string}
  ${value//pattern/string}
  进行变量内容的替换,把与pattern匹配的部分替换为string的内容,/与//的区
  别与上同
 
 
=========================================================================
$# 传递到脚本的参数个数
$* 以一个单字符串显示所有向脚本传递的参数,与位置变量不同,参数可超过9个
$$ 脚本运行的当前进程ID号
$! 后台运行的最后一个进程的进程ID号
$@ 传递到脚本的参数列表,并在引号中返回每个参数
$- 显示shell使用的当前选项,与set命令功能相同
$? 显示最后命令的退出状态,0表示没有错误,其他表示有错误
 
=====================================================================================
####使用一个 declare 语句,我们可以限制赋给变量的值。 
 
以下是 declare 的语法: 
 
declare OPTION(s) VARIABLE=value 
 
下列选项用来确定变量能存放的数据的类型和分配给它的属性:
------------------------------------
内建命令的选项:
------------------------------------
选项 含义 
-a 变量为数组。 
-f 仅使用函数名。 
-i 把变量当作整数来对待;变量被赋值之后就进行算术计算。  
-p 显示每个变量的属性和值。当使用 -p 选项,其他选项就被忽略。  
-r 使得变量变为只读。这些变量不能被后来的赋值与语句赋值,同样也不可以unset。 
-t 给于每个变量 trace 属性。  
-x Mark each variable for export to subsequent commands via the environment. 
--------------------------------------------------------------
使用 + 代替 - 来关闭属性。当使用在函数中时,declare 建立本地变量。 
---------------------------------------------------------------
以下例子显示了对一个变量怎样赋于类型来影响它的值。
 
[peter@uplooking shell] declare -i VARIABLE=12
 
[peter@uplooking shell] VARIABLE=string
 
[peter@uplooking shell] echo $VARIABLE
0
 
[peter@uplooking shell] declare -p VARIABLE
declare -i VARIABLE="0"
 
注意Bash有一个选项来声明一个数字值,但是却没有选项用来声明字符串。这是因为,默认下,如果没有特别指明,变量可以拥有任何类型的数据:
 
[peter@uplooking shell] OTHERVAR=blah
 
[peter@uplooking shell] declare -p OTHERVAR
declare -- OTHERVAR="blah"
 
只要你限制了对变量的赋值,那它只能存放那种类型的数据。可能限制是整数,常量或者数组。
 
 
 
 
###多进制整型变量表示
declare -i number
number=base#number_in_the_base
如:
declare -i number
number=2#101
number=16#A9
 
========================================================
### until循环
until 循环和 while 循环非常相似, 除了循环执行直到 TEST-COMMAND 执行成功。只要这个命令测试失败,循环就继续。语法和 while 循环一样: 
    until TEST-COMMAND; do CONSEQUENT-COMMANDS; done 
返回状态是最后一个在列表 CONSEQUENT-COMMANDS 中执行的命令的退出状态,或者没有执行的话就是零。 TEST-COMMAND 可以,again, 是任何能退出带成功或者失败状态的命令,而 CONSEQUENT-COMMANDS 可以是任何UNIX命令,脚本或者shell结构。 
 
和我们先前已经解释的一样,“;” 出现的地方可以用一个或者更多新行来替代。 
 
eg.
 
#!/bin/bash
x=1
until [ $x -ge 10 ]
do
    echo $x
    x=`echo "$x+1"|bc`   
done
 
for i in 0 1 2 3 4 5 6 7 8 9 
do
    echo $i
done    
====================================
 
改进的 picturesort.sh 脚本 (第二天讲到的摄像头拍照排序的while循环”),测试现存的磁盘空间。如果没有足够的磁盘空间,就删除之前月份的图片: 
 
#!/bin/bash
 
# This script copies files from my homedirectory into the webserver directory.
# A new directory is created every hour.
# If the pics are taking up too much space, the oldest are removed.
 
while true; do 
    DISKFUL=$(df -h $WEBDIR | grep -v File | awk '{print $5 }' | cut -d "%" -f1 -)
 
    until [ $DISKFUL -ge "90" ]; do 
 
            DATE=`date +%Y%m%d`
            HOUR=`date +%H`
            mkdir $WEBDIR/"$DATE"
                                                                                
            while [ $HOUR -ne "00" ]; do
                    DESTDIR=$WEBDIR/"$DATE"/"$HOUR"
                    mkdir "$DESTDIR"
                    mv $PICDIR/*.jpg "$DESTDIR"/
                    sleep 3600
                    HOUR=`date +%H`
            done
 
    DISKFULL=$(df -h $WEBDIR | grep -v File | awk '{ print $5 }' | cut -d "%" -f1 -)
    done
 
    TOREMOVE=$(find $WEBDIR -type d -a -mtime +30)
    for i in $TOREMOVE; do
        rm -rf "$i";
    done
 
done
 
注意 HOUR 和 DISKFULL 变量的初始化以及 ls 和 date 带选项用法如何得到正确的 TOREMOVE 列表。 
 
 
==========================================================================
#### 数组变量#####
#### 建立数组 
数组是一个包含多个值的变量。任何变量都可以在数组中使用。数组的尺寸没有最大限制,也不要求成员变量连续索引或者赋值。数组是基于0的:第一个元素的下标以0开始。
 
间接的声明使用以下的语法来声明一个变量:
 
ARRAY[INDEXNR]=value 
 
INDEXNR 需要使用一个值为正数的数学表达式。 
 
一个数组的外部声明使用内建命令 declare 来完成: 
 
declare -a ARRAYNAME 
 
一个带有索引值的声明也是可以接受的,但是索引值将被忽略。
对数组的指定属性可以通过使用内建命令 declare 和 readonly。
属性对数组中的所有变量起作用;
你不能使用混合数组。 
 
数组变量也可以使用这种格式的复合赋值来建立:
 
ARRAY=(value1 value2 ... valueN) 
 
每个值将以这种形式 [indexnumber=]string 排列。索引号是可选的。如果提供,索引号就赋给它;otherwise the index of the element assigned is the number of the last index that was assigned, plus one. 这样的格式 declare 也可以接受。如果不提供索引值,那索引自动从零开始。 
 
在数组中加入缺少或者额外的成员使用以下语法:
 
ARRAYNAME[indexnumber]=value 
 
记住 read 内建命令提供 -a 选项,来允许对一个数组的成员变量进行读取和赋值。 
 
 
================================================================
#####对数组的变量解引用 
为了指明在一个数组中的项目的内容,为了指向一个数组中的一个项目的内容,使用{}。这样是必须的,正如你可以从下面的例子看出,来绕过扩展操作符的shell解释。如果索引的数字是 @ 或者 *,一个数组的所有的成员都将被引用。 
 
[peter@uplooking shell] ARRAY=(one two three)
 
[peter@uplooking shell] echo ${ARRAY[*]}
one two three
 
[peter@uplooking shell] echo $ARRAY[*]
one[*]
 
[peter@uplooking shell] echo ${ARRAY[2]}
three
 
[peter@uplooking shell] ARRAY[3]=four
 
[peter@uplooking shell] echo ${ARRAY[*]}
one two three four
 
不提供索引号码来指向某个数组的一个数字变量的内容和指向第一个元素的内容是一样的。
=================================================================
 
 
##### 删除数组变量 
unset 内建命令用来删除数组或者数组成员: 
 
[peter@uplooking shell] unset ARRAY[1]
 
[peter@uplooking shell] echo ${ARRAY[*]}
one three four
 
[peter@uplooking shell] unset ARRAY
 
[peter@uplooking shell] echo ${ARRAY[*]}
<--no output-->
=========================================================================
####### 数组的例子 
很难找到数组使用的实际例子。你能在你的系统里找到相当多的并没有做多少事情的脚本。
 
另外一个很难找到好的例子的原因不并不所有的shell都支持数组,所以这样会破坏兼容性。
 
终于找到一个数组例子,是一个通过网络分发Apache服务器的配文件到主机上的一个程序: 
 
#!/bin/bash
 
# $Id: chap10.xml,v 1.8 2005/09/05 12:39:22 tille Exp $
# $Log: chap10.xml,v $
# Revision 1.8  2005/09/05 12:39:22  tille
# sorry made a mistake
#
# Revision 1.6  2005/03/01 19:39:20  tille
# removed blank tracer images, added info on future debugging features, more keywords, minor corrections.
#
# Revision 1.5  2004/12/06 12:27:09  tille
# changes for new domainname, minor corrections
#
# Revision 1.6  2004/10/18 18:58:06  tille
# debugging, typos removed, replaced screenshots in chap9 with screen sections.
#
# Revision 1.5  2004/06/24 14:02:48  tille
# dded tracer image
#
# Revision 1.4  2004/06/15 08:47:12  tille
# more markup, index
#
# Revision 1.3  2004/05/22 13:34:18  tille
# review for fultus
#
# Revision 1.2  2004/04/26 13:24:41  tille
# updates by tabatha
#
# Revision 1.1.1.1  2004/02/11 16:59:50  tille
# initiele bash import
#
# Revision 1.3  2003/02/05 09:52:53  mbounine
# httpd restarting added.
#
# Revision 1.2  2003/02/05 08:11:32  mbounine
# Bug fixes.
#
# Revision 1.1  2003/02/04 15:41:35  mbounine
# Script for syncing httpd config between web farm hosts.
# Initial release.
#
if [ $(whoami) != 'root' ]; then
        echo "Must be root to run $0"
        exit 1;
fi
if [ -z $1 ]; then
        echo "Usage: $0 "
        exit 1
fi
 
httpd_conf_new=$1
httpd_conf_path="/usr/local/apache/conf"
login=htuser
 
farm_hosts=(web03 web04 web05 web06 web07)
 
for i in ${farm_hosts[@]}; do
        su $login -c "scp $httpd_conf_new ${i}:${httpd_conf_path}"
        su $login -c "ssh $i sudo /usr/local/apache/bin/apachectl graceful"
 
done
exit 0
 
现在面临一个问题问题:
 
“...在Peter的公司,我们的web站点上有些演示,每周有人得对全部演示进行测试。所以我做了一个cron任务来把所有可能的候选填入一个数组, 使用 date +%W 来找到年中的某周,使用取模操作来找到正确的索引。幸运的人通过email得到通知。” 
 
以下是他的解决办法:
 
#!/bin/bash
# This is get-tester-address.sh 
#
# First, we test whether bash supports arrays.
# (Support for arrays was only added recently.)
#
whotest[0]='test' || (echo 'Failure: arrays not supported in this version of
bash.' && exit 2)
                                                                                
#
# Our list of candidates. (Feel free to add or
# remove candidates.)
#
wholist=(
     'Bob Smith '
     'Jane L. Williams '
     'Eric S. Raymond '
     'Larry Wall '
     'Linus Torvalds '
   )
#
# Count the number of possible testers.
# (Loop until we find an empty string.)
#
count=0
while [ "x${wholist[count]}" != "x" ]
do
   count=$(( $count + 1 ))
done
                                                                                
#
# Now we calculate whose turn it is.
#
week=`date '+%W'`        # The week of the year (0..53).
week=${week#0}           # Remove possible leading zero.
                                                                                
let "index = $week % $count"   # week modulo count = the lucky person
 
email=${wholist[index]}     # Get the lucky person's e-mail address.
                                                                                
echo $email         # Output the person's e-mail address.
 
这个脚本然后被其他脚本使用,比如这个,使用一个 here 文档: 
 
email=`get-tester-address.sh`   # Find who to e-mail.
hostname=`hostname`            # This machine's name.
                                                                                
#
# Send e-mail to the right person.
#
mail $email -s '[Demo Testing]' <The lucky tester this week is: $email
                                                                                
Reminder: the list of demos is here:
    http://web.example.com:8080/DemoSites
                                                                                
(This e-mail was generated by $0 on ${hostname}.)
EOF
 
 
=============================
 
1.定义函数
funcation name()
{
   command1
   ....
}

函数名()
   {
   command1
   ...
   }
eg.
 
#!/bin/bash
#hellofun
function hello()
{
echo "hello,today is `date`"
return 1
}
 
2.函数调用
#!/bin/bash
#hellofun
function hello()
{
echo "hello,today is `date`"
return 1
}
echo "now going to the function hello"
hello
echo "back from the function"
所以调用函数只需要在脚本中使用函数名就可以了。
 
3.参数传递
像函数传递参数就像在脚本中使用位置变量$1,$2...$9
 
4.函数文件
函数可以文件保存。在调用时使用". 函数文件名"(.+空格+函数文件名)
如:
hellofun.sh
#!/bin/bash
#hellofun
function hello()
{
echo "hello,today is `date`"
return 1
}
 
func.sh
#!/bin/bash
#func
. hellofun.sh
echo "now going to the function hello"
echo "Enter yourname:"
read name
hello $name
echo "back from the function"
 
[peter@uplooking 1]$ ./func.sh
now going to the function hello
Enter yourname:
hh
hello,hh today is Thu Mar 6 15:59:38 CST 2008
back from the function
 
5.检查载入函数 set
删除载入函数 unset 函数名
 
6.函数返回状态值 return 0、return 1


------------------------------------------------------------------------------------------------------------

第四天

1.正则表达式
一种用来描述文本模式的特殊语法,由普通字符以及特殊字符(元字符)组成
^    ----只匹配行首
$    ----只匹配行尾
*    ----匹配0个或多个此单字符
[]   ----只匹配[]内字符,可以使用-表示序列范围[1-5]
\    ----屏蔽一个元字符的特殊含义
.    ----匹配任意单字符
pattern\{n\} 只用来匹配前面pattern出现的次数,n为次数
pattern\{n,\}只用来匹配前面pattern出现的次数,至少为n
pattern\{n,m\}只用来匹配前面pattern出现的次数,次数在n-m之间

eg:
A\{3\}B   AAAB
A\{3,\}B AAAB AAAAB ...
A\{3,5\}B AAAB AAAAB AAAAAB

2.find命令     ----查找文件和目录
find pathname -options [-print -exec -ok]
pathname --查找的目录路径. .--表示当前目录,/表示根目录
-print 输出
-exec 对匹配的文件执行该参数所给出的shell命令,相应命令形式为'command'{} \;'    注意{}和\;之间的空格
-ok    与-exec相同,不过执行命令前会有提示
options   :
-name
-perm
-user
-group
-mtime -n +n (atime,-ctime) 修改时间(访问时间,创建时间)
-size n[c]
-type 查找某一类型的文件
eg.
[peter@uplooking 1]$ find ./ -mtime +(-)5
./helloworld.sh
./nohup.out
查看./目录(当前)下修改时间超过5天的文件

3.grep介绍
grep -c 输出匹配行计数
grep -i 不区分大小写
grep -h 查询多文件时不显示文件名
grep -H 显示文件名
grep -l 查询多文件时只输出包含匹配字符的文件名
grep -n 显示匹配行及行号
grep -s 不显示不存在或不匹配文本的错误信息
grep -v 显示不包含匹配文本的所有行(过滤文本)

eg.
[peter@uplooking 1]$ grep -n 's.a' myfile
2:/dev/sda1              20G 3.3G   16G 18% /
4:/dev/sda2              79G   18G   58G 23% /u01
5:/dev/sda4              28G 3.9G   22G 15% /u02

[peter@uplooking 1]$ grep -n '2$' myfile
5:/dev/sda4              28G 3.9G   22G 15% /u02

[peter@uplooking 1]$ grep root /etc/passwd
root:x:0:0:root:/root:/bin/bash
operator:x:11:0:operator:/root:/sbin/nologin

[peter@uplooking 1]$ grep -n root /etc/passwd
1:root:x:0:0:root:/root:/bin/bash
12:operator:x:11:0:operator:/root:/sbin/nologin

[peter@uplooking 1]$ grep -v bash /etc/passwd | grep -v nologin
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
news:x:9:13:news:/var/spool/news:
mailnull:x:47:47::/var/spool/mqueue:/dev/null
xfs:x:43:43:X Font Server:/etc/X11/fs:/bin/false
rpc:x:32:32:Portmapper RPC user:/:/bin/false
nscd:x:28:28:NSCD Daemon:/:/bin/false
named:x:25:25:Named:/var/named:/bin/false
squid:x:23:23::/var/spool/squid:/dev/null
ldap:x:55:55:LDAP User:/var/lib/ldap:/bin/false
apache:x:48:48:Apache:/var/www:/bin/false

[peter@uplooking 1]$ grep -c false /etc/passwd
7
===================
如果我们只想显示那些使用字符串 “root” 开头的行:

[peter@uplooking 1]$grep ^root /etc/passwd
root:x:0:0:root:/root:/bin/bash
============
如果我们想看哪个账号什么shell都没有分配,我们搜索行结束符 “:”:

[peter@uplooking 1]$grep :$ /etc/passwd
news:x:9:13:news:/var/spool/news:
========
如果你想找到是一个分隔的字的字符串(用空格来包围的),最好使用 -w,就像在这个例子中我们现实root分区的信息那样:
========
[peter@uplooking 1]$grep -w / /etc/fstab
LABEL=/                 /                       ext3    defaults        1 1

如果不使用这个选项,系统表中的所有行都会被显示。
========
[peter@uplooking 1]$ grep [yf] /etc/group
sys:x:3:root,bin,adm
tty:x:5:
mail:x:12:mail,postfix
ftp:x:50:
nobody:x:99:
floppy:x:19:
xfs:x:43:
nfsnobody:x:65534:
postfix:x:89:

[peter@uplooking 1]$ touch a{1,2,9}.xml b{3,4,8}.xml

[peter@uplooking 1]$ ls *[1-9].xml
 
================

使用 “.” 进行一个单字符匹配。如果你想得到一个以 “c” 开头且以 “h” 结尾的所有5位字符的英语辞典词条(来解决 crosswords 游戏,就是那个纵横填字游戏):

[peter@uplooking 1]$ grep '\' /usr/share/dict/words
catch
clash
cloth
coach
couch
cough
crash
crush


要匹配多个字符,使用星号。这个例子从系统的词典中选择了所有以 “c” 开头且以 “h” 结尾的单词:

[peter@uplooking 1]$ grep '\' /usr/share/dict/words
caliph
cash
catch
cheesecloth
cheetah
--output omitted--

如果你想在一个文件或者输出中找到包含点号 、星号字符的行,使用 grep -F:

[peter@uplooking 1]$ grep * /etc/profile

[peter@uplooking 1]$ grep -F '*' /etc/profile
for i in /etc/profile.d/*.sh ; do

[peter@uplooking 1]$ grep -F '.' /etc/profile
if [ -z "$INPUTRC" -a ! -f "$HOME/.inputrc" ]; then
for i in /etc/profile.d/*.sh ; do
        . $i
===========================
*和?分别匹配任何字符串和任何单个字符,引用这些特殊字符来匹配他们的字面值:

[peter@uplooking 1]$ touch "*"

[peter@uplooking 1]$ ls "*"
*
============
[peter@uplooking home]$ ls -l [a-cx-z]*
drwxr-xr-x    2 cathy     cathy        4096 Nov 18 2009 app-defaults/
drwxrwxr-x    4 cathy    cathy          4096 Nov 14 2009 arabic/
drwxrwxr-x    2 cathy    cathy          4096 Nov  4 2006 bin/
drwxr-xr-x    7 cathy    cathy          4096 Sep  2 2009 crossover/
drwxrwxr-x    3 cathy    cathy          4096 Mar 22 2009 xml/

列出了 peter用户home目录里的所有以“a”,“a”,“b”,“c”,“x”,“y”开头的文件。

===========
字符族可以用方括号来指定,使用语法 [:CLASS:],CALSS是定义在POSIX标准中的取以下某个值

     POSIX 类                  描述
    ----------------------------------------------------------------------------
    [:alnum:]                  字母和数字
    [:alpha:]                  字母
    [:lower:]                  小写字母
    [:upper:]                  大写字母
    [:blank:]                  空白字符(空格和制表符)
    [:space:]                  所有空格符(比[:blank:]包含的范围广)
    [:cntrl:]                  不可打印的控制字符(退格、删除、警铃...)
    [:digit:]                  十进制数字
    [:xdigit:]                 十六进制数字
    [:graph:]                  可打印的非空白字符
    [:print:]                  可打印字符
    [:punct:]                  标点符号
    [:ascii:]                  ascii码值
    [:graph:]                  非空字符(非空格、控制字符)

eg:

[peter@uplooking 1]$ ls -ld [[:digit:]]*
drwxrwxr-x    2 cathy    cathy        4096 Apr 20 13:45 2/

[peter@uplooking 1]$ ls -ld [[:upper:]]*
drwxrwxr--    3 cathy   cathy           4096 Sep 30  2001 Nautilus/
drwxrwxr-x    4 cathy   cathy           4096 Jul 11  2002 OpenOffice.org1.0/
-rw-rw-r--    1 cathy   cathy         997376 Apr 18 15:39 Schedule.sdc



练习:
一、有一个数据文件/home/peter/datafile.txt,完成下列工作:
1. 显示所有包含San的行
 cat /root/peter/datafile.txt |grep  -n "San"

2.显示所有以J开始的人名所在的行
 grep ^J ~/peter/datafile.txt
3.显示所有以700结尾的行
 grep 700$ /root/peter/datafile.txt

4.显示所有不包括834的行
 grep -v 834 /root/peter/datafile.txt
5.显示所有生日在December的行
grep -n :10 /root/peter/datafile.txt
6.显示所有电话号码的区号为498的行
grep -n  :498   /root/peter/datafile.txt
7.显示所有这样的行:它包含一个大写字母,后跟四个小写字母,一个冒号,一个空格,和一个大写字母
grep [A-Z][a-z][a-z][a-z][a-z]:[:blank:] [A-Z] /root/peter/datafile.txt
8.显示姓以K或k开头的行
grep [[:blank:]][Kk].*[a-z]: /root/peter/datafile.txt
grep '[[:blank:]][Kk][a-z]\{1,\}:'
9.显示工资为六位数的行,并在前面加行号
grep -n :......$   /root/peter/datafile.txt
10.显示包括Lincoln或lincoln的行,并且grep对大小写不敏感.
grep  -i  lincoln  /root/peter/datafile.txt
二、显示出你的系统中所有以Bash Shell登录的用户
grep '/bin/bash' /etc/passwd|cut -d : -f 1

三、显示出/etc/group目录中,显示出所有以字符串"daemon"开始的所有行,并显示其行号。
grep -n ^daemon /etc/group

四、查找出系统中包含多少README文件,不包括*README*形式的文件。

find / -name README|grep -w README
五、把你系统中50小时以内被修改过的文件列出来。

find / -mmin -300

*****注意:man grep有海量的信息,注意阅读查看!*****


---------------------------------------------------------------------------------------------------------

第五天

******sed介绍******

sed是一个流编辑器,不与初始化文件打交道,它操作的只是一个拷贝,是用来从文件读取文本或者从管道实现基本的变化。结果送到标准输出。sed命令的语法不指定输出文件,但是结果可以通过使用输出重定向来写入到文件中。编辑器并不改变原来的文件。

sed和其它编辑器比如vi和sed的区别在于它能够过滤来自管道的输入。在编辑器运行的时候你不要去干涉它。此特性允许你在脚本中使用编辑命令,极大的方便了重复性编辑任务。当面对文件中大量的文本替换的时候,sed将是一个极大的帮助。
 
sed调用:
1.命令 sed [options] '正则表达式sedcommand' input-files
2.script :sed [options] -f sedscript input-files

sed在文本中查询文本的方式:
-行号,可以是简单数字,或一个行号范围
-使用正则表达式

x ----行号
x,y ----行号范围从x到y
x,y! ---不包含行号x到y

sed命令选项:
-n 安静模式,不在屏幕上打印
-e SCRIPT      把SCRIPT指定的命令加入到在处理输入的时候运行的系列命令。
-f     把包含在 SCRIPT-FILE 文件中的命令加入到处理输入的时候运行的系列命令之中。
-V     打印版本信息然后退出。

基本sed命令
p 打印匹配行
= 显示文本行号
a\ 在定位行号后附加新文本信息
i\在定位行号前插入新文本信息
d 删除定位行
c\用新文本替换定位文本
s 使用替换模式替换相应模式
r 从另一个文件中读文本
w 写文本到一个文件
q 第一个模式匹配完成后退去
l 显示与八进制ascii代码等价的控制字符
{}在定位行执行命令组
n 从一个文件中读文本下一行,并附加在下一行
g 将模式2粘贴到/pattern n/
y 传送字符

=================
eg.
打印包含pattern的行

你可以用grep做不少事情,但是你不能用它来 “查找和替换” 。让我们开始。

这是我们的文本文件例子:

[peter@uplooking 1]$cat -n example
     1  This is the first line of an example text.
     2  It is a text with erors.
     3  Lots of erors.
     4  So much erors, all these erors are making me sick.
     5  This is a line not containing any errors.
     6  This is the last line.

========================

我们想让sed找到所有“erors”的行,我们使用 p 来得到结果:

[peter@uplooking 1]$ sed  '/erors/p' example
This is the first line of an example text.
It is a text with erors.
It is a text with erors.
Lots of erors.
Lots of erors.
So much erors, all these erors are making me sick.
So much erors, all these erors are making me sick.
This is a line not containing any errors.
This is the last line.
================

就像你看到的,sed打印出了整个文件,但是包含搜索字符串的行被打印了2次。这不是我们想要的。为了只符合要求的行,使用 -n 选项:

[peter@uplooking 1]$sed -n '/erors/p' example
It is a text with erors.
Lots of erors.
So much erors, all these erors are making me sick.

=================
删除包含pattern的输入行

我们使用同样的文本文件作为例子。现在我们只想看到那些not包含所要搜索字符串的行:

[peter@uplooking 1]$sed '/erors/d' example
This is the first line of an example text.
This is a line not containing any errors.
This is the last line.


d 命令使得被排除的行不显示出来。

匹配以第一个模板开头的和第二个模板结尾的行应该写成这样:

[peter@uplooking 1]$sed -n '/^This.*errors.$/p' example
This is a line not containing any errors.

=====================

行的范围

这次我们想单独列出包含错误的行。在例子中它们是第2-4行。使用 d 命令来指定地址的范围:

[peter@uplooking 1]$sed '2,4d' example
This is the first line of an example text.
This is a line not containing any errors.
This is the last line.

==========

要打印出文件中以特定行开头的直到最后一行的,使用如下的命令:

[peter@uplooking 1]$sed '3,$d' example
This is the first line of an example text.
It is a text with erors.


这个例子中只打印出例子里的头两行。
==============================
下面的命令打印出饱含 “a text”的第一次,直到下个包含 “a line” 的一行:

[peter@uplooking 1]$sed -n '/a text/,/This/p' example
It is a text with erors.
Lots of erors.
So much erors, all these erors are making me sick.
This is a line not containing any errors.

=============

用sed来查找替换

在例子中,我们会搜索和替换错误来代替只选择(或者不选择)包含需要查找字符串的行。

[peter@uplooking 1]$sed 's/erors/errors/' example
This is the first line of an example text.
It is a text with errors.
Lots of errors.
So much errors, all these erors are making me sick.
This is a line not containing any errors.
This is the last line.

======================

就像你看到的,这并不完全就是我们想要得:在第4行,只有第一个待搜索字符串被替换了,左面还有一个“eror”没有被替换。使用g命令来使sed检查所有的行而不在搜索到第一个匹配字符串后就停止:

[peter@uplooking 1]$sed 's/erors/errors/g' example
This is the first line of an example text.
It is a text with errors.
Lots of errors.
So much errors, all these errors are making me sick.
This is a line not containing any errors.
This is the last line.

===================

要在文件的每一行开始处插入一个字符串,比如引用:

[peter@uplooking 1]$sed 's/^/> /' example
> This is the first line of an example text.
> It is a text with erors.
> Lots of erors.
> So much erors, all these erors are making me sick.
> This is a line not containing any errors.
> This is the last line.

==================

在每行的尾部插入字符串:

[peter@uplooking 1]$sed 's/$/EOL/' example
This is the first line of an example text.EOL
It is a text with erors.EOL
Lots of erors.EOL
So much erors, all these erors are making me sick.EOL
This is a line not containing any errors.EOL
This is the last line.EOL

=================

多个查找替换命令用单独选项 -e 来隔开:

[peter@uplooking 1]$sed -e 's/erors/errors/g' -e 's/last/final/g' example
This is the first line of an example text.
It is a text with errors.
Lots of errors.
So much errors, all these errors are making me sick.
This is a line not containing any errors.
This is the final line.

=====================

记住,sed默认把结果送到标准输出,比如你的终端窗口。如果你想把输出保存到文件,使用重定向:

sed option 'some/expression' file_to_process > sed_output_in_a_file

=======================
大量的sed例子可以在你机器的启动文件里找到,通常在 /etc/init.d 或者 /etc/rc.d/init.d。切换到包含初始化脚本的目录中然后输入ru下命令:

grep sed *



====================

eg.
[peter@uplooking 1]$ sed -n '2p' myfile
c
打印myfile第2行
===========
[peter@uplooking 1]$ sed -n '2,4p' myfile
c
f
b
打印第二行到第四行
===============
[peter@uplooking 1]$ sed -n '/a/p' myfile
a
打印匹配a的行
===================
[peter@uplooking 1]$ sed -n '2,/2/p' myfile
c
f
b
1
2
打印第二行到匹配'2'的行
=====================
s命令替换
[peter@uplooking 1]$ sed 's/b/a/p' myfile
a
a
a
c
d
e
替换b为a

================================
sed命令r ---从文件中读取选定的行,读入输入文件中,显示在匹配的行后面
eg.
[peter@uplooking 1]$ cat 11
*******************Alaska***************
[peter@uplooking 1]$ sed '/a/r 11' myfile
a
*******************Alaska***************
b
c
d
e
==================================
写入命令:w  将输入文件中的匹配行写入到指定文件中
eg.
[peter@uplooking 1]$ cat 11
b
[peter@uplooking 1]$ sed -n '/a/w 11' myfile  (找匹配的a行进行下个命令替换)
[peter@uplooking 1]$ cat 11
a
================================================================
追加:a  将文本追加到匹配行的后面。sed要求在a后加\,不止一行的以\连接
eg.
[peter@uplooking 1]$ sed '/b/a\****************hello*************\-------------china---------' myfile
a
b
****************hello*************-------------china---------
c
d
e
===================================================================
插入命令:i   将文本插入到匹配行的前面。sed要求在a后加\,不止一行的以\连接
eg.
[peter@uplooking 1]$ sed '/b/i\
> THE CHARACTER B IS BEST\
> *******************************' myfile
a
THE CHARACTER B IS BEST
*******************************
b
c
d
e
===================================================
下一个:n 从一个文件中读文本下一行,并附加在下一行

退出命令 q 打印多少行后退出
eg.
[peter@uplooking 1]$ sed '3q' myfile
a alert
b best
c cook

sed script:
sed -f scriptfile myfile

======================================
          ******sed总结******

sed 流编辑器是一个强大的命令行工具,能处理数据流:能从管道读取输入行。这使得它社和非交互使用。 sed 编辑器使用类似 vi 的命令且支持正则表达式。

sed 工具可以从命令行或者脚本文件读取命令。他经常用来实现查找和替换的包含特定字符串的行的工作。
====================================================
***sed练习***

这些练习用来更好的证明sed能做些什么。

   1.打印在你scripts目录中的文件列表,用“.sh”来结尾。注意你可能需要unalias ls。把结果放入一个临时文件。
   2.完成一张文件列表:包含了在 /usr/bin 中且第二个字符是“a”的文件,把结果放入一个临时文件。
  
   3.删除每个临时文件的最初3行。
  
   4.把仅包含“an”的行打印到标准输出。
  
   5.建立一个包含sed命令的文件来完成之前的两个任务。在这个文件中加入一个额外的命令使得在以字符串 “man”开始的每行加入一个例如 “*** This might have something to do with man and man pages ***” 的字符串。检查结果。

   6.一个稍长的根目录的列表 /,用于输入。建立一个包含 sed 命令的文件来检查符号连接和纯文本文件。如果一个文件为符号连接,用类似 “--This is a symlink--” 这样的一行开始。如果文件是纯文本,在同一行上加入字符串,加入一个类似 “<--- this is a plain file” 的注释。
  
   7.建立一个从文件中显示包含空格的脚本。这个脚本应该使用一个 sed 脚本来向用户显示明显的信息。






====================

******awk介绍******
Gawk是通常在UNIX系统下使用的另外一个流行的流编辑器awk的GNU版本。尽管awk程序常常只是一个gawk的连接,但是我们还是称作它为awk。

awk的最基本的作用是搜索含有1个或者多个模板的文件中的行或者其他文本块。当一行符合搜索的patterns,在该行就会实现指定的动作。

在awk中的程序和许多其他语言中的程序是不一样的,因为awk程序是“数据驱动的”:你先描述你想处理的数据,然后当找到它们的时候怎么处理。许多其他的语言是“过程的“。你需要具体的描述程序该采取的措施。当使用过程化的语言时,通常更难清楚地描述你需要处理的数据。正因为如此,awk程序经常能清爽容易地读写。
awk可从文件或字符串值基于指定规则浏览和抽取信息
=======================
awk三种调用方式:
1.命令行方式
awk [-F field-sperator]'pattern{active}' input-files
awk [-F field-sperator]'command' input-files

2.awk脚本
所有awk命令插入一个文件,并使awk程序可执行,然后用awk命令解析器作为脚本的首行,以便通过键入脚本名称来调用。

3.awk命令插入一个单独文件
awk -f awk-script-file input-files

awk脚本由模式和动作组成
分隔符、域、记录

变量 $1, $2, $3, ..., $N把输入行的第一第二第三直到最后一个域保存起来。变量$0把整行的值保存起来。在执行du -h结果描述中,我们看到df命令输出有6栏。
由awk解释的块:第一栏是$1,第二栏是$2,以此类推,$0文件中的所有记录。

==============================================

awk 给表达式赋值
关系运算符:
<             小于  
>             大于
==           等于
!=            不等于
>=           大于等于
<=           小于等于
~              匹配
!~            不匹配

eg.
[peter@uplooking shell]$ cat employee
Tom Jones       4424    5/12/66 543354
Mary Adams      5346    11/4/63 28765
Sally Chang     1654    7/22/54 650000
Billy Black     1683    9/23/44 336500
[peter@uplooking shell]$ awk '$2~/Adams/' employee
Mary Adams      5346    11/4/63 28765

条件表达式:
condition   expression1?expression2:expression3
eg.
awk '{max=($1>$2) ? $1:$2;print max}' filename

运算符
+,-,*,/,%,^,&&,||,!

[peter@uplooking shellpeter]$ cat /etc/passwd |awk -F: '\   (\为换行)
NF!=7{\
printf("line %d does not have 7 fields:%s\n",NR,$0)}\
$1!~/[A-Za-z0-9]/{printf("line %d,nonalphanumberic user id:%s\n",NR,$0)}\
$2=="*"{printf("line %d,no password:%s\n",NR,$0)}'

awk编程
递增操作符 x++,++x
递减操作符 x--,--x


============================================

对ls -l的输出操作。 print 语句像这样使用这些域:

[peter@uplooking test]$ ls -l | awk '{ print $5 }'
160orig
121script.sed
120temp_file
126test
120twolines
441txt2html.sh

这个命令把包含文件大小的第五栏和包含文件名的最后一栏打印出来。除非你使用正式的方法来用一个逗号来分割你想打印的列,否则输出并不十分适合阅读。这样的情况下,默认的输出分割字符,通常是一个空格,将被放入每个输出域。

=================================================
格式化域

没有格式化,只使用输出分隔符的话看上去比较破,插入几个制表符和指示输出的标记会使它变得漂亮很多:

[peter@uplooking test]$ ls -ldh * | grep -v total | \
awk '{ print "Size is " $5 " bytes for " $9 }'
Size is 160 bytes for orig
Size is 121 bytes for script.sed
Size is 120 bytes for temp_file
Size is 126 bytes for test
Size is 120 bytes for twolines
Size is 441 bytes for txt2html.sh


注意反斜杠的用法,让shell不把它翻译成分隔命令而使其在下一行继续很长的输入。虽然命令行的输入实际上是没有长度限制的,但是你的显示器不是,打印的纸当然也不是。使用反斜杠也允许拷贝和粘贴以上行到一个终端窗口。

ls的-h选项用来支持把大文件的字节数转换成更容易读的格式。当把目录作为参数的时候长列表的输出显示目录中快的总数。这行对我们并没有什么用处,所以我们加上一个*,同样的原因我们同样加上-d选项。。

===================================

下表给出了特殊格式化字符的总揽:

gawk的格式化字符:

转义字符     含义
\a     Bell character
\n     Newline character
\t     Tab

引用,$和其他元字符应该使用反斜杠来进行转义。

===============================================

print命令和正则表达式

用斜杠把正则表达式包含起来可以当作一个pattern。然后正则表达式测试整个文本的每条记录。语法如下:

awk 'EXPRESSION { PROGRAM }' file(s)

下面的例子现实了只有本地磁盘设备的信息,网络文件系统没有显示:

[root@uplooking ~]# df -h | awk '/dev\/sd/ { print $6 "\t: " $5 }'
/       : 46%

斜杠也需要转义,因为对于awk它们有着特殊的含义。

=============================

下面的另外一个例子是我们在 /etc 目录搜索以 “.conf” 结尾和 “a” 或者 “x” 开头的文件,使用扩展的正则表达式:

[peter@uplooking etc]$ ls -l | awk '/\<(a|x).*\.conf$/ { print $9 }' 
amd.conf
antivir.conf
xcdroast.conf
xinetd.conf

这个例子说明了在正则表达式中.的特殊意义。第一个表明了我们想要搜索在第一个搜索字符串之后的任何字符,第二个因为是要查找字符串的一部分所以被转义了(文件名的结束)。

==================

特殊的patterns

为了在输出之前加上注释,使用BEGIN语句:

[peter@uplooking etc]$ ls -l | \
awk 'BEGIN { print "Files found:\n" } /\<[a|x].*\.conf$/ { print $9 }'
Files found:
amd.conf
antivir.conf
xcdroast.conf
xinetd.conf

加上 END 语句能插入文本在整个输入被处理之后:

[peter@uplooking etc]$> ls -l | \
awk '/\<[a|x].*\.conf$/ { print $9 } END { print \
"Can I do anything else for you, mistress?" }'
amd.conf
antivir.conf
xcdroast.conf
xinetd.conf
Can I do anything else for you, mistress?

==========================================

Gawk脚本

由于往往命令都有点长,你可能想把他们放到脚本中,来重用它。一个awk脚本包含定义pattern和动作的awk语句。

作为一个说明,我们将建立一个报告来显示占用率最高的分区。

[peter@uplooking ~]$ cat diskrep.awk
BEGIN { print "*** WARNING WARNING WARNING ***" }
/\<[8|9][0-9]%/ { print "Partition " $6 "\t: " $5 " full!" }
END { print "*** Give money for new disks URGENTLY! ***" }

[peter@uplooking ~]$ df -h | awk -f diskrep.awk
*** WARNING WARNING WARNING ***
Partition /usr  : 97% full!
*** Give money for new disks URGENTLY! ***

awk先打印一个开始信息,然后格式化所有包含一个8或者9开头的词的行,然后后接一个数字和百分符号,然后加入结束信息。

===================================================

Gawk变量

既然awk处理输入文件,他使用几个变量。一些是可以编辑的,一些是只读的。
6.3.1. 输入域的分隔符

域分隔符,既不是一个单独的字符也不是一个普通的表达式,是控制awk把一个输入分割成几个域。输入记录按分割定义进行字符顺序扫描;域就是在相符的那些文字中间的那部分。

域分隔符代表内建的变量FS,注意POSIX标准的shell使用的变量IFS和他是有一些区别的。

域分隔符变量的值可以在awk程序中用赋值操作符 = 来改变。通常最好的执行时间是一开始也就是还没有处理任何输入的时候,因此第一个记录就被随合适的分隔符一起读取。要这么做,清使用特殊的BEGIN pattern。

以下的例子,我们编制了一条命令来显示系统里的所有用户及其描述:

[peter@uplooking ~]$ awk 'BEGIN { FS=":" } { print $1 "\t" $5 }' /etc/passwd
--output omitted--
kelly    Kelly Smith
franky    Franky B.
eddy    Eddy White
willy    William Black
cathy    Catherine the Great
sandy    Sandy Li Wong

[peter@uplooking ~]$

在一个awk脚本中,它看起来像这样:

[peter@uplooking ~]$ cat printnames.awk
BEGIN { FS=":" }
{ print $1 "\t" $5 }

[peter@uplooking ~]$ awk -f printnames.awk /etc/passwd
--output omitted--

小心地选择输入分隔域来防止出现问题。一个例子来说明这个:说你需要输入想这样的行的形式:

“Sandy L. Wong, 64 Zoo St., Antwerp, 2000X”

你这样写了一个打印出记录中人的名字的命令行或者是脚本:

[peter@uplooking ~]$awk 'BEGIN { FS="," } { print $1, $2, $3 }' inputfile

但是可能一个人有PhD,而且可能写成这样:

“Sandy L. Wong, PhD, 64 Zoo St., Antwerp, 2000X”

你的 awk 会给出错误的输出。需要的话,使用额外的一个 awk 或者 sed 来统一数据输出的格式。

默认的输入分隔符是一个或多个空格和制表符。

===========================

输出域分隔符

在输出中域通常被空格分隔。当你对print命令使用正确的语法且字段用逗号分隔时,这将很明显的:

[peter@uplooking test]$ cat test
record1         data1
record2         data2

[peter@uplooking test]$ awk '{ print $1 $2}' test
record1data1
record2data2

[peter@uplooking test]$ awk '{ print $1, $2}' test
record1 data1
record2 data2

[peter@uplooking test]$

如果你不输入逗号, print将把输出的项目全部当成一个字段,因此省略默认输出分隔符,OFS的使用。

在摄制了这个内建变量的值之后任何字符串可以被用作一个输出域。

===============================================================

输出记录分隔符

整个print语句的输出叫做输出记录。每个在输出记录里的print命令的结果,输出一个叫做输出记录分隔符ORS的字符串。这个变量的默认值是 “\n”,一个换行符。因此,每个print语句生成一个单独的行。

要改变输出域和记录的,只要给OFS和ORS赋新的值:

[peter@uplooking test]$ awk 'BEGIN { OFS=";" ; ORS="\n-->\n" } \
{ print $1,$2}' test
record1;data1
-->
record2;data2
-->

[peter@uplooking test]$

如果ORS的值不包含换行,那么程序中的输出就会输出成一个单独行。

===================

记录的数量

内建的NR包含了处理过的记录的数量(FNR表示当前文件的记录数 NR表示到此为止的记录数)。在读入一个新的输入行之后他会自行增加一次。你可以用它来计算记录的总数,或者在每个输出记录中:

[peter@uplooking test]$ cat processed.awk
BEGIN { OFS="-" ; ORS="\n--> done\n" }
{ print "Record number " NR ":\t" $1,$2 }
END { print "Number of records processed: " NR }

[peter@uplooking test]$ awk -f processed.awk test
Record number 1:        record1-data1
--> done
Record number 2:        record2-data2
--> done
Number of records processed: 2
--> done

==============================================

用户定义的变量

除了内建的变量之外,你也可以定义自己的变量。当 awk 碰到一个不存在的变量(没有事先定义的)的引用时,这个变量就被创建并且使用一个空字符串进行赋值。对于后来所有的引用,就是该变量最后被赋予的那个值。变量可以是一个字符串或者一个数字。输入域的内容也可以被赋予变量。

值可以直接用 = 来赋值,或者你可以使用现有变量的值和其他操作符组合:

[peter@uplooking ~]$cat revenues
20021009        20021013        consultancy     BigComp         2500
20021015        20021020        training        EduComp         2000
20021112        20021123        appdev          SmartComp       10000
20021204        20021215        training        EduComp         5000

[peter@uplooking ~]$cat total.awk
{ total=total + $5 }
{ print "Send bill for " $5 " dollar to " $4 }
END { print "---------------------------------\nTotal revenue: " total }

[peter@uplooking ~]$awk -f total.awk test
Send bill for 2500 dollar to BigComp
Send bill for 2000 dollar to EduComp
Send bill for 10000 dollar to SmartComp
Send bill for 5000 dollar to EduComp
---------------------------------
Total revenue: 19500

类似于C的简写 VAR+= value 也是可以接受的。
   
============================================

更多例子

当我们使用awk脚本时,写输出文件的例子会变得更加容易:

[peter@uplooking html]$ cat make-html-from-text.awk
BEGIN { print "\nAwk-generated HTML\n\n
" }
{ print $0 }
END { print "
\n\n" }

而且当用 awk 来替代 sed 后,命令也变得更加直截了当:

[peter@uplooking html]$ awk -f make-html-from-text.awk testfile > file.html

[提示]     在你系统上的awk例子。

我们再次回到包含你系统启动脚本的目录。输入一个和以下相似的命令来查看更多awk命令的使用方法:

grep awk /etc/init.d/*

========================================

格式输出:
打印函数—
[peter@uplooking shellpeter]$ date
Mon Mar 10 15:15:47 CST 2008
[peter@uplooking shellpeter]$ date |awk '{print "Month:" $2"\nYear:" $6}'
Month:Mar
Year:2008
[peter@uplooking shellpeter]$ awk '/Sally/{print "\t\tHave a nice day,"$1"\t"$2}' employee
                Have a nice day,Sally   Chang

printf函数
[peter@uplooking shellpeter]$ echo "LINUX"|awk '{printf "|%-10s|\n",$1}'
|LINUX     |
[peter@uplooking shellpeter]$ echo "LINUX"|awk '{printf "|%10s|\n",$1}'
|     LINUX|

~匹配符
[peter@uplooking shellpeter]$ awk '$1~/Tom/{print $1,$2}' employee
Tom Jones

=====================================

BEGIN模块
BEGIN模块后面紧跟着动作块,在读入文件前执行。通常被用来改变内建变量的值,如:FS\RS\OFS,初始化变量的值和打印输出标题。
[peter@uplooking shellpeter]$ awk 'BEGIN{print "HELLO WORLD"}'
HELLO WORLD
[peter@uplooking shellpeter]$ awk 'BEGIN{print "---------LIST---------"}{print}END{print "------END--------"}' donors
---------LIST---------
Mike Harrington:(510) 548-1278:250:100:175
Christian Dobbins:(408) 538-2358:155:90:201
Susan Dalsass:(206) 654-6279:250:60:50
Archie McNichol:(206) 548-1348:250:100:175
Jody Savage:(206) 548-1278:15:188:150
Guy Quigley:(916) 343-6410:250:100:175
Dan Savage:(406) 298-7744:450:300:275
Nancy McNeil:(206) 548-1278:250:80:75
John Goldenrod:(916) 348-4278:250:100:175
Chet Main:(510) 548-5258:50:95:135
Tom Savage:(408) 926-3456:250:168:200
Elizabeth Stachelin:(916) 440-1763:175:75:300
------END--------

========================
重定向和管道
输出重定向
awk输出重定向到一个文件需要使用输出重定向符,输出文件名需要用双引号括起来。
[peter@uplooking shellpeter]$ awk -F: '{print $1,$2>"note"}' donors
[peter@uplooking shellpeter]$ cat note
Mike Harrington (510) 548-1278
Christian Dobbins (408) 538-2358
Susan Dalsass (206) 654-6279
Archie McNichol (206) 548-1348
Jody Savage (206) 548-1278
Guy Quigley (916) 343-6410
Dan Savage (406) 298-7744
Nancy McNeil (206) 548-1278
John Goldenrod (916) 348-4278
Chet Main (510) 548-5258
Tom Savage (408) 926-3456
Elizabeth Stachelin (916) 440-1763
========================================
输入重定向
getline函数
[peter@uplooking shellpeter]$ awk 'BEGIN{"date +%Y"|getline d;print d}'
2008

[peter@uplooking shellpeter]$ awk -F"[ :]" 'BEGIN{printf "What is your name?";\
getline name<"/dev/tty"}\
$1~ name{print "Found\t" name "\ton line",NR"."}\
END{print "see ya," name "."}' donors
What is your name?Jody
Found   Jody    on line 5.
see ya,Jody.

[peter@uplooking shellpeter]$ awk 'BEGIN{while(getline<"/etc/passwd">0)lc++;print lc}'
36
从文件中输入,如果得到一个记录,getline函数就返回1,如果文件已经到了末尾,则返回0,如果文件名错误则返回-1.

============================================

管道:
awk命令打开一个管道后要打开下一个管道需要关闭前一个管道,管道符右边可以使用“”关闭管道。在同一时间只有一个管道存在
[peter@uplooking shellpeter]$ awk '{print $1,$2|"sort -r +1 -2 +0 -1"}' names
tony tram
john smith
dan savage
john oldenrod
barbara nguyen
elizabeth lone
susan goldberg
george goldberg
eliza goldberg
alice cheba
|后用""关闭管道
======================================
system函数
system("LINUX command")
system("cat" $1)
system("clear")
============================
条件语句
1.if(){}
2.if(){}
else{}
3.if(){}
else if(){}
else if(){}
else{}

[peter@uplooking shellpeter]$ awk -F: '{if ($3>250){printf "%-2s%13s\n",$1,"-----------good partman"}else{print $1}}' donors
===================
循环语句
[peter@uplooking shellpeter]$ awk -F: '{i=1;while(i<=NF){print NF,$i;i++}}' donors
循环控制语句break、continue
=========================
程序控制语句
next从输入文件中读取下一行,然后从头开始执行awk脚本
{if($1~/Peter/){next}
else{print}}

exit 结束awk语句,但不会结束END模块的处理。
===========================================
数组:
awk '{name[x++]=$1;for(i=0;i
============================================

awk内建函数
sub(正则表达式,替换字符[,$n]) ---域n匹配正则表达式的字符串将被替换。
[peter@uplooking shellpeter]$ awk '{sub(/Tom/,"Jack",$1);print}' employee
Jack Jones 4424 5/12/66 543354
Mary Adams      5346    11/4/63 28765
Sally Chang     1654    7/22/54 650000
Billy Black     1683    9/23/44 336500
Jack He 3000 8/22/44 320000

=====================
index函数 index(字符串,子字符串) 子字符串在字符串中的位置
[peter@uplooking shellpeter]$ awk 'BEGIN{a=index("hello","llo");print a}'
3
==================================
length函数 length(string) 字符串的长度
[peter@uplooking shellpeter]$ awk 'BEGIN{a=length("hello world");print a}'
11
==============================================
substr函数 substr(字符串,开始位置[,子字符串长度])
[peter@uplooking shellpeter]$ awk 'BEGIN{a=substr("hello world",7);print a}'
world
[peter@uplooking shellpeter]$ awk 'BEGIN{a=substr("hello world",7,3);print a}'
wor
==========================================
match(string,正则表达式) 找出字符串中第一个匹配正则表达式的位置,其内建变量RSTART为匹配开始位置,RLENGTH为匹配开始后字符数
[peter@uplooking shellpeter]$ awk '{a=match($0,/Jon/);if (a!=0){print NR,a}}' employee
1 5
[peter@uplooking shellpeter]$ awk '{a=match($0,/Jon/);if (a!=0){print NR,a,RSTART,RLENGTH}}' employee
1 5 5 3
============================================
toupper和tolower函数
[peter@uplooking shellpeter]$ awk 'BEGIN{a=toupper("hello");print a}'
HELLO
======================================
split函数 split(string,array,fieldseperator)
[peter@uplooking shellpeter]$ awk 'BEGIN{"date"|getline d;split(d,date);print date[2]}'
Mar
===========================
时间函数
systime() ----1970年1月1日到当前忽略闰年得出的秒数。
strftime(格式描述,时间戳)
[peter@uplooking shellpeter]$ awk 'BEGIN{d=strftime("%T",systime());print d}'
13:08:09
[peter@uplooking shellpeter]$ awk 'BEGIN{d=strftime("%D",systime());print d}'
03/12/08
[peter@uplooking shellpeter]$ awk 'BEGIN{d=strftime("%Y",systime());print d}'
2008

===================================================


         ******awk总结******

gawk 工具解释特殊目的的编程语言,处理简单的数据重新格式化的工作,只需要几行代码。他是通常UNIX awk 命令的免费版本。

这个工具从输入数据读取行且能够方便的识别。 print 是最普通的来过滤和格式化定义的域的程序。

闲置的变量可以直接声明也允许在处理输入流的同时进行简单的计算求和,统计和其他运算。变量和命令可以放到 awk 脚本来进行后台处理。


==========================================================

******split cut join 分割和合并文件命令******

[peter@uplooking 1]$ df -h >myfile
[peter@uplooking 1]$ split -l 2 myfile Xiaorong   (每两行分割为一个以split名称开头的文件)
[peter@uplooking 1]$ ls
case.sh df.out helloworld.sh ifpeter.sh myfile nohup.out nullfile.txt parm.sh Xiaorongaa Xiaorongab Xiaorongac Xiaorongad Xiaorongae
[peter@uplooking 1]$ cat splitaa
Filesystem            Size Used Avail Use% Mounted on
/dev/sda7              20G 3.3G   16G 18% /