shell基础收集

一.脚本基础
1.非交互启动SHELL
$ /bin/sh filename  --文件中记录命令
2.终端培植最通用的标准: TERM=vt100
3.设置PATH
PATH=/bin:/usr/bin:/usr/sbin:/sbin:/u1/topprod/tiptop/ds4gl2/bin:/tmp:/u1/genero.dev/bin:/u1/genero.run/bin
/u1/genero.run/bin
/u1/genero.as/bin
/u1/genero.run/bin
.
/u1/topprod/tiptop/ora/bin
/u1/topprod/tiptop/work
/u1/topprod/topcust/bin
/u1/topprod/tiptop/bin
/u1/topprod/tiptop/ds4gl2/bin
/u1/topprod/tiptop/script
/u2/oracle/9i/bin
/u1/usr/tiptop
/usr/kerberos/bin
/usr/local/bin
/bin
/usr/bin
/usr/X11R6/bin
4.设置MANPATH
MANPATH=/usr/man:/usr/share/man
5.使SHELL脚本可执行
chmod a+x ./logins
6.使用正确的SHELL运行脚本,要在脚本开头增加一行代码,必须是第一行:
#!/bin/sh
二.文件操作
ls -F  显示是目录或者文件
ls -laF
浏览文件concatenate
cat files  
cat -n -b hosts    -n表明行号
wc [option] files
wc -l -m -c files  统计行数,单词数,字符数
cp source destination
cp -i test_results test_results.orig  如果目标文件存在,提示是否覆盖
cp test_results work/  
cp res.01 res.02 res.03 work/
mv source destination
mv -i ch07 ch07.bak  --如果存在,提示是否覆盖
mv /home/ranga/names /tmp 把ranga下的文件names移到目录/tmp下
mv docs/ work/
mv work/docs
mv work/ docs/ .profile pub/
不同分区情况
cp -r /tmp/ch01 /home/ranga
rm -r /tmp/ch01
删除目录
rmdir  删除空目录
rm -r
rm files
rm -i hw1 hw2 hw3    --提示是否删除
rm -r ch01/ 删除目录ch01和文件
cd 回到用户起始目录
pwd 查询当前目录
ls /usr/local
ls http://www.cnblogs.com/usr/local
ls /u1 /usr  列出两个目录清单
ls .profile /usr  /bin/sh
ls -d /home/ranga  只显示目录名
mkdir drectory
mkdir docs pub
mkdir -p /tmp/ch04/test 创建所有需要的父目录
拷贝目录
cp -r source destination
cp -r docs/book /mmt/zip  包括创建文件夹book
cp -r docs/book docs/school work/src /mnt/zip
cp -r .profile /docs/book /mnt/jaz
三.文件属性操作
-普通文件
l符号链
c字符特殊文件
b块特殊文件
p命名管道
s Socket
d目录文件
ls -ld /home/ranga
file filename 查询文件类型
ls -al /u1/usr/1000810
创建符号链
ln -s source destination
ls -s ../httpd/html/users/ranga ./public_html
文件权限
r 读 w 写 x执行
改变文件和目录权限
符号方式
who action permissions
u 所有者 g 组 o 其他 a 所有
+ 增加权限 - 删除权限 = 显示地设置文件权限 rwxs
chmod a=r*   chmod guo=r* 所有用户读权限
chmod go-w .profile
chmod go-rwx*
chmod guo+rx*
chmod go-w,a+x a.out
chmal -R o+r pub
八进制方式
读权限4  写权限2 执行权限1  SUID用4 SGID用2
八进制方式是设置而不是修改文件的权限
chmod 0600 .profile
改变文件的所有者chown
改变文件的组chgrp
chown option user:group files
chown efrioo:/home/httpd/html/users/ranga  -改目录所有者
chown -R efrioo:/home/httpd/html/users/ranga --所有文件和子目录
chgrp options group files
chgrp authors /home/ranga/docs/ch5.doc
chgrp :authors /home/ranga/docs/ch5.doc
chgrp -R group files
四.进程
后台进程:&
ls cho*.doc &
使监测可用:set -o monitor
取消监测信息: set +o monitor
检查所有的shell选项:set -o
悬挂键:ctrl+z
用stty命令来识别哪个键执行哪个功能 stty -a
ctrl+z后,进程已停止并悬挂,输入 bg 进程在后台恢复工作
如果多个悬挂进程
bg %1   
bg %2恢复在后台工作
从后台移到前台
fg %1
使后台进程持续运行
nohup ls &
等待后台进程结束
wait %1
jobs命令显示出悬挂的以及正在后台运行的进程
ps
ps -f
ps -ef
ps -f -u user
ps -a 所有用户信息
ps -x没有终端的进程信息
ps -ux
杀死进程
kill %1
强迫结束进程
kill -9 或kill -KILL
五.变量
定义变量
fruit=peach
fruit ="apple orange plum"
访问值$
$echo $fruit
$$fruit=efrioo 用改变量的值替换该变量
数组变量:数组下标必须为0-1023的整数
fruit[0]=apple
一次设置多个元素
set -A name value1 value2 ....
set -A band derri terry mike gene
$echo ${fruit[0]}
$echo ${fruit
  • }
    $echo ${fruit[@]} 有空格时使用
    只读变量
    readonly fruit
    删除变量:只读的无法使用该变量删除
    unset name
    unset fruit
    局部变量.环境变量.SHELL变量
    导出环境变量:将变量放入环境中
    PATH=/sbin:/bin ;export PATH
    导出多个变量:export PATH HOME UID
    export name=value
    export PTH=/sbin:/bin
    export FMHOME=/usr/frame CLEARHOME=/usr/atria PATH
    shell变量
    PWD UID SHLVL REPLY RANDOM SECONDS IFS PATH HOME
    六.替换
    文件名替换
    ls * 所有文件和目录
    ls ch1*
    ls *doc
    ls back*doc
    ls cgi*st*java
    ls ch??.doc
    ls cho[0123456789].doc
    ls cho[0-9].doc
    ls [a-z]*
    ls [A-Z]*
    ls [a-z A-Z]*
    ls *[a-Z A-Z 0-9]
    ls [!a]* 不包含a
    变量替换
    ${parameter:-word}替换缺省值,只当变量未设置时执行替换
    PSI=${parameter:-localhost} "$";export PSI;
    ${parameter:=word}赋予一个缺省值
    PSI=${HOST:='uname -n'} "$"; export PSI HOST;
    ${parameter:?message} 检查是否已定义,否则提示
    :${HOME:? "your home directory is undefined"}
    ${parameter:+word}已设置时将替换的值
    echo ${DEBUG:+ "debug is active "}
    命令替换
    grep `id -un` /ect/passwd  使用后引号`,不是单引号
    up=`date; uptime`
    user=`who | wc -l`
    算术替换
    $((expression))
    s1:$((((5+3*2)-4)/2)) 整数运算,取整
    七.引用
    用反斜线引用(单个)
    echo hello "; world
    echo you owe "$1250
    使用单撇号(一串)
    echo ']?)'
    echo it"'s friday
    使用双撇号:保证了$和后引号`的替换功能
    echo "$user owes;[as of(`date+%m/%d`)]"
    echo "the dos directory is ""windows
    [url=file://""temp"]""temp"[/url]
    ""
    引用规则和环境
    引用忽略了单词边界
    echo hel"lo;w"orld
    命令中的组合引用
    echo the '$user' variable contains this value ">"|$user|"
    在单个参数中嵌入空格
    echo "name      address"
    mail -s 'meetion tomorrow' fred jane line2 '
    访问包含特殊字符的文件名而引用
    rm 'ch1*'
    引用正规表达式通配符
    grep '[0-9][0-9]*$' report2 report7
    引用反斜线开启echo转义序列
    echo -e "line 1"nline 2"
    -e使得shell将echo转义序列解释成特殊字符而非文本字符
    cpio是一个存储和恢复文件的命令
    cpio -icvdum 'usr2/*'
    find / -name 'ch*.doc' -print 匹配所有目录
    十.流控制
    使用test
    文件测试
    test option file
    if [-d /home/ranga/bin ]; then PATH="$ PATH:/home/ranga/bin"; fi  目录是否存在
    if [-f $home/.bash_aliai]; then $home/.bash_aliai; fi  测试该文件是否有内容
    -b 块特殊文件 -c字符特殊文件 -d目录  -e文件存在 -f规则文件
    -g是否设置SGID位的值 -h符号连接  -k是否设置"sticky"位的值  -p为已命名管道
    -r文件存在且可读 -s文件存在且大于0  -u设置了SUID位的值 -x文件存在且可执行  -o文件存在且被有效用户ID拥有
    字符串比较
    -z string长度为0
    -n string长度不为0
    string1=string2 字符串相等
    string1!=string2字符串不等
    if [-z "$fruit_basket"]; then
       echo "your fruit basket is empty";
    else
       echo "your fruit basket has following fruit:$fruit_basket"
    fi
    if [ "$fruit" = apple ] ; then
       echo "it is empty"
    else
       echo "it is $fruit"
    fi
    数字比较
    if [ $? -eq 0 ] ; then
       echo "successful";
    else
       echo "an error was encountered"
    fi
    -eq 等于 -ne不等于
    -lt小于  -le小于等于
    -gt大于  -ge大于等于
    符合表达式
    使用操作符:&& || !
    ! expr 取反
    expr1 -a expr2 都为真则为真
    expr1 -o expr2 一个为真则为真
    if [ -z "$DTHOME"] && [-d /usr/dt]; then dthmoe=/usr/dt; fi
    if [ -z "$dthmoe"] -a -d /usr/dt] ; then dthmoe=/usr/dt; fi
    否定一个表达式
    if [!-d $home/bin]; then mkdir $home/bin; fi
    case语句
    fruit=kiwi
    case "$fruit" in
         apple) echo "apple pie is quite tasty";;
         banana) echo " i like banana";;
         kiwi) echo "new zealand is famous for kiwi";;
    esac
    if ["$fruit"=apple]; then
       echo "apple pie is quite tasty"
    elif ["$fruit"=banana]; then
       echo "i like anana nut bread"
    elif ["$fruit"=kiwi]; then
       echo "new zealand is famous for kiwi"
    fi
    case "$term" in
         *term) term=xterm;;
         network|dialup|unknown|vt[0-9][0-9][0-9]) term=vt100;;
    esac
    十一.循环
    while循环
    x=0
    while [$x -lt 10]
    do
       echo $x
       x=`echo "$x+1"|bc` --bc为X加1
    done
    while循环嵌套
    x=0
    while ["$x" -lt 10 ];
    do
       y="$x"
       while [ "$y" -ge 0 ] ;
       do
         echo "$y "c"
         y=`echo "$y-1"|bc`
       done
       echo
       x=`echo "$x+1"|bc`
    done
    RESPONSE=
    while [-z "$RESPONSE" ] ;
    do
      echo "enter the name of a directory where your files are -locate:"c"
      read ESPONSE
      if [! -d "$RESPONSE" ] ; then
         echo "ERROR:please enter a directory pathname"
         RESPONSE=
      fi
    done
    until循环
    x=1
    while [ ! $x -ge 10]
    do
       echo $x
       x=`echo "$x+a"|bc
    done
    x=1
    until [$x -ge 10]
    do
      echo $x
      x=`echo "$x+1"|bc
    done
    for循环
    for i in 0 1 2 3 4 5 7 9 8
    do
      echo $i
    done
    for file in $home/.bash*
    do
      cp $file ${home}/public_html
      chmod a+r ${home}/public_html/${file}
    done
    select循环
    select component in comp1 comp2 comp3 all none
    do
      case $component in
           comp1|comp2|comp3) compconf $component ;;
           all) compconf comp1
                compconf comp2
                compcomf comp3
                ;;
           none) break;;
           *) echo "error:invalid selection,$reply.";;
      esac
    done
    $PS3="please make aselection=>";export PS3
    循环控制
    while
    do
      read CMD
      case $CMD in
           [qQ]|[qQ][uU][iI][tT]) break;;
           *) process $CMD;;
      esac
    done
    for i in 1 2 3 4 5
    do
      mkdir -p /mnt/backup/docs/ch0${i}
      if [ $? -eq 0] ; then
         for j in doc c h m pl sh
         do
           cp $home/docs/ch0${i}/*.${j} /mnt/backup/docs/ch0${i}
           if [ $? -ne 0] ; then break 2 ;   --从两个循环中跳出
           fi   
         done
      else
         echo "could not make backup directory"
      fi
    done
    continue命令  只跳出当前迭代而不是整个循环
    for file in $files
    do
      if [! -f "$file" ] ; then
         echo "error:$file is not a file"
         continue
      fi
      #process the file
    done
    十二.参数
    特殊shell变量
    $0 正在被执行命令的名字
    $n 与脚本被激活时所带的参数相对应
    $#提供给脚本的参数号
    $*所有参数都被双引号引住$*=$1$2
    $@所有参数都被双引号引住
    [email=$@=$1$2]$@=$1$2[/email]
    $?前一命令执行后的退出状态
    $$当前shell的进程号
    $!前一个后台命令的进程号
    使用$0
    #!/bin/sh
    case $0 in
         *listtar) targs="-tvf $1";;
         *maketar) targs="-cvf $1.tar $1";;
    esac
    tar $targs
    usage语句
    case $0 in
         *listtar) targs="-tvf $1";;
         *maketar) targs="-cvf $1.tar $1";;
         *) echo "usage:$0[file|directory]"
         exit 0
         ;;
    esac
    处理参数
    USAGE="usage:$0 [ -c|-t] [file|directory]"
    case "$1" in
        -t) targs="-tvf $2";;
        -c) targs="-cvf $2.tar $2";;
         *) echo "$suage"
            exit 0
            ;;
    esac
    使用basename命令
    接受一个绝对或相对路径并返回相应的文件或目录名
    $ basename /usr/bin/sh
    usage:"usage:'basename $0' [-c|-t][file|directory]"
    case `basename $0` in
          listtar) targs="-tvf $1";;
          maketar) targs="-cvf $1.tar $1";;
    esac
    tar $targs
    公用参数处理
    usage="usage:`basename $0` [-c|-t][file|directory]"
    if [ $# -lt 2]; then
       echo "$usage"
       exit 1
    fi
    case "$1" in
         -t) targs="-tvf $2";;
         -c) targs="-cvf $2.tar $2";;
          *) echo "$usage"
             exit 0
             ;;
    esac
    tar $targs
    处理附加文件
    case "$1" in
         -t) targs="-tvf"
             for i in "$@"; do
                 if [-f "$i"]; then tar $targs "$i"; fi;
             done
             ;;
         -c) targs="-cvf $2.tar $2";
             tar $targs
             ;;
          *) echo "$usage";
             exit 0
             ;;
    esac
    usage="usage:`basename $0` [-c|-t][files|directoryies]"
    if [$# -lt 2] ; then
       echo "$usage";
       exit 1;
    fi
    case "$1" in
         -t) shift; targs="-tvf";
         for i in "$@" ; do
          if [-f "4i"] ; then
             files=`tar $targs "$i" 2>/dev/null`
             if [$? -eq 0] ; then
                echo ; echo "$i"; echo "#files"
             else
                echo "error:$i not a tar file"
             fi
         else
            echo "error: $i not a file"
         fi
        done   
        ;;
      -c) shift; targs="-cvf";
          tar $targs archive.tar "$@"
          ;;
       *) echo "$usage"
          exit 0
          ;;
      $?
    shell脚本中的选项分析:getopts
    verbose=false
    while getopts f:o:v option;
    do
      case "$option" in
           f) infile="$optarg";;
           o) outfile="$optarg";;
           v) verbose=true;;
          "?) echo "$usage";
              exit 1
              ;;
      esac
    done
    shift `echo "$optind -1"|bc`
    if [-z "$1" -a -z "$infile" ] ; then
       echo "error:input file was not specified"
       exit 1
    fi
    if [-z "$infile"] ; then infile="$1" ; fi
    shift命令放弃通过减号提供给脚本的参数
    if [-f "$infile"]; then uuencode $infile $infile>$outfile; fi
    完整脚本:
    #!/bin/sh
    usage="usage:`basename $0` [-v][-f][filename][-o][filename]";
    verbose=false
    while getopts f:o:v option ; do
          case "$option" in
            f) infile="$optarg";;
            o) outfile="$optarg";;
            v) verbose=true;;
           "?) echo "$usage";
               exit 1
               ;;
          esac
    done
    shift `echo "$optind -1"|bc`
    if [-z "$1" -a -z "$infile"]; then
       echo "error:input file was not specified"
       exit 1
    fi
    if [-z "$infile"] ; then infile="$1"; fi
    :${outfile:=${infile}.uu}
    if [-f "$infile"]; then
       if [["$verbose"="true"];then
          echo "uuencoding $infile to $outfile..."c"
       fi
       uuencode $infile $infile > $outfile;ret=$?
       if ["$verbose"="true"] ; then
          msg="failed" ; if [$ret -eq 0] ; then msg="done"; fi
       fi
    fi
    exit 0
    十三.输入/输出
    向终端输出stdout
    echo  printf
    echo eliza,where the devil are my slippers?!?
    echo you r home directory is $home
    echo的转义序列
    "n 换行符 "t 跳格键tab  "c 字符串不带换行符
    echo "your fruit basket contains:"n$fruit_basket"
    echo "name "tuser name"nsriranga"tranga"nsrivathsa"tvathsa"
    echo "making directories,please wait ..."t"c"
    for i ${dirs_to_make} ; do mkdir -p $I;done
    echo "done"
    printf
    格式化序列类型
    s 字符串 c字符 d十进制整数  x十六进制   o八进制  e指数型浮点数  f固定浮点数  g压缩浮点数
    printf "%32s %s"n" "file name" "file type"
    #printf "%-32s %s"n" "file name" "file type"
    for i in *;
    do
      printf "%32s" "$i"
      if [-d "$i"]; then
        echo "directory"
      elif [-h "$i"]; then
        echo "symolic link"
      elif [-f "$i"]; then
        echo "file"
      else
        echo "unknown"
      fi;
    done
    输出重定向:将输出重定向到一个文件 >
    date>now
    {date;uptime;who;}>mylog
    向一个文件增加内容
    {date;uptime;who;}>>mylog
    向文件和屏幕重定向输出
    date|tee now
    if ["$logging"!="true"] ; then
       logging="true";export logging;
       exec $0|tee $logfile
    fi
    输入
    输入重定向
    mail
    [email protected]

    lpr
    本地文档
    cat >urls
    读取用户输入
    read name
    YN=yes
    printf "do you want to play a game [$YN]?"
    read YN
    :${YN:=yes}
    case $YN in
         [yY]|[yY][eE][sS]) exec xblast;;
         *) echo "maybe later" ;;
    esac
    while read line
    do
      case $line in
      *root*) echo $line;;
    esac
    done
    管道 pipeline
    使用一个程序操纵另一个程序的输出
    tail -f /var/adm/messages |more  tail的标准输出被管道输送到MORE的标准输入中
    ps -ael |grep "$uid" |more       ps的标准输出被连到grep的标准输入中,而grep的标准输出被连到more的标准输入中
    文件描述符
    标准输入stdin  0
    标准输出stdout 1
    标准错误stderr 2
    使用exec可将任何文件和文件描述符关联起来
    当需要多次将输入输出重定向到一个文件但又不想多次重复文件名时.
    exec n>file
    exec n>>file  n为文件描述符
    输入/输出重定向通用的格式
    将stdout 1和stderr 2重定向到不同的独立文件
    command 1>file1 2>file2
    command >>file1 2>>file2
    for file in $files
    do
      ln -s $file ./docs>>/tmp/ln.log 2>/dev/null --删除输出的一个特殊文件
    done
    将stdout和stderr重定向到同一个文件
    command >file 2>&1
    command >>file 2>&1
    rm -rf /tmp/my_dir > /dev/null 2>&1;mkdir /tmp/my_dir
    rdate -s ntp.nasa.gov>>/var/log/rdate.log 2>&1   --rdate与时间服务器同步时间
    向STDOUT打印消息
    echo string 1>&2
    printf format args 1>&2
    echo string>&2
    printf format args>&2
    if [!-f $file] ; then echo "error:$file is not a file" > &2; fi   是否为文件
    重定向两个文件描述符
    n>&m
    if [-f "$1" ]; then
       i=0
       while read line
       do
         i=`echo "$i+1" |bc`
       done
    if [$# -ge 1] ; then
       for file in $@
       do   
         exec 5
    十四.函数
    创建和使用函数
    name() {list;}
    cd() {chdir${l:-$home};psi=`pwd`$";export psi;}
    函数举例
    列出路径
    OLDIFS="$IFS"
    IFS=:
    for DIR in $PATH; do echo $DIR ; done
    IFS="$OLDIFS"
    IFS是shell的内部域分割符internal field separator
    lspath() {
          OLDIFS="$IFS"
          IFS=:
          for DIR in $PATH; do echo $DIR ; done
          IFS="$OLDIFS"
    }
    lspath|grep "/usr/dt/bin"
    制作用户自己的路径
    path=
    for dir in /bin /sbin /usr/bin /usr/sbin /usr/ccs/bin /usr/ucb ;
    do
      if [-d "$dir"]; path = "$path:$dir"; fi
    done
    export path
    SetPath() {
       for _dir in "$@"
       do
         if [-d "$_dir"] ; then path = "$path";"$_dir"; fi
       done
       export path
       unset _dilr
    }
    使用变量替换进行检查
    SetPath() {
       path=${path:"/sbin:/bin"};
       for _dir in "$@"
       do
         if [-d "$_dir"] ; then path = "$path";"$_dir"; fi
       done
       export path
       unset _dilr
    }
    在函数间共享数据
    在文件系统间移动
    三个在文件系统移动的命令:popd  pushd  dirs
    dirs(){
      oldifs="ifs"
      ifs=:
      for i in $_dir_stack
      do
        echo "$i "c"
      done
      echo
      ifs="$lodifs"
    }
    实现pushd
    pushd() {
      req="$1";
      if [-z "$req"] ; then req=.; fi
      if [-d "$req"]; then  
         cd "$seq"> /dev/null 2>$1
         if [$? -eq 0] ; then
            _dir_stack="`pwd`:$_dir_stack"; export _dir_stack;
            dirs
         else
            echo "error:connot change to directory $req.">$2
         fi
      else
        echo "error:$req is not a directory." >$2
      fi
      unset req
    }
    实现popd
    帮助函数
    _popd_helper() {
      popd="$1"
      if [-z "$popd" ] ; then
        echo "error:the directory stack is empty" >$2
        return 1
      fi
      shift
      if [-n "$1"]; then
         _dir_stack="$1";
        shift;
        fo i in $@; do _dir_stack="$_dir_stack:#i"; done
      else
        _dir_stac=
      fi
      if [-d "$pdpd"] ; then
         cd "$popd" >/dev/null 2> &1
         if [$? -ne 0]  ;then
            echo "error:could not cd to $popd" >&2
         fi
         pwd
      else
        echo "error:$popd is not a directory ." >&2
      fi
      export _dir_stack
      unset popd
    }
    打包函数
    popd() {
      oldifs="$ifs"
      ifs=:
      _popd_helper $_dir_stack
      ifs="$oldifs"
    }

    十五.文本过滤器
    head  tail  grep  sort  uniq  tr
    head命令
    head [-n lines] files 没有选项就显示前10行,否则显示前N行
    按最近访问时间将清单排序 -ut
    ls -lut /home/ranga/public_html
    检索前5个
    ls -lut /home/ranga/public_html|head -5
    ps -ef|grep udm7|head -5
    tail命令
    tail [-n lines] files 没有选项,显示标准输入的后10行
    ls -t按上次改动时间排序
    ls -lt /var/spool/mail
    ls -lt /var/spool/mail|tail -5
    ps -ef|grep udm7|tail -5
    从最老到最新排序 -r
    ls -lrt /var/spool/mail|tail -5
    tail -f file   (follow)当程序正在向文件写入时也可以查看该文件
    tail -f /var/log/httpd/access_log
    使用grep 在文件中找到包含某个特殊单词或词组的行
    grep word file
    grep pipe ch14.doc
    grep pipe ch14.doc ch15.01.doc  同时查找多文件
    grep -i unix ch16.doc 同时匹配大小写
    从stdin(标准输入)中读入
    who |grep ranga
    grep -v home /etc/passwd 不包含单词home
    所有运行在一个系统上的bash例程
    /bin/ps -ef|grep bash
    /bin/ps -ef|grep bash|grep -v grep
    行号
    grep -n pipe ch15.doc ch15-01.doc
    只列出文件名
    grep -l delete *
    统计单词的总数
    tr命令 将一个集合中的所有字符改变成另一个集合中的字符,也可用于删除字符集
    sort命令为输入文件中的行进行分类
    uniq命令打印出文件中所有的唯一行,并列出某个特定行重复的次数
    tr命令
    tr `set1` `set2` 将set1转变成set2
    tr `!?":;"["]{}(),."t"n` ` `
    压缩输出空格
    tr -s `set1`
    遇到字符多次连续出现时,只使用一次这个字符
    echo "feed me"|tr -s `e`   fed me
    echo "shell programming"|tr -s `ln`
    tr `!?":;"["]{}(),."t"n` ` `
    sort命令:统计一个单词使用了多少次,要使用sort命令将文件中的单词排序
    先将文件变成每个单词占用一行的格式,要把所有空格变成换行符
    tr `!?":;"["]{}(),."t"n` ` `
    uniq命令
    通过sort使用-u选项删除所有重复的单词
    uniq统计单词出现的次数使用-c
    tr `!?":;"["]{}(),."t"n` ` `
    为数字排序
    sort按数字值排序代替按字符串排序-n,最大的数字首先先打-r,缺省时,最后打印出最大的数字
    tr `!?":;"["]{}(),."t"n` ` `
    sort命令关键字从何处开始到何处结束,以列为单位
    sort -k start,end files
    sort -rn -k 2,2 switched.txt
    sort -rn -k 2 switched.txt
    tr命令中字符分类的使用
    alnum 字符和数字  alpha 字母  blank 白色空格  cntrl 控制字符  digit 数字  graph 可打印字符,不包括空格 lower 小写字母
    print 打印字符,包括空格  punct 标点符号  space 水平或垂直空格 upper 大写字母 xdigit 16进制数字
    tr `[:classname:]` `set2`
    去掉标点符号和空格
    tr `[:punct:]` ` `
    十六.使用正规表达式过滤文本
    awk sed激活语法: command 'script' filenames
    正规表达式
    基本构造块包括:普通字符.元字符(通配符)
    用于正规表达式的元字符
    . 匹配任何除换行符外的单个字符
    * 匹配恰处于*字符前的0个或多个所给字符
    [chars] 匹配在chars中给出的任一个字符,用 - 指出字符范围,^若为第一个字符,则匹配在char2中未指定的字符
    ^匹配一行的开始
    $匹配一行的结尾
    "将紧随在"字符后的字符作为文字字符处理
    匹配字符
    /a.c/   匹配包含a+c,a-c,abc行
    /a*c/   包含0个或有a且以c结尾的字符串
    /ch.*doc/ 以ch开头,doc结尾
    指定字符集
    在正规表达式中指定具体的字符集,使用中括号[ ]    /[chars]/
    常用字符集
    [a-z]匹配单个小写字母
    [A-Z]匹配单个大写字母
    [a-z A-Z]匹配单个字母
    [0-9]匹配单个数字
    [a-z A-Z 0-9]匹配单个字母或数字
    [^T]匹配任何在集合中未给出的字符
    /cho[0-9]*doc/
    锚定模式anchoring
    只匹配单字the ,以there这类单词开头的行不匹配,只需在后面加一个空格
    /the /
    字符串the在行的开始位置,使用^元字符
    /^the/
    使用$元字符将表达式锚定在一行的末尾
    /friend$/
    /^chapter[1-9]*[0-9]$/
    匹配空格行
    /^$/
    转义元字符
    /"$[0-9]*".[0-9][0-9]"/[a-z A-Z]*/
    一些有用的正规表达式
    空行: /^$/
    整行:/^.*$/
    一个或多个空格:/*/
    html或xml标记:/][^>]*>/
    有效的
    [url=/[a-z]url:/[a-z[/url]
    A-Z][a-z A-Z]*:"/"/[a-Z A-Z 0-9][a-z A-z 0-9".]*.*/
    美元数量格式:/"$[0-9]*".[0-9][0-9]/
    使用sed:sed是一个可用作过滤器的流编辑器
    sed `script` files
    script是一个或多个按如下格式写出的命令:/pattern/action
    在sed中可以利用的某些动作
    p 打出该行
    d 删除该行
    s/pattern1/pattern2 用pattern2替代pattern1
    打印行
    打出价格低于1元的清单
    sed `/0".[0-9][0-9]$/p` fruit_prices.txt   会出现都打印,符合条件的打印两次,使用-n
    sed -n `/0".[0-9][0-9]$/p` fruit_prices.txt
    删除行
    以单字mango或Mango开头的行都删除
    sed '/^[Mn]ango/d' fruit_prices.txt
    执行替换
    /pattern/s/pattern1/pattern2/   pattern2代替pattern1
    s/pattern1/pattern2
    sed 's/paech/Peach/' fruit_prices.txt  一次只执行一次替换
    执行全局替换
    s/pattern1/pattern2/g
    sed 's/eqal/equal/g' nash.txt
    重用表达式的值
    s命令为我们提供了&操作符,使得pattern2中重用匹配pattern1的字符串  $1 代替 1
    sed 's/*[0-9][0-9]*".[0-9][0-9]$/"$&/' fruit_prices.txt
    使用多个sed命令
    sed -e 'command1' -e 'command2' ....... files
    sed -e 's/Paech/Peach/' -e 's/'*[0-9][0-9]*".[0-9][0-9]$/"$&/' fruit_prices.txt
    mu fruit_pieces.txt fruit_pieces.txt.$$
    sed -e 's/Paech/Peach/' -e 's/*[0-9][0-9]*".[0-9][0-9]$/"$&/' fruit_prices.txt.$$ > fruit_prices.txt
    在管道中使用sed
    如果sed没有收到文件清单,那么作用于STDIN,可在管道中使用sed
    删除第一个圆括号后的所有字符
    $/usr/bin/id | sed 's/(.*$//'
    删除该行开始处的字符串uid
    $/usr/bin/id |sed -e 's/(.*$//' -e 's/^uid=//'

    十七.使用awk过滤文本
    基本语法: awk 'script' files
    显示从一个文件来的所有输入行
    awk '{print;}' fruit_prices.txt
    域编辑:自动将输入行分割成域,域缺省分割字符是跳格(tab空格)和空格
           当输入一行时,awk将已经分析过的域放入变量1作为第一个域.为了访问一个域,使用域操作符$,第一个为$1
           awk中,只有当访问一个域变量的值才使用$,访问其他变量的值时,不要求使用$
    打印水果名和数量
    awk '{print$1 $3;}' fruit_prices.txt
    列之间有空格,使用,
    awk '{print$, $3;}' fruit_prices.txt
    通过使用printf命令代替print将输出格式化
    awk '{printf "%-15s %s"n",$1,$3;}' fruit_prices.txt
    执行 模式-特定 行为
    大于1美元的加*,低于的不变
    awk '/*"$[1-9][0-9]*".[0-9][0-9]*/ {print $1,$2,$3,"*";} /*"$0".[0-9][0-9]*/{print ;}' fruit_prices.txt
    格式化问题
    awk '/*"$[1-9][0-9]*".[0-9][0-9]*/ {print $0,"*";} /*"$0".[0-9][0-9]*/{print ;}' fruit_prices.txt
    比较操作符
    expression {actions;}
      =   ==  !=  
    value~/pattern/ value匹配pattern则为真    value!~/pattern/ value不匹配pattern则为真
    awk '$375 {print $0;} ' fruit_prices.txt
    复合表达式
    (expr1)&&(expr2)
    (expr1)||(expr2)
    单价高于1美元且数量少于75
    awk '($2-/^"$[1-9][0-9]*".[0-9][0-9]$/)&&($3
    next命令
    awk '$375 {print $0}' fruit_prices.txt
    awk '$375 {print $0}' fruit_prices.txt
    使用STDIN作为输入
    /bin/ls -l | awk '$1 !-/total/{printf "%-32s %s"n",$9,$5;}'
    使用数字表达式
    awk中的数字操作符
    +  - *  /   %求余   ^求幂
    for i in $@
    do
      if [-f $i] ; then
        echo $i
        awk '/^*$/{x=x+1;print x;}' $i
      else
        echo "error:$i not a file" > &2
      fi
    done
    赋值操作符
    x=x+1   x+=1
    awk中的赋值操作符
    +=  加   -=减    *=乘    /=除   %=求余   ^=求幂
    特殊模式:begin  end
    awk '/^&$/{x=x+1;print x;}' $i
    awk '
        begin {actions}          读取任何输入前执行
         /pattern/ {actions}
         /pattern/ {actions}
        end {actions}            退出前执行
        ' files
    for i in $@
    do
      if [-f "$i"]; then
        echo "$i"c"
        awk '
            /^*$/{x+=1;}
            end {printf "%s"n",x;}
            ' "$i"
      else
        echo "error:$i not a file" > &2
      fi
    done
    内置变量
    awk中的内置变量
    FILENAME 当前输入文件的文件名,不应改变
    NR 输入文件中当前输入行或记录的编号,不应改变
    NF 当前行或记录中域的编号,不应改变
    OFS 输入域分割符(缺省为空)
    FS 输入域分割符(缺省为空或TAB键)
    ORS 输出记录分割符(缺省为换行符)
    RS 输入记录分割符(缺省为换行符)
    FILENAME
    for i in $@
    do
      if [-f "$i"] ; then
        awk 'begin {printf "%s"t", FILENAME;}
             /^*$/ {x+=1;}
             end {ave=100*(x/NR); printf "%s"t%3.1f"n",x,ave;}
             ' "$i"
      else
        echo "error:$i not a file"  >&2
      fi
    done
    改变输入域分割符
    在BEGIN模式配置FS
    awk指定-F选项 使用SHELL变量动态指定域分割符
    awk 'begin {FS=":";} {print $i,$6;}' /etc/passwd
    awk -F: '{print $1,$6;}' /etc/passwd
    允许awk使用shell变量
    awk 'script' awkvar1=value awkvar2=value.......files
    NUMFRUIT="$1"
    if [-z "$NUMBRUIT"] ;then NUMBRUIT=75 ; fi
    awk '$3
    $./reorder.sh 25
    流控制
    if语句
    awk '{printf "%s"t",$0; if ($2-/"$[1-9][0-9]*".[0-9][0-9]/) {printf "*"; if ($3
    while语句
    awk 'begin {x=0 ; while (x
    do语句:执行至少一次  
    awk 'BEGIN {'BEGIN {x=0 ; do {x+=1; print x; } while (x
    nawk gawk awk 建议使用nawk gawk
    for语句
    for (initalize_counter; test_counter; increment_counter) { action }
    awk '{for (x=1;x
    十八.各种工具
    eval命令  第二次重新处理命令
    output=">out.file"
    eval echo hello $output
    : 命令 是一个完整的shell命令 ,只返回一个完成代码 0 ,指示命令成功完成 .也可作一个空操作no-op
    if [-x $cmd]
    then :
    else
      echo error:$cmd is not executable >&2
    fi
    作为无限循环
    while :
    do
      echo "enter some input: "c"
      read INPUT
      ["$INPUT" = stop ] && break
    done
    命令估计参数值
    :${lines:=24}${term:? "term not set"} lines为空或为定义,则被设为24,term为空或未定义,则发出错误信息
    type命令:告诉用户一个指定命令的全路径
    type command1 command2...
    type s1 ls mv
    sleep命令:用于暂停一段时间(给定秒数)
    sleep n
    echo -e "a value must be input!"a"
    sleep 1
    echo -ne ""a"      "a音频信号 为听到声音信号需要为"a加上-e选项
    sleep 1
    echo -ne ""a"
    用户清单5分钟增加到文件一次
    while :
    do
      date
      who
      sleep 300
    done >>logfile

    find命令
    find start-dir options actions
    find / -name efrioo -print  寻找efrioo并在屏幕上显示他们的全路径
    find /reports/1998 -name efrioo -type f -print -exec lp {} ";
    /reports/1998 开始目录,只在该目录或子目录寻找
    绝对路径: find /u1 -name efrioo -print
    相对路径: find ./tmp -name efrioo -print
    全目录:   find / -name efrioo -print
    搜索多目录:find dir1 dir2 -name efrioo print
    -name efrioo 只找文件名,不检查目录部分
    find / -name '*efrioo*' -print
    -type f 只寻找文件类型为f的文件 (f为规则或普通文件,而不是目录,设备文件)
    find命令可使用的文件类型
    f 规则或普通文件  d 目录  b 块特殊设备 c 字符特殊设备raw  l 符号链接  p 有名管道
    find -mtime 定位最后一次修改的文件或定位在很久一段时间内都未改变过的文件,该参数以天为单位
    find / -mtime -5 -print
    +n只寻找改变日期在n天之前的
    n 只寻找改变日期在n天前当天的
    -n只寻找改变日期在n天之内的
    find / -mtime +90 -print
    -mtime 找到上次改变时间大于.刚好.或少于n天前
    -atime 找到上次访问时间大于.刚好.或少于n天前
    -ctime 找到其inode上次改变时间大于,刚好.或少于n天前的文件 inode是磁盘表中一项,包含有关文件属性.大小.最后访问时间等
    find -size 基于文件的块大小定位文件
    find / -size +2000 -print 所有大小超过2000块的文件的文件名
    +n 只寻找文件大小超过n块的文件
    n  只寻找文件大小等于0块的文件
    -n 只寻找文件大小小于n块的文件
    find 组合选项
    find / -name efrioo -size +50 -mtime -3 -print
    使用-o  指定一个逻辑条件 或
    find / "( -size +50 - o -mtime -3 ") -print
    find 否定选项  !
    find /dev !"(-type b -o -type c -o -type d") -print
    -print 一个动作,向标准输出显示文件的全路径
    若在其他选项之前,则后面的被忽略
    -exec lp{}"; 一个动作,使用lp命令打印任何匹配规则的文件的硬拷贝,可指定多个动作
    -exec使用户指定一个unix命令
    find / -name efrioo -exec chmod a+r {}";
    find / -name efrioo -exec rm -f {}"   知道文件后执行rm命令删除它们 -f不要求用户确认
    xargs命令:从标准输入接收一系列单词并将这些单词提供给一个给定命令做为参数
    cat filelist|xargs rm
    使用 -n选项,可以指定每次从标准输入取来多少个参数用语构造命令行
    cat filelist|xargs -n 20 rm
    ls |xargs -n 2 echo -----  指定每行显示的文件数
    ls|grep '^acb'|xargs -n 20 rm
    expr命令:执行简单的整数算术运算
    expr操作符:+ - "*  /  %
    expr 3"*5   3乘5
    expr 19%7   求余mod  5
    expr要求参数都要被分割开,参数之间都由一个空格分开
    cnt=`expr $cnt+1`    后引号完成命令替换
    匹配一个正规表达式
    expr $abc :'.*'       .*是一个可以指出所有字符的正规表达式 ,变量$abc中的所有字符都被计算内
    expr $abc :'[0-9]*'      计算在字符串开始部分的数字的个数
    expr abcdef :'.."(..|)..' 每个点都是一个正规表达式通配符,代表给定字符串中的一个字符,括号中两个字符被输出
    bc命令:是一个不局限于整数的算术工具
    bc进入 quit退出
    bc可精确计算任何大小的数字  + - * / % ^
    bc可为一个变量赋一个计算出的值
    average = `echo "scale=4;$price/$units"|bc`
    设置obase=16 输出进制是16进制
        ibase=8  输入进制是8进制
    远程shell
    remsh/rsh/rcmd/remote
    rmesh remote-sys unix-command
    向远程系统拷贝整个目录树
    find . -print | cpio -ocva | remsh remote_sys "(cd /destdir "; cpio -icdum ")
    高级主题
    十九.信号处理
    shell脚本中的重要信号
    SIGHUP  1  检测控制终端的挂起或控制进程的死亡
    SIGINT  2  键盘中断
    SINGQUIT  3  从键盘退出
    SIGKILL  9  杀死信号
    SIGALRM  14  报警时钟信号(为计时器使用)
    SIGTERM  15  终止信号
    信号列表 signal.h
    man 7 signal
    man -s 5 signal
    man 5 signal
    可以理解的信号列表
    kill -l
    缺省动作
    终止进程   忽略信号  内核转储(创建一个名为core的文件,包含接受到信号时进程的内存镜像)  停止进程  继续一个停止的进程
    传递信号
    最常用的是在脚本执行时按 control-c或interrupt键
    其他传递信号的常用方法:kill -signal pid
    TERM
    缺省条件下,kill向进程ID为pid的进程发送一个TREM或终止信号
    kill pid  = kill -s SIGTERM pid
    HUP
    kill -s sighup 1001  发送挂起信号
    kill -1 1001
    如果kill命令不能终止一个进程,用户可向进程发送quit或int(interrupt)信号
    kill -s SIGQUIT 1001
    kill -s SIGINT 1001
    信号处理函数
    kill -9 1001
    信号处理
    trap命令:设置或取消收到一个信号时的动作
    trap name signals  当信号被接收时,就执行name中列出的命令或shell函数
    三种trap的常见用法:清除临时文件.一直忽略信号.只在关键操作期间忽略信号
    清除临时文件
    trap "rm -f $TMPF; exit 2 " 1 2 3 15
    当接收到一个HUP,INT,QUIT,TERM信号时,就删除在$TMPF中存储的文件并返回代码2指出退出非正常,正常退出返回0
    cleanup() {
      if [-f "$outfile" ] ; then
         printf "cleaning up...";
         rm -f "$outfile" 2>/dev/null;
         echo "done.";
      fi
    }
    trap cleanup 1 2 3 15
    维持一个进程活着的脚本在接收不同信号时行为就不同:
    #!/bin/sh
    if [$# -lt 1 ] ; then
       echo "usage: `basename $0` command."
       exit 0
    fi
    init() {    ---负责停止任何正在运行的程序,然后再次启动它
      printf "info: initializing..."
      kill -0 $! 2>/dev/null;
      if [$? -eq 0] ; then
         kill $!>/dev/null 2>&1
         if [$? -ne 0] ; then
            echo "error:already running as pid $!.exiting."
            exit 1
         fi
      fi
      $PROG &
      printf "done."n"
    }
    cleanup() {       ---负责杀死正在运行的程序并退出
      kill -9 $!;exit 2;
    }
    #main()         --搜索程序并等待它结束,再往下查,接收到一个INT,QUIT,TERM信号时退出
    trap cleanup 2 3 15
    trap init 1
    PROG=$1
    init
    while : ;
    do
      wait $!
      $PROG &
    done
    忽略信号
    trap '' signals
    trap : signals
    脚本要忽略所有信号,则应该在脚本开头增加
    trap '' 1 2 3 15
    给出该命令时,脚本将忽略所有信号,一直到它退出
    在关键操作期间忽略信号
    trap ' ' 1 2 3 15
    doimportantstuff  --函数
    trap 1 2 3 15
    建立一个计时器
    使用ALARM信号和一个信号处理函数来设置一个计时器
    #main()
    trap alarmhandler 14
    settimer 15
    $PROG &
    CHPROCIDS="$CHPROCIDS $!"   --用来维护脚本所有的子进程列表
    wait $!
    umsettimer
    echo "all done"
    exit 0
    为ALARM信号建立一个处理函数
    alarmhandler() {
      echo "got sigalarm,cmd took too loog."
      killsubprocs
      exit 14
    }
    杀死脚本保存在变量CHPROCIDS中的所有子进程
    killsubprocs() {
      kill ${CHPROCIDS:-$!}
      if [$? -eq 0 ] ; then
         echo "sub-processes killed";
      fi
    }
    settimer
    使用settimer函数设置计时器
    settimer() {
      def_tout=${1:-10};
      if [$def_tout -ne 0]; then
         sleep $def_tout && kill -s 14 $$ &
         CHPROCIDS="CHPROCIDS $!"
         TIMERPROC=$!
      fi
    }
    计时器是一个后台进程,要用进程ID 来提交子进程列表,还要在变量TIMER PROC中保存计时器的进程ID,便于以后能取消计时器
    unsettimer() {
      kill $TIMERPROC
    }
    二十.调试
    启动调试--调用激活(invocation activated)调试方式
    执行shell脚本的一个常用方法
    script arg1 arg2 ...argn
    /bin/sh script arg1 arg2 ... argn
    通过为shell提供参数来启动一个调试模式:
    /bin/sh option script arg1 arg2 ... argn
    改变脚本的第一行
    #!/bin/sh     #!/bin/sh option
    shell脚本的调试选项
    -n 读所有的命令,但不执行它们
    -v 在读时显示所有的行
    -x 在执行时显示所有命令和他们的参数(为shell跟踪选项)
    使用set命令,用户可以在shell脚本的任何地方启动或取消调试
    使用set启动调试
    set option
    #!/bin/sh
    set -x
    if [-z "$1" ] ; then
       echo "error:insufficient args"
       exit 1
    fi
    使用set取消调试
    set +option
    set +x  取消shell跟踪调试模式
    所有为一个脚本启动的调试模式都可以用下面命令来取消
    set -
    为一个函数启动调试
    set -x; buggyfunction; set +x  保证了函数的实现不被改动
    语法检查
    /bin/sh -n script arg1 arg2 ...argn
    使用verbose模式
    shell提供-v(verbose)调试模式来检查语法错误发生的上下文,脚本的每一行都打印出来
    用户只运行-v选项,则脚本的每一行都要执行
    用户要检查语法错误,应组合-n和-v选项:
    /bin/sh -nv script arg1 arg2 ... argn
    #!/bin/sh
    failed() {
      if [$1 -ne 0 ] ; then
         echo "failed exiting." ; exit 1;
      fi
      echo "don."
    }
    echo "deleting old backups,please wait ..."c"
    rm -r backup> /dev/null 2>&1
    failed $?
    echo "make backup(y/n)?"c"
    read RESPONSE
    case $RESPONSE in
      [yY]|[Yy][Ee][Ss])
         echo "making backup,please wait..."c"
         cp -r docs backup
         failed ;;
      [nN]|[Nn][Oo]
         echo "backup skipped";;
    esac
    shell跟踪
    -x (execution)
    set -x; ls *.sh; set +x
    #!/bin/sh
    failed() {
      if [$1 -ne 0] ; then
        echo "failed exiting " ; exit 1;
      fi
      echo "done"
    }
    yesno() {
      echo "$1 (y/n)? "c"
      read RESPONSE
      case $RESPONSE in
        [yY]|[Yy][Ee][Ss]) RESPONSE=y ;;
        [nN]|[Nn][Oo] RESPONSE=n;;
        *) RESPONSE=n;;
      esac
    }
    yesno "make backup"
    if [$RESPONSE ="y"] ; then
      echo "deleting old backups ,please wait..."c"
      rm -fr backup >/dev/null 2>&1
      failed $?

      echo "making new backups ,please wait..."c"
      cp -r docs backup
      failed $?
    fi
    使用shell跟踪找出逻辑缺陷
    使用调试陷阱
    调试陷阱是在函数或关键代码部分启动shell跟踪的函数
    脚本用一个特定的命令行选项或把环境变量置为true(DEBUG=true或TRACE=true)
    debug() {
      if ["$debug"="true"] ; then
        if ["$1"="on" -o "$1"= "ON"]; then
           set -x
        else
           set +x
        fi
      fi
    }
    激活调试:debug on
    取消调试:debug 或 debug off
    应用在程序中:debug on
                .........
                 debug off
    脚本运行:DEBUG=true /bin/sh ./s1.sh
    二十一.使用函数解决问题
    创建一个函数库:一个shell函数库一般不包含任何主代码,而只包含函数
    从一个库中包含函数
    .file
    #!/bin/sh
    .$HOME/lib/sh/messages.sh
    MSG="hello"
    echo_error $MSG
    命名习惯
    库命名
    $HOME/lib/sh/libTYSP.sh
    使用改库:.$HOME/lib/sh/libTYSP.sh
             ./usr/local/lib/sh/libTYSP.sh
    函数命名
    print prompt get
    有用的函数
    printERROR
    printWARNING
    printUSAGE
    promptYESNO
    promptRESPONSE
    getSpaceFree
    getPID
    getUID
    询问一个问题
    询问yes或no问题
    promptYESNO() {
      if [$# -lt 1] ; then
        printERRPR "insufficient arguments"
        return 1
      fi
    DEF_ARG=""
    YESNO=""
    case "$2" in
      [yY]|[yY][eE][sS]) DEF_ARG=y;;
      [nN]|[nN][oO]) DEF_ARG=n;;
    esac
    while :
    do
      printf "$1 (y/n)?"
      if [-n "$DEF_ARG" ] ; then
        printf "[$DEF_ARG]"
      fi
      read YESNO
      if [-z "$YESNO"] ; then
        YESNO="$DEF_ARG"
      fi
      case "$YESNO" in
        [yY]|[yY][eE][sS]) YESNO=y; break;;
        [nN]|[nN][oO]) YESNO=n; break;;
        *) YESNO="";;
      esac
    done
    export YESNO
    unset DEF_ARG
    return 0
    }
    函数应用
    promptYESNO "make backup"
    if ["$YESNO" = "y" ] ; then
      cp -r docs backup
    fi
    提示要求一个回答
    promptRESPONSE() {
      if [$# -lt 1] ; then
        printERROR "insuficient arguments"
        return 1
      fi
      
      RESPONSE =""
      DEF_ARG=""
      while :
      do
        printf "$1 ?"
        if [-n "$DEF_ARG"] ; then
          printf "[$DEF_ARG]"
        fi
        read RESPONSE
        if [ -n "$RESPONSE"] ; then
          break
        elif [-z "$RESPONSE" -a -n "$DEF_ARG" ] ; then
          response = "$DEF_ARG"
          break
        fi
      done
      export RESPONSE
      unset DEF_ARG
      return 0
    }
    函数应用:
    promptRESPONSE "in which idrectory do you want to install"
    if [! -d "$RESPONSE" ] ; then
      echo "the directory $RESPONSE does not exist"
      promptYESNO "create it " "y"
      if ["$YESON" = "y" ] ; then
         mkdir "$RESPONSE"
      else
         exit
      fi
    fi
    检查磁盘空间
    getSpaceFree
    getSpaceUsed
    计算剩余空间
    使用df -k (k指KB)命令
    df -k /home/ranga
    getSpaceFree() {
      if [$# -lt 1] ; then
         printERROR "insufficient arguments"
         return 1
      fi
      df -k "$1" | awk 'NR!=1 {print $4 ; }'
      if ["`getSpaceFree /usr/local`" -gt 20000] ; then
        echo "enough space"
      fi
    }
    计算已用空间
    计算一个目录使用了多少磁盘空间du
    -s 要输出整个目录和它的内容
    du -sk
    du -sk /home/rang/pub
    getSpaceUsed() {
      if [$# -lt 1] ; then
        printERROR "insufficient arguments"
        return 1
      fi
      if [! -d "$1" ] ; then
        printERROR "$1 is not a directory"
        return 1
      fi
      du -sk "$1"|awk '{print $1;}'
      if ["`getSpaceSpace /home/ranga/pub`" -gt 10000] ; then
        printWARNING "your're using to much space!"
      fi
    }
    通过名字获得进程ID
    getPID() {
      if [$# -lt 1] ; then
        printERROR "INSUFFICIENT ARGUMENTS"
        return 1
      fi
      PSOPTS = "-ef"
      /bin/ps $PSOPTS |grep "$1" | grep -v grep |awk '{print $2;}'
    }
    ps -ef |grep sshd
    getPID httpd
    ps -auwx (linux freebsd)
    获得一个用户ID
    id tiptop
    id
    getID() {
      id $1 |sed -e 's/(.*$//' -e 's/^uid=//'
      if ["`getUID`" -gt 100] ; then     --是否大于100
        printERROR "you do not have sufficient privileges"
        exit 1
    }
    getID
    getID tiptop
    完整函数库
    echo "error:"
    [email=$@>&2]$@>&2[/email]
    echo "warning:"
    [email=$@>&2]$@>&2[/email]
    echo "usage:" $@
    df -k "$1" | awk 'NR!=1 {print $4;}'
    du -sk "$1"| awk '{print $1;}'
    ps -ef |grep "$1" |grep -v grep |awk '{print $2;}'
    二十二.使用shell脚本解决问题
    移动目录
    rm -rf a     cp -r b a   rm -rfb  
    使用tar (内容没有压缩,存储了文件权限和文件所包含的分组和拥有者信息)
    创建一个tar文件
    tar -cpf - source     (- 指出创建的tar文件应被写到STDOUT)
    从STDIN解开一个tar文件命令
    tar -xpf -  (- 指出tar文件应从STDIN读入)
    最终使用的命令:
    tar -cpf - source | (cd destination ; tar -xpf -)  source,destination都是目录
    mudir.sh
    #!/bin/sh
    path = /bin:/usr/bin;export path
    printERROR() {
      echo "error:$@">&2 ; exit 1;
    }
    printusage() {
      echo "usage:`/bin/basename $@`
    [email=$@">&2]$@">&2[/email]
    ; exit 1;
    }
    if [$# -lt 2]; then printusage "[src] [dest] " ; fi
    if [! -d "$1" ] ; then
       printERROR "the source $1 is not a directory ,or does not exist"
    fi
    SRCDIR_PARENT="`/usr/bin/dirname $1`"
    ARCDIR_CHILD="`/bin/basename $1`"
    SRCDIR_PARENT="`(cd $SRCDIR_PARENT ; pwd;)`"
    if [-d "$2"] ; then
       DESTDIR=`(cd "$2" ; pwd;)`
    else
       DESTDIR="`/usr/bin/dirname $2`"
       NEWNAME="`/BIN/BASENAME $2`"
       DESTDIR=`(cd $DESTDIR; pwd;)`
       if [! -d "$DESTDIR"] ; then
         printERROR "a parent of the destination directory $2 does not exist"
       fi
    fi
    cd "$SRCDIR_PARENT">/dev/null 2>&1
    if [$? -ne 0]; then
      printERROR "could not cd to $SRCDIR_PARENT"
    fi
    /bin/tar -cpf - "$SRCDIR_CHILD" |(cd "$DESTDIR"; /bin/tar -xpf -)
    if [$? -ne 0]; then
       printERROR "unable to successfully move $1 to $2"
    fi
    if [-n "$NEWNAME"] ; then
      cd "$DESTDIR">/cev/null 2>&1
      if [$? -ne 0] ; then
        printERROR "could not cd to $DESTDIR"
      fi
      /bin/mu "$SRCDIR_CHILD" "$NEWNAME" > /dev/null 2>&1
      if [$? -ne 0] ; then
         printERROR "could not rename $1 to $2"
      fi
      cd "$SRCDIR_PARENT" >/dev/null 2>&1
      if [$? -ne 0] ; then
         printERROR "could not cd to $SRCDIR_PARENT"
      fi
    fi
    if [-d "$SRCDIR_CHILD"] ; then
       /bin/rm -r "$SRCDIR_CHILD">/dev/null 2>&1
       if [$? -ne 0] ; then
         printERROR " could not remove $1"
       fi
    fi
    exit 0
    示例
    ./mvdir.sh /tmp/ch22 /home/ranga/docs/book
    ls /tmp /home/ranga/docs/book
    维护一个地址簿
    显示信息
    awk -F:'{ printf "name:%s"nemail:%s"naddress:%s"nphone:%s"n"n",$1,$2,$3,$4;}'
    showperson脚本程序
    #!/bin/sh
    path=/bin:/usr/bin
    if [$# -lt 1]; then
       echo "usage: `basename $0` name"
       exit 1
    fi
    MYADDRESSBOOK = "$HOME/addressbook"
    if [! -f "$MYADDRESSBOOK"] ; then
       echo "ERROR: $MYADDRESSBOOK does not exist ,or is not a file " >&2
       exit 1
    fi
    grep "$1" "$MYADDRESSBOOK"|
       awk -F:'{ printf "%-10s %s"n%-10s %s"n%-10s %s"n%-10s %s"n"n",""name:",$1,"email:",$2,"address:",$3,"phone:",$4;}'
    exit $?应用:
    ./showperson ranga
    增加信息
    addperson脚本的完整程序清单
    #!/bin/sh
    path =/bin:/usr/bin
    MYADDRESSBOOK=$HOME/addressbook
    NAME=""
    EMAIL=""
    ADDR=""
    PHONE=""
    remove_colon() {
      echo "$@" |tr ':' ' ' ;   --删除字符
    }
    if [$# -lt 1 ] ; then
      stty erase '^?'
      printf "%-10s " "Name:" ; read NAME
      printf "%-10s " "Email:" ; read EMAIL
      printf "%-10s " "Address:" ; read ADDR
      printf "%-10s " "Phone:" ; read PHONE
    else
      usage= "`basename $0` [ -n name] [ -e email] [ -a address] [-p phone]"
      while getopts n:e:a:p:h OPTION
      do
        case $OPTION in
          n) NAME="$OPTARG" ;;
          e) EAMIL="$OPTARG";;
          a) ADDR="$OPTARG";;
          p) PHONE="$OPTARG";;
          "?|h) echo "usage:$usage" >&2 ; exit 1 ;;
        esac
      done
    fi
    NAME="`remove_colon $NAME`"
    EMAIL="`remove_colon $EMAIL`"
    ADDR="`remove_colon $ADDR`"
    PHONE="`remove_colon $PHONE`"
    echo "$NAME:$EMAIL:$ADDR:$PHONE">>"$MYADDRESSBOOK"
    exit $?
    应用:
    ./addperson
    ./addperson -n "james" -e
    [email protected]
    -a "1701 main street"
    删除信息
    delperson脚本程序清单
    #!/bin/sh
    .$HOME/lib/sh/libTYSP.sh
    path=/bin:/usr/bin
    if [$# -lt 1] ; then
       printUSAGE "`basename $0` name"
       exit 1
    fi
    MYADDRESSBOOK="$HOME/addressbook"
    if [! -f "$MYADDRESSBOOK"] ; then
       printERROR "$MYADDRESSBOOK does not exists,or is not a file"
       exit 1
    fi
    TMPF1=/tmp/apupdate.$$
    TMPF2=/tmp/abdelets.$$
    docleanup() {
      rm "$TMPF1" "$TMPF1.new" "$TMPF2" 2>/dev/null;}
    failed() {
      if ["$1" -ne 0 ] ; then
        shift
        printERROR $0
        docleanup
        exit 1
      fi
    }
    cp "$MYADDRESSBOOK" "$TMPF1" 2>/dev/null
    failed $? "could not make a backup of the address book"
    grep "$1" "$TEMPF1" >"$TMPEF2" 2>/dev/null
    failed $? "no matches found"
    exec 5 "$TMPF1.new" 2>/dev/null
         failed $? "unable to update the address book"
         mv "$TMPF1.new" "$TMPF1" 2>/dev/null
         failed $? "unable to update the address book"
      fi
    done
    exec 5/dev/null
    failed $? "unable to update the address book"
    mv "$TMPF1" "$MYADDRESSBOOK" 2> /dev/null
    failed $? "unable to update the address book"
    docleanup
    exit $?
    }
    二十三.脚本可移植性
    判断UNIX版本
    BSD berkeley software distribution
    openbsd netbsd freebsd sunos
    system v
    solaris sunos hp-ux
    BSD      SYSTEM V
    /bin      /usr/bin
    /sbin     /usr/sbin
    /usr/adm  /var/adm
    /usr/mail /var/mail  /var/spool/mail
    /usr/tmp  /var/tmp
    linux的命令和网络都是基于BSD
    caldera    red hat
    使用uname
    -a 打印所有信息
    -m 打印当前硬件类型
    -n 打印系统主机名
    -r 打印操作系统发行版本号
    -s 打印操作系统的名字
    sun microsystems  --solaris(新 system v)  sunos(BSD)
    uname -rs
    判断硬件类型
    uname -m
    判断一个系统的主机名
    hostname
    uname -n
    使用函数判断UNIX版本
    getosname() {
      case '`uname -s` in
        *BSD) echo bsd ;;
        SunOS)
           case `uname -r` in
              5.*) echo solaris ;;
                *) echo sunos;;
           esac
            ;;
        Linux) echo linux ;;
        HP-UX) echo hpux ;;
        AIX) echo aix ;;
        *) echo unknown ;;
      esac
    }
    isos() {
      if [$# -lt 1] ; then
         echo "ERROR: insufficient arugments " >&2
         return 1
      fi
      REQ=`echo $1 |tr '[A-Z]' '[a-z]'`
      if ["$REQ" = "`getosname`"] ; then return 0 ; fi
      return 1
    }
    提高可移植性的技巧
    条件执行
    执行远程命令 rsh  remote shell
    if SystemIS HPUX ; then
       RCMD = remsh
    else
       RCMD=rsh
    fi
    设置了变量$RCMD后,可执行远程命令
    "$RCMD" host command
    抽取
    getfreespace()
    if isos HPUX ; then
      df -b "$DIR" |awk '{print $5;}'
    else
      df -k "$DIR" |awk 'NR!=1 {print $4;}'
    fi
    getpid()
    case `getosname` in
       bsd|sunos|linux) PSOPTS="-auwx" ;;
       *) PSOPTS="-ef" ;;
    esac
    /bin/ps $PSOPTS 2>/dev/null | grep "$1" |grep -v grep |awk '{print $2;}'
    二十四.shell编程疑难解答
    判断shell是否交互式
    test -t
    tty -s
    如何去掉一条命令的输出
    command >/dev/null
    去掉一条命令的输出和错误输出,使用标准重定向把STDERR重定向到STDOUT
    command >/dev/null 2>&1
    怎样把信息显示到STDERR
    echo message 1>&2
    怎样判断shell能否找到一条特定的命令 whence -v
    type name >/dev/null 2>&1; if [$? -ne 0]; then list ; fi
    怎样判断任务控制是否在shell中可以使用 jobs jsh
    if type jobs >/dev/null 2>&1 ; then
       echo "wo have job control"
    fi
    一次一个的考虑一个shell脚本的每个参数
    for arg in "$@"
    do
      list
    done
    把一个脚本的所有参数赋给另一条命令
    command "$@"
    在一条sed命令使用一个shell变量的值
    sed "/$DEL/d" file1>file2
    检查一个变量是否有一个值
    if [-z "$VAR"]; then list ; fi
    初始化变量
    :${VAR:=default}
    :${VAR:=`default`}
    确定一个目录的全路径名
    fullpath='(cd dir; pwd)'
    确定一个文件的全路径名
    CURDIR=`pwd`
    cd `dirname file`
    FULLPATH="`pwd`/`basename file`"
    cd $CURDIR
    定位一个特定文件
    find dir -name file -print
    find dir -name "*txt" -print
    把find命令和xargs命令组合在一起目录及其子目录包含的每一个文件中查找一个特定字符串:
    find dir -type f -print | xargs grep "string"
    find s -type f -print | xargs grep "/bin"
    在一个目录中删除匹配一个特定名字的所有文件
    LODIFS="$IFS"
    IFS='
    '   ---为了使for循环在每次循环时把FILE置为正确的值,IFS需要被置成换行符
    for FILE in `find . -type f -name "*string*" -print`
    do
       rm "$FILE"
    done
    IFS="$OLDIFS"
    把所有*.aaa文件重命名为*.bbb文件
    dos:
    rename *.aaa *.bbb
    unix:
    OLDSUFFIX=aaa
    NEWSUFFIX=bbb
    for FILE in *."$OLDSUFFIX"
    do
      NEWNAME=`echo "$FILE" |sed -e "s/${OLDSUFFIX}"$/$NEWSUFFIX/"`
      mv "$FILE" "$NEWNAME"
    done
    mv -i 更改前提示
    把所有aaa*文件重命名为bbb*文件
    OLDSUFFIX=aaa
    NEWSUFFIX=bbb
    for FILE in "$OLDSUFFIX"*
    do
      NEWNAME=`echo "$FILE" |sed -e "s/^${OLDSUFFIX}/$NEWSUFFIX/"`        s/替换
      mv "$FILE" "$NEWNAME"
    done
    把文件名置为小写字母
    for FILE in *
    do
      mv -i "$FILE" `echo "$FILE"|tr '[A-Z]' '[a-z]'` 2>/dev/null
    done
    消除文件中的回车
    "r回车  "n换行
    tr -d '"015'  newfile         "015八进制表示的回车
    命令快速参考
    .在当前
    !!重复执行前次命令
    alias 为命令创建一个短名字
    eval使shell重新解释后面的命令
    exit n 用状态码n来结束shell脚本
    fc 显示或编辑历史列表中的一条命令
    fg把一个后台或悬挂任务放到前台
    function 此关键字定义了一个使用局部变量的函数
    getopts在循环中重复调用一个函数来处理命令行参数
    history显示该用户最近运行的命令
    integer整数变量
    jobs列出后台和悬挂任务
    let执行整数运算
    newgrp改变用户的基本分组,影响用户在分组中创建的所有新文件和目录
    print 与echo相同
    pwd打印出现在正在工作的或当前目录
    r 再次执行上一条命令
    read 等待一行标准输入并把每个单词保存在后面的变量中
    readonly标记后面的变量为只读
    select提供一个菜单并使用户可以选择
    set显示或改变shell选项
    shift丢弃$1并把所有参数提前一个位置来代替它
    test提供许多选项来检测文件.字符串和数值,通常用[表示
    trap指明当接收到一个特定信号时执行的代码
    type显示后面命令的路径名或指出它是一个内部命令还是别名
    typeset设置变量类型及其初值
    ulimit显示或设置最大文件或资源的限制
    umask显示或设置一个屏蔽来影响用户创建的新文件或目录的权限
    unalias删除一个别名
    unset取消后面所跟变量的定义
    until一直循环到测试命令为真
    wait一直等待到所有后台任务完成
    whence类似于type命令
    while循环到一个测试命令为真
    文件测试
    -a file file存在则为真
    -b file file是一个块特殊设备则为真
    -c是一个字符特殊设备则为真
    -d是一个目录则为真
    -f是一个普通文件则为真
    -g设置了SGID权限位则为真
    -G file的组与用户组相匹配则为真
    -k设置了粘位则为真
    -L是一个符号链则为真
    -O运行该命令的用户拥有该file则为真
    -p是一个命名管道或fifo则为真
    -r是可读的则为真
    -s大小大于0则为真
    -S是一个socket则为真
    -t文件描述符与一个终端相关联则为真
    -u设置了SUID权限位则为真
    -w是可写的则为真
    -x是可执行的则为真
    字符串测试
    -z string string为空时为真
    -n string string大小不为0时为真
    整数比较
    n1 -eq n2 等于为真
    n1 -ne n2 不等于为真
    n1 -gt n2 大于为真
    n1 -ge n2 大于等于为真
    n1 -lt n2 小于为真
    n1 -le n2 小于等于为真
    !expr expr为假时为真
    -a  and
    && and
    -o  or
    || or
    算术表达式
    let "variable=integer_expression"  变量赋值
    - 一元减(为后面的值取反)
    !~ 逻辑not,二进制补码
    */% 乘 除 求余  + -
    >>>2)) =8
    =小于等于 大于等于
    >>=  
    参数和变量
    uservar=value  赋值
    $uservar 用uservar的内容替换
    ${uservar} 替换为uservar的内容
    uservar[index]=value
    ${uservar[index]} 把一个值替换到命令行
    ${uservar
  • } 用所有的数组元素替换
    ${uservar[@]} 用所有的数组元素替换并把每个用双引号扩起来
    korn数组的初始化
    set -A uservar value1 value2 ...
    bash数组的初始化
    uservar=(value1 value2 value3...)
    内置shell变量
    $0 执行的命令或脚本的名字
    $n 定位的参数
    $# 命令行中所给变元的数目
    $* 所有命令行参数的列表
    $@ 所有命令行参数的列表每个用双引号括起
    $? 最后一条命令的退出状态值
    $$ 当前的shell的PID值(进程ID)
    $! 最后一个后台命令的PID(进程ID)值
    直接影响到变量的内置命令
    getopts  export read  readonly unset
    shell变量
    CDPATH 包含以冒号分开的目录列表
    HOME 用户的起始目录
    IFS 内部域分割符
    OPTARG 由getopts处理的最后一个命令行参数
    OPTIND 由getopts处理的最后一个命令行参数的索引值
    PATH 包含一个冒号隔开的目录列表来搜索不包含任何斜杠的命令
    PS1基本shell提示字符串
    PS2续行时的从属提示字符串
    PWD 当前目录
    RANDOM每次调用时返回一个不同的随机数(0-32767)
    REPLY 通过select命令读入的最后一行输入
    SECONDS自从激活shell后所经过的秒数
    SHLVL当前激活的shell的数目
    UID用户数字ID号
    参数替换
    ${parameter} 替换parameter的内容
    ${parameter:-word} 替换parameter的内容,但如果它是空的或未定义,则替换word,它可能包含一个未加引号的空格
    ${parameter:=word}替换parameter的内容,但如果它是空的或未定义,则把word赋给parameter并替换word
    ${parameter:?message}替换parameter的内容,但如果是空的或未定义,则放弃脚本并给出message做为一个最终错误消息
    ${parameter:+word}如果parameter不是空的,则替换word,否则不替换
    仅在korn/bash中使用的参数替换
    ${#parameter}替换parameter中包含的字符数
    ${#array
  • }替换array中元素数目
    ${parameter#pattern}如果在parameter的开头找到了给出的正规表达式pattern,则删除匹配的字符并替换其余字符.
    ${parameter##pattern}和上面同,但在parameter开头的最大可能匹配被删除.
    ${parameter%pattern}和上面同,但删除parameter末端的最小匹配
    ${parameter%%pattern}和上面同,但删除parameter末端的最大匹配
    在bourne/korn/bash中使用的模式通配符
    * 匹配0个或更多的任意字符
    ? 精确匹配一个任意字符
      精确匹配list中任意一个字符
      [!list] 精确匹配任意一个不在list中的字符
      仅在KORN中使用的通配符
      ?(pattern1|pattern2...)匹配模式中的任何一个
      *(pattern1|pattern2...)匹配模式0次或多次的出现
      +(pattern1|pattern2...)匹配模式一次或多次的出现
      @(pattern1|pattern2...)仅匹配模式中的一个
      !(pattern1|pattern2...)匹配除了模式以外任何字符串
      I/O
      STDIN 标准输入 0
      STDOUT标准输出 1
      STDERR标准错误 2
      cmd >file
      cmd 1>file STDOUT保存到file
      cmd >>file
      cmd 1>>file STDOUT附加到file
      cmd 2>file    STDERR
      cmd 2>>file
      cmd
      cmd1 |cmd2 把cmd1的STDOUT作为cmd2的STDIN
      cmd |tee file 把unix命令的STDOUT保存到file中,同时把该文本当作STDOUT
      exec n>file 把文件描述符n的输出重定向到(覆盖)file ,它用在紧接着的unix命令中
      exec n>>file 同上,但是附加到file
      cmd 2>&1 把STDERR重定向到STDOUT当前所指向的地方,把输出和错误保存在一个文件中或把他们一起送到另一条命令时有用
               cmd>file 2>&1 STDERR和STDOUT都保存到FILE中
      cmd>&2 把STDOUT重定向做STDERR.在ECHO显示一个错误信息时有用
      cmd 1>&2 同上
      cmd n>&m 把文件描述符n重定向到文件描述符m当前所指向的地方,大于2的n和m可以用来保存一个I/O目标并在以后提取它
      常用命令汇总
      echo
      "b backspace
      "c 抑制跟踪新行
      "f 换页
      "n 换行
      "r 回车
      "t Tab
      "" 反斜杠
      "0nm ASCII码为八进制nm的字符
      grep
      -i 忽略大小写
      -l 仅显示包含匹配的文件名,而不是匹配的行
      -n 显示文件行号和每个匹配的行
      -r 与测试相反,意味着忽略包含模式的行
      printf
      printf "text %[-]m.nx" arguments
      - 左对齐(可选)
      m 最小域长度
      n 字符串的最大长度,浮点数的小数部分位数
      x参数类型  s 字符串  c字符值  d十进制整数值 x十六进制值 o八进制值 e指数浮点值 f固定浮点值 g普通浮点值
      sort ---显示排序后的行
      -b 忽略开头的空格
      -d 忽略开头的标点
      -f 不分大小写
      -n 按开头数字大小排序
      -r 以相反顺序排序
      +n 排序时忽略前n个域
      gerp fgrep egrep sed vi perl awk都允许在模式搜索中使用正规表达式通配符
      所有的正规表达式都可以包含下面的通配符
      ^pattern 仅匹配以pattern开始的行
      pattern$ 仅匹配以pattern结尾的行
      . 恰好匹配一个任意字符
        恰好匹配list中的一个任意字符
        [^list] 恰好匹配一个在list中的任意字符
        * 匹配前面元素的0次或多次重复
        .* 匹配0个或多个任意字符
        扩充正规表达式通配符
        "{n"}  匹配前面元素的n次重复
        "{n,"} 匹配前面元素的n次或更多次重复
        "{n,m"}匹配前面元素的至少n次至多m次重复
        ? 匹配前面元素的0次或1次出现
        +匹配前面元素的1次或更多次出现
  • 你可能感兴趣的:(shell)