Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1462541
  • 博文数量: 122
  • 博客积分: 340
  • 博客等级: 一等列兵
  • 技术积分: 2967
  • 用 户 组: 普通用户
  • 注册时间: 2009-04-01 11:50
个人简介

斑竹网络专注为中小企业客户提供以管理服务为核心的IT全方位服务 https://www.sysadm.cn

文章分类

全部博文(122)

文章存档

2018年(2)

2017年(1)

2015年(2)

2014年(30)

2013年(81)

2011年(5)

2009年(1)

分类: 系统运维

2013-11-21 15:51:10

Shell 编程语法

(整理时间:2006-10-12)

三种指定解释执行脚本的Shell类型的方法

1. 如果Script的第一个非空白字元不是"#",则它会使用Bourne Shell  
2. 如果Script的第一个非空白字元是"#"时,但不以"#!"开头时,则它会使用C Shell  
3. 如果Script"#!"开头,则"#!"後面所写的就是所使用的Shell,而且要将整个路径名称指出来

注:

Bourne Shell的路径名称是:/bin/sh;

而 C Shell的路径名称是: /bin/csh

同时还可以用perl来解释执行,如 /etc/perl;

常用的系统变量

$HOME 使用者自己的目录  
$PATH 执行命令时所搜寻的目录  
$TZ 时区  
$MAILCHECK 每隔多少秒检查是否有新的信件  
$PS1 在命令列时的提示号  
$PS2 当命令尚未打完时,Shell 要求再输入时的提示号  
$MANPATH man 指令的搜寻路径 

$BASH 其值指向bash的路径位置

$BASH_VERSION :当前所使用的bash的版本号

$DIRSTACK:当前目录的上一层目录名

$EDITOR:脚本调用的默认编辑器,一般是vi 或者是emacs

$FUNCNAME : 当前的函数名

$GROUPS :当前用户属于的组的ID

$HOSTNAME :系统的名称

$HOSTTYPE :主机类型,如I386 I686

$IFS:内部域分隔符。这个变量用来决定Bash在解释字符串时如果字符串的分隔。默认是空格

$LINENO:这个变量记录它所在的shell脚本中它所在行的行号。一般用于调试目的。

$MACHTYPE:系统类型,I386

$OLDPWD:老的目录,即你切换到当前目录之前的工作目录;

$OSTYPE:操作系统类型

$PPID:一个进程的PPID,也就是它的父进程的进程ID

$PWD:工作上当(即当前所在的目录)

$SECONDS:这个脚本已经运行的时间(单位为秒)

$SHLVLShell层次,就是shell层叠的层次,如果是在命令行,则$SHLVL1,如果

是在脚本是,则为2,如在脚本里调用了第二个调本文件,则在第二个脚本文

件里的这个变量为:3,依次类推;

$TMOUTshell的超时时间,如果这个变量不为零,则当时间到了以后就退出shell

在命令行的情况下,如果这个值不为零,则时间到了之后,就会引起一个logout

$UID:当前用户的ID

${#string}: 字符串string的长度,也可以用expr length $string 来获得字符串的长度

${string:position} 表示截取从position位置之后的子字符串

${string: position:length} 表示从position开始截取长度为length长度的子字符串;

${string : -position }  用负值表示从字符串的后面的位置开始截取;

注:

上述的截取字符串也可以使用下列的命令:

expr substr $string $position $length 

${string#$substr} 表示将字符串string中的substr子字符截去;

${string%$substr} 表示从右开始将字符串string中的substr子字符串截去掉;

${string/substr/replacestr} :string中的substrreplacestr替换,只替换第一个子字符串

${string//substr/replacestr} 与上要类似,但是替换所有的substr子字符串;

${string/#substr/replacestr} 如果substr在字符串string的开头部分匹配到,则用replacestr来替换,否则不替换;:

string: abcABCDEF123abc,substrabc,string中的第一个abc可以被替换,但是如果stringtabcABCDEF123abc ,则一个都不会被替换;

${string/%substr/replacestr} 与上面类似,但是替换最后一个子字符串;

变量的间接引用

    当一个变量的值是另一个变量的名字,那可以用eval var=\$$var获得第二个变量的值,如:

var1=abc

abc=12345678

eval aa=\$$var1 则变量aa的值就是变量abc的值,也就是12345678

常用的特殊变量

$0 这个程式的执行名字  
$n 这个程式的第n个参数值,n=1..9  
$* 这个程式的所有参数  
$# 这个程式的参数个数  
$$ 这个程式的PID  
$! 执行上一个背景指令的PID  
$? 执行上一个指令的返回值

 Bourne Shell 中执行命令的五种方法

1. 直接下命令  

这个方式和在命令列中直接下命令的效果一样。  
2. 使用sh命令  

sh command  这个档案必须是Bourne ShellScript,但这个档案并不一定要设成可执行。 除此之外和直接下命令的方式一样。  
3. 使用"."命令  . command  

这时和使用sh命令相似,只不过它不像sh一般会产生新的process ,相反地, 它会在原有的process 下完成工作。  
4. 使用exec命令  exec command  

此时这个Script将会被所执行的命令所取代。当这个命令执行完毕之後,这个 Script也会随之结束。  
5. 使用命令替换  

这是一个相当有用的方法。如果想要使某个命令的输出成为另一个命令的参数 时,就一定要使用这个方法。我们将命令列於两个"`" 号之间,而Shell 会以 这个命令执行後的输出结果代替这个命令以及两个"`" 符号。  
str='Current directory is '`pwd`  
echo $str  
结果如下:  
Current directory is /users/cc/mgtsai  
这个意思是pwd 这个命令输出"/users/cc/mgtsai",而後整个字串代替原 来的`pwd` 设定str 变数,所以str 变数的内容则会有pwd 命令的输出。  
number=`expr $number + 1`  
这就是先前所提要作数值运算的方法,基本上expr命令只将运算式解,而 後输出到标准输出上。如果要将某变数设定成其值,非得靠命令替换的方 式不可。这个例子是将number变数的值加後再存回number变数。 

使用cut命令可以对字符串按照指定的分隔符分开,如
test=`echo $1|cut -d "@" -f1`

就是将变量1的值按照@字符分开,并将它付给test变量

数值测试运算符

-eq等于则为真 

  -ne不等于则为真 

  -gt大于则为真 

  -ge大于等于则为真 

  -lt小于则为真 

-le小于等于则为真

字符串测试运算符

=:等于则为真 

  !=:不相等则为真 

  -z字符串:字符串长度为零则为真 

-n字符串:字符串长度不为零则为真

文件测试运算符

-e文件名:如果文件存在则为真 
  -r文件名:如果文件存在且可读则为真 
  -w文件名:如果文件存在且可写则为真 
  -x文件名:如果文件存在且可执行则为真 
  -s文件名:如果文件存在且至少有一个字符则为真 
  -d文件名:如果文件存在且为目录则为真 
  -f文件名:如果文件存在且为普通文件则为真 
  -c文件名:如果文件存在且为字符型特殊文件则为真 
  -b文件名:如果文件存在且为块特殊文件则为真

Test 指令的用法

test 是用来检测文件类型和比较数值大小的,具体用法如下:

用法:test  <表达式> [表达式] [选项]

返回结果:根据检测的状态返回结果

各种选项返回的结果:

EXPRESSION1 –a  EXPRESSION2   EXPRESSION1EXPRESSION2都为真

         的时候,返回真结果

        EXPRESSION1 -o EXPRESSION2    EXPRESSION1EXPRESSION2有一个

             为真时返回真结果

        -n STRING   当字符串长度不为零是,返回真

-z STRING   当字符串长度为零时,返回真

        STRING1 = STRING2   当两个字符串相等时,返回真

STRING1 != STRING2   当两个字符串不相等时,返回真

        其它的看man 说明文档

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

                    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

一、 关于变量

1$是变量替换操作符

2、变量只有在被声明、赋值、unset或者export及代表一个信号时,才能本来面目出现,即以不带$形式出现,否则都是以$变量名的方式出现;

3””,即一对双引号里的变量是会被替换的,即以变量的值来替换变量的名字。这叫

做部分引用,或叫弱引用。

   而在 中的变量是不会发生替换的,这叫强引用。见面下面的例子

1example1

#! /bin/bash

a="11111111"

echo "this is double qouta:$a"

echo 'this is single qouta:$a'

上面的例子的输出结果如下,注意看两行的输出结果的不一样性。

this is double qouta:11111111

this is single qouta:$a

4、定义一个变量时,使用$变量名和${变量名}在绝部份情况时是一样,但是在某些情况下是不一样的。如在下列这种情况下:

parameter=”aaaaaa

echo $parameterabc           这一句输出的结果是空值

echo ${parameter}abc          这一句则可以输出aaaaaaabc这个字符串

 

5、关于${parameter:-default}和 ${parameter:=default}的用法,见下面的例程:

2example2

#! /bin/bash

1. username1=${testa:-`whoami`}   #因为testa没有定义,所以username1whoami

的结果,但因为使用的是“-”号,所以testa值为空

2. username2=${testa-`whoami`}   结果与上面类似

3. username1=${testa:=`whoami`}  结果与上面类似,但是testa有值了,也是

whoami的结果,注意与第一句相比较

4. username2=${testa=`whoami`}   #与上面类似

5. testa=""

6. username1=${testa:=`whoami`}  4句类似,testa有值

7. username2=${testa=`whoami`}   6句不同的是testa值为空,因为testa已经

设置了,虽然为空

运行后输出的结果如下所示:

The $username1 value is:root

The $username2 value is:root

The $testa value is:

The $username1 value is:root

The $username2 value is:root

The $testa value is:root

The $username1 value is:root

The $username2 value is:root

The $testa value is:root

6${#var} ${#} ${a}

${#var}: 表示变量var 的值的字符长度;如果变量var是数组的话,则是第一个元

素的字符长度;

${#}: 表示调用脚本时,输入的参数的个数;

${@}: 表示调用脚本时,输入的参数的内容,如参数1,参数2等等

        ${#var[@]} :如果var变量是数组的话,则表示数组元素的个数;

${#var[*]}:  :如果var变量是数组的话,则表示数组元素的个数;

    

7echo $hello echo $hello在输出结果上不一样的

a. 当直接用echo $hello时,系统会跟变量中的连续的空格删除,只留一个,同时回

车换行符也删除了;

b. 当使用echo $hello 时,则输出时,变量中的连续空格不会被删除,还是保留;

8、关于变量的变量值的引用,即变量的间接引用

变量可以通过间接的方式来引用,如:

message=Hello

Hello=Goodbye

则 echo ${!message}出来的值就是 Goodbye。因为,message的值是Hello,而

Hello正好又是一个变量,这样相当引用了变量Hello的值

二、 循环

1. for arg in [list]

do

command(s)

done

注意:

 a..在循环每次执行中,arg将顺序的存取list中列出的变量;

 b. list中可以使用 ?等通配符,表示当前目录下的文件,如

for a in *

或 for a in *.sh等等

 c. 如果list 里列出来的不是变量,而是值,则各个值之间用空格分开,如:

for a in 12 34 45 67 89 

 d. 可以在list位置放一个变量,如

b=aaaa bbbbbbbb cccccccc ddddddddd eeee

for a in $b

在循环时分别为aaaa   bbbbbbbb等等

 e. 如果list 省略的话,则使用$@(命令行中传过来的参数列表)替代list 

for a 

do

echo $a

done

f. 可以使用一个命令来替代list :

for a in `du -h`

g. for 循环的结果可以通过管道传递给其它命令,如:

for a in $(find ./ -type f )

do

echo $a

done |sort

2. while [condition]

do

commands

done

注意:

a. while循环可以有多个判断,但是只有最后一个才能决定是否退出循环。

b. while 循环的输入支持管道输入,如:

while read line

do

echo $line

done 

这样可以直接从test.txt文件里读出数据

3. until [ conditions ]

do

command 

done

注意:

a. until 是当条件为假的时候循环的,如果条件为真,则退出循环;

b. 

三、 条件语句

1. case in $var

value1)

Command;

;;

value2)

Command;

;;

Esac

注意:

a. 对变量使用”” 并不是强制的,因为不会发生单词分离;

b. 每句测试行,都以右小括号)结尾;

c. Case块的结束以esac(case的反向拼写)结尾

2Select variable [in list]

  Do

Command ..

Break

  done

四、 关于受限shell

作用:为了限制某些用户或脚本的权限,可以让shell受限模式。

进入退出方法:可以用 set /bin/bash 进入受限模式了。当要退出受限模式,

只需要执行 bash就可以了。

        权限限制:在受限shell如下的操作将会被限制

1、 使用cd 更改工作路径;

2、 更改$PATH $SHELL $BASH_ENV, $ENV等的值;

3、 读取或更改$SHELLOPTS的值;

4、 输出重定向;

5、 调用的命令路径中包括有一个或多个/字符;

6、 在脚本中可能存在的其它脱离受限shell的操作;

实例:

#!/bin/bash -r

echo "Current PATH is:`pwd`"

cd /usr/local

echo "Current PATH is:`pwd`"

echo "Try to change PATH value:"

export PATH=$PATH:"/var"

export SHELL=/sbin/sh

echo $SHELLOPTS

echo "Try to redirect content into:"

echo "aaaaa" >a.txt

执行后的提示是:

Current PATH is:/root/test

./test.sh: line 3: cd: restricted

Current PATH is:/root/test

Try to change PATH value:

./test.sh: line 6: PATH: readonly variable

./test.sh: line 7: SHELL: readonly variable

braceexpand:hashall:interactive-comments

Try to redirect content into:

./test.sh: line 10: a.txt: restricted: cannot redirect output

五、 进程替换

  进程替换是把一个进程的输出回馈给另一个进程。换句话说,它把一个进程的结果发送给另一个进程。

进程替换的一般形式是: >(command) <(command)形式。

利用进程替换可以:

1、 比较两个不同目录之间的内容,如:diff <(ls -l aaa1) <(ls -l aaa2),得出结果:

1,2c1

< ×????? 88

< -rw-r--r--  1 root root    29  1?? 13 17:34 test1.sh

---

> ×????? 72

4d2

< -rw-r--r--  1 root root    40  1?? 13 17:34 test2.txt

2

注意,参考文档

阅读(1808) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~