Shell编程基础 part 1/2

SHELL编程基础

shell脚本, 必须在开始的第一行输入:

1
#!/bin/bash

>#! 会告诉系统执行该脚本的程序, 例如bash;

Note 最好使用"!/bin/bash"而不是"!/bin/sh", 如果使用tc shell改为tcsh,其他类似.

保存文件后, 想要执行脚本, 必须先使它可执行:

1
chmod  +x filename

>输入 ./filename就可以执行


1 变量赋值和引用

变量无需事先声明, 变量的命名规则:
1) 首字符必须为字母(a-z, A-Z)或者下划线_; 2) 中间不能有空格, 可以有下划线; 3) 不能使用其他标点符号;

赋值表达式: 变量名=值; [没空格]

取得变量的值, 需要在变量前加'$'

1
2
3
4
5
#!/bin/bash
# 对变量赋值:
a= "hello world"   #等号两边均不能有空格存在
# 打印变量a的值:
echo  "A is:"  $a

>取得变量的值, 前面加 $;

Note 注意空格; 给变量赋值, 等号两边不能有空格;

区分文字和变量:

1
2
num=2
echo  "this is the ${num}nd"

>输出: 'this is the 2nd'num 

>不加花括号的话, shell会搜索 '&numnd' 变量, 发现没有值; 输出: 'this is the '

注意花括号的位置:

1
2
num=2
echo  "this is the {$num}nd"

>输出: 'this is the {2}nd'

Note shell的默认赋值是字符串赋值;

1
2
3
var=1
var=$var+1
echo  $var

>输出不是2而是 1+1;

为了达到数字效果:

1
2
3
4
5
6
let  "var+=1"
var= "$[$var+1]"
((var++))
var=$(($var+1))
var= "$(expr " $var " + 1)"  #不建议使用
var= "`expr " $var " + 1`"  #强烈不建议使用,注意加号两边的空格,否则还是按照字符串的方式赋值,`为Esc下方的`,而不是单引号'。

Note 前2种方式在bash下有效, 在sh下会出错;

>let表示数学运算, expr用于整数值运算, 每一项用空格分开, $[]将中括号内的表达式作为数学运算先计算结果再输出;

shell脚本有许多便利是系统自动设定的, 除了只在脚本内有效的普通shell便另外, 还有环境变量, 即由export关键字处理过的变量, 一般只在登录脚本中用到;


2 Shell里的流程控制

if语句

if 表达式为真, 则执行 then 后的部分

1
2
3
4
5
6
7
if  ....;  then
   ....
elif  ....;  then
   ....
else
   ....
fi

多数情况下, 可以使用测试命令来对条件进行测试, 比如可以比较字符串, 判断文件是否存在以及是否可读等等...通常用" [ ] "来表示条件测试; 

Note 要注意中括号前后的空格, 确保存在;

[ -f "somefile" ] :判断是否是一个文件

[ -x "/bin/ls" ] :判断/bin/ls是否存在并有可执行权限

[ -n "$var" ] :判断$var变量是否有值

[ "$a" = "$b" ] :判断$a和$b是否相等

Note 执行 man test 可以查看所有测试表的式可以比较和判断的类型; 

e.g.

1
2
3
4
5
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/hash比较来判断当前shell是否是bash;


&&和||操作符

类似C语言:

1
[ -f  "/etc/shadow"  ] &&  echo  "This computer uses shadow passwords"

&&是一个快捷操作符, 如果左边的表达式为真则执行右边的语句, 可以看作逻辑运算中的与操作;

>如果/etc/shadow文件存在, 则打印文字; 

或操作 || 类似:

1
2
3
4
mailfolder= /var/spool/mail/james
[ -r  "$mailfolder"  ] || {  echo  "Can not read $mailfolder"  exit  1; }
echo  "$mailfolder has mail from:"
grep  "^From "  $mailfolder

>先判断mailfolder是否可读, 可读则打印文件中的 From 行; [可读为true则不执行后面的条件]; 不可读则或操作生效, 打印错误信息, 脚本退出;

使用花括号以匿名函数的形式将两个命令放到一起作为一个命令, 以 ; 分割; 使用与, 或操作符会使脚本更便利;


case语句

case表达式可以用来匹配一个给定字符串, 而不是数字; (和C语言的switch...case不同)

1
2
3
4
case  ...  in
    ...)  do  something here
    ;;
esac

file命令 可以辨别一个给定文件的文件类型; e.g. file lf.gz --> lf.gz: gzip compressed data, ...

利用file命令写smartzip脚本, 自动解压bzip2, gzip, zip不同类项的压缩文件:

1
2
3
4
5
6
7
8
9
10
ftype= "$(file " $1 ")"
  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

Note 特殊变量$1, 包含传递给脚本的第一个参数值; e.g. 运行 smartzip sample.zip 时, $1就是字符串sample.zip; 


select语句

select表达式是bash的扩展应用, 用于交互式场合; 

1
2
3
4
select  var  in  ... ;  do
  break ;
done
.... now $var can be used ....

e.g.

1
2
3
4
5
echo  "What is your favourite OS?"
select  var  in  "Linux"  "Gnu Hurd"  "Free BSD"  "Other" do
   break ;
done
echo  "You have selected $var"

>自动按照1)...2)...的方式输出选项;


while/for 循环

只要测试条件为真, while循环将一直运行; 关键字break跳出循环, continue跳过一个循环;

1
2
3
while  ...;  do
    ....
done

for循环会查看字符串列表(字符串用空格分隔), 将其赋给一个变量:

1
2
3
for  var  in  ....;  do
    ....
done

e.g.

1
2
3
for  var  in  A B C ;  do
    echo  "var is $var"
done

e.g. 脚本打印RPM包统计信息: showrpm   

1
2
3
4
5
6
7
8
9
10
11
# list a content summary of a number of RPM packages
# USAGE: showrpm rpmfile1 rpmfile2 ...
# EXAMPLE: showrpm /cdrom/RedHat/RPMS/*.rpm
for  rpmpackage  in  "$@" do
    if  [ -r  "$rpmpackage"  ]; then
       echo  "=============== $rpmpackage =============="
       rpm -qi -p $rpmpackage
    else
       echo  "ERROR: cannot read file $rpmpackage"
    fi
done

Note 特殊变量$@, 包含输入的所有命令行参数值; e.g. showrpm ssh.rpm web.rpm, 那么"$@"(必须有引号)就会出现2个字符串: ssh.rpm, web.rpm; 

$* 有类似功能, 但是只有一个字符串, 如果不加引号, 带空格的参数会被截断;


3 Shell里的特殊符号

引号

在向程序传递任何参数之前, 程序会扩展通配符和变量; 这里的扩展是指程序会把通配符(比如*)替换成适当的文件名, 变量替换成变量值; 使用引号可以防止这种扩展;

e.g. 假设目录下有两个jpg, mail.jpg和tux.jpg;

1
echo  *.jpg

>输出两个文件名;

单引号和双引号可以防止通配符的扩展:

1
2
echo  "*.jpg"
echo  '*.jpg'

>输出都是 *.jpg ;

单引号更严格, 可以防止任何变量扩展; 双引号则是防止通配符扩展但允许变量扩展:

1
2
3
echo  $SHELL
echo  "$SHELL"
echo  '$SHELL'

>输出 /bin/hash 和 /bin/bash 和 $SHELL;

还有一种防止扩展的方法, 使用转义字符--反斜杠 \ ;

1
2
echo  \*.jpg
echo  \$SHELL

>输出 *.jpg 和 $SHELL ;


4 Here Document

需要将几行文字传递给一个命令时, here document是不错的选择; 对每个脚本写一段帮助性的文字, 如果使用here document就不必用echo函数一行行输出; here document用 << 开头, 后面接一个字符串, 字符串必须出现在here document的末尾;

e.g. 对多个文件重命名, 使用here document打印帮助:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# we have less than 3 arguments. Print the help text:
if  [ $ # -lt 3 ] ; then
cat  << HELP
 
ren -- renames a number of files using  sed  regular expressions USAGE: ren  'regexp'  'replacement'  files...
 
EXAMPLE: rename all *.HTM files  in  *.html:
    ren  'HTM$'  'html'  *.HTM
 
HELP                 #这里HELP要顶格写,前面不能有空格或者TAB制表符。如果cat一行写成cat << -HELP,前边可以带TAB.
    exit  0
fi
OLD= "$1"
NEW= "$2"
# The shift command removes one argument from the list of
# command line arguments.
shift
shift
# $@ contains now all the files:
for  file  in  "$@" do
    if  [ -f  "$file"  ] ;  then
       newfile=` echo  "$file"  sed  "s/${OLD}/${NEW}/g" `
       if  [ -f  "$newfile"  ];  then
        echo  "ERROR: $newfile exists already"
       else
          echo  "renaming $file to $newfile ..."
          mv  "$file"  "$newfile"
       fi
    fi
done

>第一个if表达式判断输入命令行参数是否小于3个; 如果小于, 则将帮助文字传递给cat命令, 打印到屏幕上, 然后退出;

Note 特殊变量 $# 表示包含参数的个数; $2, $3 ...代表第二, 第三个参数; 

>如果输入参数>=3个, 将第一个参数赋值给变量OLD, 第二个赋值给变量NEW;  shift命令将第一个和第二个参数从参数列表删除, 原来的第三个参数变成参数列表$*的第一个参数;

>进入循环, 命令行参数列表被赋给$file, 判断文件是否存在, 存在则通过sed命令搜索和替换来产生新文件名; 然后将反斜线内命令结果赋值给newfile; 最后得到了旧文件名和新文件名, mv命令进行重命名;

[HELP不是关键字, 可以被其他字符替换(EOF), 加上单引号/双引号防止扩展] 

refer to http://www.serverwatch.com/columns/article.php/3860446/Shell-Scripts-and-Here-Documents.htm

1
2
3
4
5
cat  <<-  'EOF'
     test  word
     text will be printed
     with out tabs
EOF

你可能感兴趣的:(Shell编程基础 part 1/2)