Shell编程基础 part 1/2

来源:互联网 发布:少数民族流动人口数据 编辑:程序博客网 时间:2024/05/21 06:26

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

原创粉丝点击