Shell编程

shell 脚本实质上为文本文件

shell基本语法

执行方式

  • $ sh < program.sh
  • $ sh program.sh
  • $ ./program.sh 需要赋予执行权限

信息流

默认有三个信息流

  • 文件描述符0 stdin -> 键盘
  • 文件描述符1 stdout -> 显示器
  • 文件描述符2 stderr -> 显示器

可以重定向

  • > 输出重定向 如$ cat demo.sh > result.sh 会删除result.sh的原有内容

  • >> 输出重定向 将输出结果追加到文件末尾

  • < 输入重定向 如 sh < demo.sh

  • >& 错误流重定向 不同的shell不同,好像没有错误也会重定向额

管道线处理

  • | 程序输入作为另一个程序输出 如 $ ls -l | wc -l (计算行数)

  • ; 多个命令顺序执行(单线程)

  • & 后台执行命令,关了命令行也会继续执行(在命令行末尾输入)
  • && 左边命令为真时执行右边命令
  • || 左边命令为假时执行右边命令

Shell变量定义

变量名 ^[a-zA-Z_][a-zA-Z0-9_]*

类型 字符串(only)

清除变量unset var,相当于delete

如何调用(引用)变量

在变量名前加$$var

也可以用{}${var}

$转义后不表示引用,如\$var输出$var

三种引号

  • ' ' 单引号,屏蔽任意字符的特殊含义 也就是说,里面的东西全当成字符串,不进行任何处理

  • " " 双引号,屏蔽大部分字符的特殊含义 除了$美元符号,' ' 单引号,\下划线

  • ``反引号,里面的命令可以执行,且只能为命令

变量定义、调用、赋值例子

赋值时不用加$

#!/bin/sh

var=hello

echo '$var='$var       #$var=hello
echo '${var}'=$var     #${var}=hello
echo '"$var"'="$var"   #"$var"=hello
echo '"\$var"'="\$var" #"\$var"=$var

echo 'date is `date`'  #date is `date`
echo "date is `date`"  #date is Tue May 12 11:02:28 CST 2015

name=`I am whoami` #./shelldemo.sh: 1: ./shelldemo.sh: I: not found
echo $name

变量作用域

以默认的变量声明方式,声明的变量,其作用域是局部的,只能在该shell进程中起作用

export var可以将其声明为全局变量

finalize@junjie:/$ var=hello
finalize@junjie:/$ echo $var
hello
finalize@junjie:/$ sh
$ echo $var

$ exit
finalize@junjie:/$ echo $var
hello
finalize@junjie:/$ export var
finalize@junjie:/$ sh
$ echo $var
hello
$ exit
finalize@junjie:/$ echo $var
hello

ShellScript

行首

#!/bin/sh 指定解释器

系统默认内置变量

  • $# 传递到脚本的参数个数
  • $* 以一个单字符串显示所有向脚本传递的参数
  • $@ 与$*相同,但是以多个字符串显示所有向脚本传递的参数,每个字符串为一个参数
  • $$ 脚本运行的当前进程ID号
  • $! 后台运行的最后一个进程的进程ID号
  • $- 显示shell使用的当前选项,与set命令功能相同;
  • $? 显示最后命令的退出状态。0表示没有错误,其他任何值表明有错误;

shelldemo.sh

#!/bin/sh

echo '$# '$#
echo '$* '$*
echo '$$ '$$
echo '$! '$!
echo '$@ '$@
echo '$- '$-
echo '$? '$?

output

finalize@junjie:~$ ./shelldemo.sh hello world
$# 2
$* hello world
$$ 5690
$! 
$@ hello world
$- 
$? 0

条件判断与计算

测试文件状态

语法 test condition or [ condition ](括号两边有空格)

condition:

( 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

STRING equivalent to -n STRING

-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

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

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

-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 read permission is granted

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

-S FILE
      FILE exists and is a socket

-t FD  file descriptor FD is opened on a terminal

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

-w FILE
      FILE exists and write permission is granted

-x FILE
      FILE exists and execute (or search) permission is granted

Except for -h and  -L,  all  FILE-related  tests  dereference  symbolic
links.   Beware  that  parentheses  need  to be escaped (e.g., by back‐
slashes) for shells.  INTEGER may also be -l STRING, which evaluates to
the length of STRING.

括号的使用

#[]中的分号不可少,{}两边空格不可少
[ -f main.c ] && { pwd; ls; rm main.c -f;}

#()两边可以没有空格
[ -f main.c ] && (pwd ;ls ;rm main.c -f)
[ -f main.c ] && {
pwd
ls
rm main.c -f
}

expr计算表达式的值

符号需要用转义字符,如[expr $x > 20= 0 ] && echo ‘$x = ’ $x is less than 20

ARG1 | ARG2
      ARG1 if it is neither null nor 0, otherwise ARG2

ARG1 & ARG2
      ARG1 if neither argument is null or 0, otherwise 0

ARG1 < ARG2
      ARG1 is less than ARG2

ARG1 <= ARG2
      ARG1 is less than or equal to ARG2

ARG1 = ARG2
      ARG1 is equal to ARG2

ARG1 != ARG2
      ARG1 is unequal to ARG2

ARG1 >= ARG2
      ARG1 is greater than or equal to ARG2

ARG1 > ARG2
      ARG1 is greater than ARG2

ARG1 + ARG2
      arithmetic sum of ARG1 and ARG2

ARG1 - ARG2
      arithmetic difference of ARG1 and ARG2

ARG1 * ARG2
      arithmetic product of ARG1 and ARG2

ARG1 / ARG2
      arithmetic quotient of ARG1 divided by ARG2

ARG1 % ARG2
      arithmetic remainder of ARG1 divided by ARG2

STRING : REGEXP
      anchored pattern match of REGEXP in STRING

match STRING REGEXP
      same as STRING : REGEXP

substr STRING POS LENGTH
      substring of STRING, POS counted from 1

index STRING CHARS
      index in STRING where any CHARS is found, or 0

length STRING
      length of STRING

+ TOKEN
      interpret TOKEN as a string, even if it is a

      keyword like 'match' or an operator like '/'

( EXPRESSION )
      value of EXPRESSION

Beware that many operators need to be escaped  or  quoted  for  shells.
Comparisons are arithmetic if both ARGs are numbers, else lexicographi‐
cal.  Pattern matches return the string matched between \(  and  \)  or
null;  if  \( and \) are not used, they return the number of characters
matched or 0.

Exit status is 0 if EXPRESSION is neither null nor 0, 1  if  EXPRESSION
is  null  or  0,  2 if EXPRESSION is syntactically invalid, and 3 if an
error occurred.

流程控制

条件结构 if-then-elif-else-fi

注意if[ condition ]后面有逗号

if [ -d mdir ] ; then
    echo 'this is a dir'
elif [ -f mdir ] ; then
    echo 'this is a regular file'
else
    echo 'unknown file'
fi

case结构

case $1 in
    "start")
        echo "start"
        date
    ;;
    "stop")
        echo "stop"
        date
    ;;
esac

while循环结构

#!/bin/sh

sum=0;
index=0;

while [ $index -le 100 ]
do
    #加号之间一定要有空格,否则就成了字符串了"0+0"
    sum=$(expr $sum + $index)
    index=$(($index+1))
done

echo "1+2+3+...+100="$sum

output

finalize@junjie:~$ ./manexpr
1+2+3+...+100=5050

for循环结构

#!/bin/sh

for file in /home/finalize/*
do
    if [ -d $file ] ; then
        echo $file
    fi
done

output

finalize@junjie:~$ ./manexpr
/home/finalize/Desktop
/home/finalize/Documents
/home/finalize/Downloads
/home/finalize/Music
/home/finalize/Pictures
/home/finalize/Public
/home/finalize/Softwares
/home/finalize/Templates
/home/finalize/Videos
/home/finalize/git
/home/finalize/workspace
finalize@junjie:~$

break,continue,exit

break,continue在while和for中使用。exit终止程序,exit 0 表示程序正常退出

函数

函数声明

funcname(){

}

funcname()
{

}

函数调用与参数传递

  • 调用 funcname
  • 传参 funcname arg1 arg2
  • 获取参数 $0 $1 $2 $* $@ $0表示文件名(相对路径)

shell script追踪与调试

  • n : 不要执行script,仅查询语法错误
  • v : 输出script的源码,遇到输出命令时,则打印到屏幕
  • x : 将使用到的script输出到屏幕上,也就是调用到的其它命令

  • -n noexec If not interactive, read commands but do not execute them. This is useful for checking the syntax of shell scripts.

  • -u nounset Write a message to standard error when attempting to expand a variable that is not set, and if the shell is not interactive, exit immediately.

  • -v verbose The shell writes its input to standard error as it is read. Useful for debugging.

  • -x xtrace Write each command to standard error (preceded by a ‘+ ’) before it is executed. Useful for debuging.

finalize@junjie:~$ cat shdemo 
#!/bin/sh

echo `date`)
echo `whoami`
echo `pwd`

echo "haha"

finalize@junjie:~$ sh -n shdemo
shdemo: 3: shdemo: Syntax error: ")" unexpected

finalize@junjie:~$ sh -v shdemo
#!/bin/sh

echo `date`
Tue May 12 16:04:44 CST 2015
echo `whoami`
finalize
echo `pwd`
/home/finalize

echo "haha"
haha

finalize@junjie:~$ sh -x shdemo
+ date
+ echo Tue May 12 16:05:15 CST 2015
Tue May 12 16:05:15 CST 2015
+ whoami
+ echo finalize
finalize
+ pwd
+ echo /home/finalize
/home/finalize
+ echo haha
haha

finalize@junjie:~$

对于有循环的script,使用sh -v 时

finalize@junjie:~$ cat manexpr 
#!/bin/sh

sum=0;

add(){
    sum=$(($1+$2))

}

echo "xixi"

for i in `seq 100`
do 
    add $sum $i
done

echo $sum

finalize@junjie:~$ sh -v manexpr
#!/bin/sh

sum=0;

add(){
    sum=$(($1+$2))

}

echo "xixi"
xixi

for i in `seq 100`
do 
    add $sum $i
done

echo $sum
5050
finalize@junjie:~$