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是否相等