Shell 编程 0 – 基本语法

1.1 shell提示符及其环境

提示符:$

环境:Linux,Unix,Dos等等

Shell是一个解释执行命令的程序,所谓shell编程其实就是用一定的语法将各种基本的命令组合起来,让shell程序去解释执行。

同传统的编程语言一样,shell提供了很多特性,这些特性可以使你的shell script编程更为有用,如:数据变量、参数传递、判断、流程控制、数据输入和输出,子程序及以中断处理等。

1.2 如何执行shell程序

Shell程序(***.sh)

执行shell三种方法:

(1) #./***.sh

(2) #chmod u+x ***.sh

#***.sh

(3) #sh ***.sh

1.3 常量

字符串,例如“Hello World!”。

数字,例如705,23。

1.4 变量

shell中变量默认没有类型,简称弱类型,类似于PHP、Javascript、Swift中的变量,无类型声明

STR="Hello World!"

echo $STR

注意:赋值不能有空格

HOME="/home"

a=123

HOME_LIST=$(ls /home) //把命令的执行结果赋值给变量

ehco $HOME //$ 符号是获得变量HOME的值

let a=a+1

 

系统变量:
$0       这个程式的执行名字
$n       这个程式的第n个参数值,n=1..9
$*       这个程式的所有参数,此选项参数可超过9个。
$#       这个程式的参数个数
$$       这个程式的PID(脚本运行的当前进程ID号)
$!       执行上一个背景指令的PID(后台运行的最后一个进程的进程ID号)
$?       执行上一个指令的返回值 (显示最后命令的退出状态。0表示没有错误,其他任何值表明有错误)
$-       显示shell使用的当前选项,与set命令功能相同
$@       跟$*类似,但是可以当作数组用

1.5 局部变量

局部变量一般是指写在函数中的变量,生命力是有限的

1.6 从键盘输入变量值

使用read命令

read var1 var2 … varn

1.7 注释

Shell编程中的注释以#开头

1.8 数字运算

主要是指整数运算。expr命令可以将字符型变量转换为整数进行操作

语法:expr integer operator integer

其中operator为+ - * / %, 但对*的使用要用转义符/,如:

#!/bin/bash

expr 5 /* 5

Bash shell 的算术运算有四种方式:
使用 expr 外部程式

加法 r=`expr 4 + 5`
注意! '4' '+' '5' 这三者之间要有空白
r=`expr 4 * 5` #错误
乘法 r=`expr 4 \* 5`

使用 $(( ))
r=$(( 4 + 5 ))

使用 $[ ]

r=$[ 4 + 5 ]

乘法
r=`expr 4 \* 5` 
r=$(( 4 * 5 ))
r=$[ 4 * 5 ]

除法
r=`expr 40 / 5` 
r=$(( 40 / 5 ))
r=$[ 40 / 5 ]

减法
r=`expr 40 - 5` 
r=$(( 40 - 5 ))
r=$[ 40 - 5 ]

求余数
r=$[ 100 % 43 ]

乘幂 (如 2 的 3 次方)
r=$(( 2 ** 3 ))
r=$[ 2 ** 3 ]
注:expr 沒有乘幂

使用let 命令
加法:
n=10
let n=n+1
echo $n #n=11

乘法:
let m=n*10

除法:
let r=m/10
求余数:
let r=m%7
乘冪:
let r=m**2

虽然Bash shell 有四种算术运算方法,但并不是每一种都是跨平台的,建议使用expr。
另外,我们在 script 中经常有加1操作,以下四法皆可:
m=$[ m + 1]
m=`expr $m + 1`
m=$(($m + 1))
let m=m+1

PS:shell的内部算术运算符无法处理浮点数,所以当需要处理浮点数是,要用到外部工具(如awk)

1.9 逻辑运算

test进行逻辑运算,用[ ]括起来就是test运算

int1 -eq int2       相等?

int1 -ne int2       不等?

int1 -gt int2       int1 > int2 ?

int1 -ge int2       int1 >= int2 ?

int1 -lt int2       int1 < int2 ?

int1 -le int2       int1 <= int2

1.10 双引号及单引号

$echo "$HOME $PATH"  -- 显示变量值

/home/hbwork opt/kde/bin:/usr/local/bin:

$echo '$HOME $PATH'  -- 显示单引号里的内容

$HOME $PATH

1.11 空格

由于shell对命令中的多余的空格不作任何处理 ,而用引号括起来则可以防止shell去掉这些空格。

$ str1=abcd

$ str2="abcd      "

1.12        分支结构

1.12.1 结构一

if [ variable = value ]

then

command

else

if [ variable = value ]

then

command

else

command

fi

1.12.2 结构二

if [ variable = value ]
then

command

elif [ variable = value ]

then

command

fi

if 语句示例:

if [ "22" -lt "33" ] then   //注意空格

echo "22 less than 33"

else

echo "no"

fi //if语句的结束

注意:使用i f语句时,必须将then部分放在新行,否则会产生错误。如果要不分行,必须使用命令分隔符。现在简单i f语句变为:

if 条件;  then

command

fi

1.13 Case语句

case value in

pattern1)

command

;;

pattern2)

command

;;

...

patternn)

command;

esac

case 示例:

echo "enter a number"

read ans                    //读取一个变量read

case $ans in

1)

echo   "you numer is $ans"

;;                                //注意符号是两个 ;

2)

echo "you number is 2"

;;

[3-9])

echo "you number is $ans"

;;

*)                                   //*通配符

echo "others"

esac

1.14 And 和 Or

command1 && command2

command1 || command2

1.15 循环语句

1.15.1 For循环

for var in arg1 arg2 ... argn

do

command

....

command

done

For循环示例

int=1

for $int in 1 2 3 4 5

do

sq=`expr $int /* $int`

echo $sq

int=`expr $int + 1`

done

1.15.2 while循环

while command

do

command

command

command

...

done

While循环的示例

int=1

while [ $int -le 5 ]

do

sq=`expr $int /* $int`

echo $sq

int=`expr $int + 1`

done

1.15.3 until循环结构

until command

do

command

command

....

command

done

Until循环示例

int=1

until [ $int -gt 5 ]

do

sq=`expr $int /* $int`

echo $sq

int=`expr $int + 1`

done

1.16 从循环中退出: break和continue命令

break 立即退出循环

continue 忽略本循环中的其他命令,继续下一下循环

1.17 函数 (子过程)

[ function ] funname [()]

{

command;

[return int;]

}

1、函数可以带 function fun() 定义,也可以直接fun() 定义,不带任何参数。
2、函数参数返回,可以显示加:return 返回,如果不加,将以最后一条命令运行结果,作为返回值。
return后跟数值n(0-255
3、必须在调用函数地方之前,声明函数,shell脚本是逐行运行。不会像其它语言一样先预编译。因此必须在使用函数前先声明函数。
4、returnvalue=$(funname 3 2); 在shell 中 单括号里面是命令语句。 因此,可以将shell中函数看作是定义一个新的命令,因此各个输入参数直接用空格分隔。 命令里面获得参数方法可以通过:$0…$n得到。 $0代表函数本身。
5、函数返回值,只能通过$? 系统变量获得,直接通过 = 获得是空值。其实,函数是一个命令,在shell获得命令返回值,都需要通过$?获得。
6、如果需要传出其它类型函数值,可以在函数调用之前,定义变量(这个就是全局变量)。在函数内部可以直接修改,然后在执行函数就可以读出修改过的值。
7、如果需要定义自己变量,可以在函数中定义:local 变量=值 ,这时变量就是内部变量,它的修改,不会影响函数外部相同变量的值 。

1.18  shell程序的调试

使用-x进行跟踪执行,执行并显示每一条指令。

1.19 比较运算符

1.19.1 文件比较运算符

-e filename         如果 filename 存在,则为真                  [ -e /var/log/syslog ]

-d filename         如果 filename 为目录,则为真             [ -d /tmp/mydir ]

-f filename          如果 filename 为常规文件,则为真   [ -f /usr/bin/grep ]

-L filename         如果 filename 为符号链接,则为真   [ -L /usr/bin/grep ]

-r filename          如果 filename 可读,则为真                  [ -r /var/log/syslog ]

-w filename        如果 filename 可写,则为真                  [ -w /var/mytmp.txt ]

-x filename         如果 filename 可执行,则为真             [ -L /usr/bin/grep ]

-b filename         如果 filename 块文件,则为真

-c filename         如果 filename 字符文件,则为真

-p filename         如果 filename 管道,则为真

-s filename         如果 filename 大于0,则为真

filename1 -nt filename2 如果 filename1 比 filename2 新,则为真

filename1 -ot filename2 如果 filename1 比 filename2 旧,则为真

 

1.19.2 字符串比较运算符 (请注意引号的使用,这是防止空格扰乱代码的好方法)

-z string               如果 string 长度为零,则为真         [ -z "$myvar" ]

-n string              如果 string 长度非零,则为真         [ -n "$myvar" ]

string1 = string2                如果 string1 与 string2 相同,则为真 [ "$myvar" = "one two three" ]

string1 != string2               如果 string1 与 string2 不同,则为真 [ "$myvar" != "one two three" ]

string1 < string2               String1 的ASCII在string2前

 

1.19.3 算术比较运算符

num1 -eq num2         等于

num1 -ne num2         不等于

num1 -lt num2         小于

num1 -le num2         小于或等于

num1 -gt num2         大于

num1 -ge num2         大于或等于

1.20 正则表达式

正则表达式(regular expression)描述了一种字符串匹配的模式,可以用来检查一个串是否含有某种子串、将匹配的子串做替换或者从某个串中取出符合某个条件的子串等。

grep, expr, sed , awk. 或Vi中经常会使用到正则表达式,为了充分发挥 shell 编程的威力,需要精通正则表达式。

如:使用shell时,从一个文件中抽取多于一个字符串将会很麻烦。例如,在一个文本中抽取一个词,它的头两个字符是大写的,后面紧跟四个数字。如果不使用某种正则表达式,在shell中将不能实现这个操作。

一个正则表达式包含下面一个或多个项:

1.一个字符集

这里的字符集里的字符表示的就是它们字面上的意思.正则表达式最简单的情况就是仅仅由字符集组成,而没有其他的元字符。

2.锚

一个锚指明了正则表达式在一行文本中要匹配的位置,例如^和$就是锚。

3.修饰符

它们用于展开或缩小(即是修改了)正则表达式匹配文本行的范围.修饰符包括了星号、括号和反斜杠符号。

*         匹配重复零次或多次前一字符

+         匹配一个或多个前面的字符.它的作用和*很相似,但唯一的区别是它不匹配零个字 符的情况

?         匹配零或一个前面的字符。它一般用于匹配单个字符

.         匹配任意字符( 除换行符 )

^         匹配一行的开头,但依赖于上下文环境,可能在正则表达式中表示否定一个字符 集的意思
[...]         匹配集合中任意字符 如"[xyz]" 匹配字符 x, y, 或z
[^...]         匹配不属集合 中 任意字符
^, $         匹配 行首、行尾
\<, \>         用于表示单词的边界。\< 匹配词首,\>词尾,如"\<the\>" 匹配单词"the"
...         正则表达式分组。进行子字符串提取(substring extraction)一起使用很有用
\n         第 n 个分组内容

\         转义(escapes) 一个特殊的字符,使这个字符表示原来字面上的意思。"\$"表示 了原来的字面意思"$",而不是在正则表达式中表达的匹配行尾的意思."\\"也被 解释成了字面上的意思"\"

\{ \}         指示前面正则表达式匹配的次数.

要转义是因为不转义的话大括号只是表示他们字面上的意思.这个用法只是技巧上 的而不是基本正则表达式的内容."[0-9]\{5\}" 精确匹配5个数字(从0到9的数字).

| "或"         正则操作符用于匹配一组可选的字符

{n}         n是一个非负整数。匹配确定的n次。例如,''o{2}'' 不能匹配 "Bob"中的''o'',但是能匹配"food" 中的两个o。

{n,}         n是一个非负整数。至少匹配n次。例如,''o{2,}'' 不能匹配"Bob"中的'o'',但能匹配 "foooood"中的所有o。''o{1,}''等价于''o+''。''o{0,}''则等价于''o*''。

{n,m}         m和n均为非负整数,其中n<=m。最少匹配n次且最多匹配m次。例如,"o{1,3}"将匹配 "fooooood"中的前三个o。''o{0,1}''等价于''o?''。请注意在逗号和两个数之间不能有空格。

\b         匹配一个单词边界,也就是指单词和空格间的位置。例如,''er\b'' 可以匹配"never" 中的''er'',但不能匹配 "verb"中的 ''er''。

\B         匹配非单词边界。''er\B''能匹配"verb"中的''er'',但不能匹配"never"中的 ''er''

\w         匹配包括下划线的任何单词字符。等价于''[A-Za-z0-9_]''。

\W         匹配任何非单词字符。等价于''[^A-Za-z0-9_]''。

\d         匹配一个数字字符。等价于[0-9]。

\D         匹配一个非数字字符。等价于[^0-9]。

\f         匹配一个换页符。等价于\x0c和\cL。

\n         匹配一个换行符。等价于\x0a和\cJ。

\r         匹配一个回车符。等价于\x0d和\cM。

\s         匹配任何空白字符,包括空格、制表符、换页符等等。等价于[\f\n\r\t\v]。

\S         匹配任何非空白字符。等价于[^\f\n\r\t\v]。

\t         匹配一个制表符。等价于\x09 和 \cI。

\v         匹配一个垂直制表符。等价于\x0b和\cK。

例子:

/\b([a-z]+)\1\b/gi         一个单词连续出现的位置

/(\w+):\/\/([^/:]+)(:\d*)?([^# ]*)/         将一个URL解析为协议、域、端口及相对路径

/^(?:Chapter|Section) [1-9][0-9]{0,1}$/         定位章节的位置

/[-a-z]/         A至z共26个字母再加一个-号。

/ter\b/         可匹配chapter,而不能terminal

/\Bapt/         可匹配chapter,而不能aptitude

/Windows(?=95 |98 |NT )/         可匹配Windows95或Windows98或WindowsNT,当找到一个匹配后,从Windows后面开始进行下一次的检索匹配。

高阶例子:

vi 中使用:

s/.∗$\n^.*$/\1/g      将两行(如行1和行2 )内容替换为第一行内容(即行1的内容)

%s/.∗$\n^.*$/\1/g 删除偶数行

%s/^.*$\n.∗$/\1/g 删除奇数行

其它:

sed 's/..$/\2/'  删除该行除最后一个字符外的所有字符,保留最后一个字符,也即取得该行最后一个字符。

 

 Shell 的一些注意事项

1. 基础

#!/bin/bash   //bash脚本第一句都是这个,他会让系统指定以bash来解释这个脚本

#                 //shell脚本注释符号

2. 变量

变量赋值的时候"="两边的内容要紧靠"="

3. 在使用if、while时,注意变量与符号之间的分割

例如:if [ `expr $a % 3` -ne 0 ],while [ "$var" != "end" ],尽量分开写,这样也挺好看也便于识别。

4.  let的意思是让"="后面的内容做算术运算,例如:let x=a-b

5. 大多数情况下,可以使用测试命令来对条件进行测试

比如可以比较字符串、判断文件是否存在及是否可读等等……通常用" [ ] "来表示条件测试,注意这里的空格很重要,要确保方括号前后的空格

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

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

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

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


Comments are closed.