Shell编程基础
在计算机科学中,Shell俗称壳(用来区别于核),是指“为使用者提供操作界面”的软件,这里我们是指shell脚本基础编程。
1.概述
我们通过cat /etc/shells可以查看系统中存在的shell程序:
$ cat /etc/shells
# /etc/shells: valid login shells /bin/sh /bin/bash /usr/bin/bash /bin/rbash /usr/bin/rbash /usr/bin/sh /bin/dash /usr/bin/dash
一般Redhat系默认使用的是bash,Debian系默认使用的是dash。我们可以通过env命令或者printenv命令查看全局变量,使用set命令可查看当前shell的变量。
下面是一个简单的shell脚本文件,hello.sh(编写脚本文件一般都加后缀名".sh",当然不加也可以用,最好是加后缀,增加辨识性):
1 #!/bin/bash 2 3 echo "hello world."
其中"#!"叫做shebang,第一行表示使用"/bin/bash"运行此文本,当然也可以用其它的shell程序,比如dash。运行此脚本文件,会输出"hello world."。
2.执行方式
执行脚本文件的常见方式有4种:
1).指定shell程序
# 使用相对路径 $ bash ./hello.sh # 或者使用绝对路径 $ bash /home/hello.sh
2).增加执行权限
# 增加可执行权限 $ chmod +x hello.sh # 使用相对路径执行 $ ./hello.sh # 或者,使用绝对路径执行 $ /home/hello.sh
3).使用"."执行
# 此例忽略可执行权限有无 $ . hello.sh # 或者绝对路径 $ . /home/hello.sh
4).使用"source"执行
# 此例忽略可执行权限有无 $ source hello.sh # 或者绝对路径 $ source /home/hello.sh
其中1)、2)是在创建的子shell中执行,3)、4)是在当前shell中直接执行。
3.变量
3.1 系统预定义变量
1). $HOME、$PWD、$SHELL、$USER,查看变量:例echo $HOME;
2). 显示当前shell中所有变量 set
3). 显示全局变量: env或printenv
3.2 自定义变量
一、基本语法:
1).定义变量
变量名=变量
2).撤销变量
unset 变量名
3).声明静态变量
readonly 变量,注意:不能unset
4).声明全局变量
export 变量,子shell中修改变量值不会影响当前shell中的值.
二、变量定义规则:
1).变量名称可以有字母、数字和下划线组成,但是不能以数字开头,环境变量名建议大写;
2).等号两侧不能有空格;
3).在Bash中,变量默认类型都是字符串型,无法直接进行数值运算;
4).变量的值如果有空格,需要使用双引号或单引号括起来.
3.3 特殊变量
$n
n为数字,$0代表脚本本身,$1-$9代表第1-9个参数,10以上需用大括号,如${10};
$#
获取所有输入参数的个数;
$*、$@
$*,代表命令行中所有的参数,并把所有参数看成一个整体;
$@,代表命令行中所有的参数,并每个参数区分对待;
$?
最后一次执行命令的返回状态,0代表正确执行,非0代表错误执行.
3.4 读取数据
read (选项) (参数)
1).选项
-p: 指定读取值时的提示符;
-t: 指定读取值时的等待时间(秒),如果不加此项,表示一直等待;
2).参数
变量: 指定读取值的变量名;
例:
1 #!/bin/bash 2 3 read -t 10 -p "Input number:" num 4 echo $num
4.运算符
$((运算式))或$[运算式]
例:
1 a=$[(1 + 2) * 3], 2 a=$(expr 4 \* 5), 3 a=`expr 6 + 7` 4 a=$((5 + 1)) 5 for ((i = 0; i < 10; i++)) 6 while [ $i -le 100 ] 7 while (($i < 100))
5.条件判断
一、基本语法:
test condition或[ condition ],二者等价,判断condition是否为真,注意condition前后都有空格;
二、常用判断条件
1).整数之间比较
-eq 等于(equal)
-ne 不等于(not equal)
-lt 小于(less than)
-le 小于等于(less equal)
-gt 大于(greater than)
-ge 大于等于(greater equal)
注:如果是字符串之间的比较,使用"="判断相等,使用"!="判断不相等;
2).文件权限判断
-r 有读权限(read)
-w 有写权限(write)
-x 有执行权限(execute)
3).文件类型判断
-e 文件存在(existence)
-f 文件存在并且是一个常规文件(file)
-d 文件存在并且是一个目录(directory)
4).多条件判断
&& 表示前一条命令成功后,再执行后一条命令;
|| 表示前一条命令失败后,才执行后一条命令;
-a and,逻辑与;
-o or,逻辑或;
6.流程控制
6.1 if
1).单分支
1 if [ 条件表达式 ]; then 2 程序 3 fi
或
1 if [ 条件表达式 ] 2 then 3 程序 4 fi
2).多分支
1 if [ 条件表达式 ]; then 2 程序 3 elif [ 条件表达式 ]; then 4 程序 5 else 6 程序 7 fi
例(注意判断式中括号中的空格):
1 #!/bin/bash 2 3 i=9 4 5 if [ $i -lt 3 ]; then 6 echo "Not OK" 7 elif [ $i -le 6 ]; then 8 echo "Normal" 9 else 10 echo "Good" 11 fi
6.2 case
1).写法
1 case $变量名 in 2 "值1") 3 如果变量值等于值1,则执行程序1 4 ;; 5 "值2") 6 如果变量值等于值2,则执行程序2 7 ;; 8 ...省略其它分支... 9 *) 10 如果变量值不等于上面任一值,则执行此程序 11 ;; 12 esac
2).注意事项
case行尾必须为单词"in",每个分支匹配必须以")"结束;
";;"表示命令序列结束;
最后的"*)",表示默认模式.
例:
1 #!/bin/bash 2 3 case $1 in 4 1) 5 echo "one" 6 ;; 7 2) 8 echo "two" 9 ;; 10 3) 11 echo "three" 12 ;; 13 *) 14 echo "other" 15 ;; 16 esac
6.3 for
1).基本语法1:
1 for ((初始值; 循环控制条件; 变量变化)) 2 do 3 程序 4 done
例:
1 #!/bin/bash 2 3 sum=0 4 5 for ((i = 1; i <= 100; i++)) 6 do 7 sum=$[$sum + $i] 8 done 9 10 echo $sum
2).基本语法2:
1 for 变量 in 值1 值2 值3... 2 do 3 程序 4 done
例1:
1 #!/bin/bash 2 3 sum=0 4 5 for i in {1..100} 6 do 7 sum=$[$sum + $i] 8 done 9 10 echo $sum
例2:
1 #!/bin/bash 2 3 for i in 1 2 3 4 4 do 5 echo $i 6 done
6.4 while
(注意判断式中括号中的空格)
1 while [ 条件表达式 ] 2 do 3 程序 4 done
例1:
1 #!/bin/bash 2 3 i=1 4 sum=0 5 6 while [ $i -le 100 ] 7 do 8 sum=$[ $sum + $i ] 9 i=$[ $i + 1 ] 10 done 11 12 echo $sum
例2:
1 #!/bin/bash 2 3 i=1 4 sum=0 5 6 while [ $i -le 100 ] 7 do 8 let sum+=i 9 let i++ 10 done 11 12 echo $sum
7.函数
7.1 系统函数
1).basename
截取字符串最后一级名称。
basename [string/pathname] [suffix]
string/pathname: 字符串/路径名;
suffix: 后缀名,如果指定此项则会去除字符串与之相同的后缀名。
例:
$ basename /home/hello.sh .sh hello
2).dirname
截取字符串的路径名称。
dirname [string/pathname]
string/pathname: 字符串/路径名;
例:
$ dirname /home/hello.sh /home
3).expr
数学计算。
expr [数学式]
例:
$ expr 7 + 8 15
7.2 自定义函数
[function] funname[()] { Action; [return int;] }
上述总"[]"表示里面内容可省略不写。函数应在调用之前声明。
function: 声明函数,可以省略,后面的括号相同;
funname: 函数名称;
return: 显式加上返回return值,不显示加,返回最后一条命令执行的情况。
例:
1 #!/bin/bash 2 3 function add() 4 { 5 s=0 6 s=$[$1 + $2] 7 echo $s 8 } 9 10 read -p "Input 2 number:" a b 11 12 sum=0 13 sum=$(add $a $b) 14 15 echo "a + b = "$sum
8.正则表达式
正则表达式使用单个字符串来描述、匹配一系列符合某个语法规则的字符串。
在很多文本编辑器里,正则表达式通常被用来检索、替换哪些符合某个模式的文本。
在Linux中,grep、sed、awk等文本处理工具都支持通过正则表达式进行模式匹配。
8.1 常规匹配
$ cat /etc/shells | grep bash
匹配所有包含bash的行。
8.2 常用特殊字符
1).特殊字符"^"
$ cat /etc/shells | grep ^b
匹配所有以b开头的行。
2).特殊字符"$"
$ cat /etc/shells | grep h$
匹配所有以h结尾的行。
3).特殊字符"."
$ cat /etc/shells | grep b..h
匹配所有bxxh的行,其中xx表示两个任意字符。
4).特殊字符"*"
$ cat /etc/shells | grep b*sh
匹配所有b*sh的行,其中*代表任意多个相同字符。
5).字符区间"[]"
匹配某个范围内的一个字符。
[7,9] ---- 匹配7或9;
[0-9] ---- 匹配0-9中的任意1个数字;
[0-9]* ---- 匹配任意长度的数字字符串;
[a-z] ---- 匹配a-z中的任意1个字母;
[a-z]* ---- 匹配任意长度的a-z中字符的字符串;
[a-c,e-h] ---- 匹配a-c或e-h之间任意1个字符;
$ cat /etc/shells | grep [b,d]ash
匹配包含bash或dash的字符串。
6).转义字符"\"
当我们需要匹配特殊字符时,例如$,就需要使用转义字符,"\$",这样就可以正常匹配$了。
$ echo "adfvda$hwegwethk" | grep 'a\$h'
匹配包含a$h的字符串。
7).正则表达式扩展
例(匹配中国11位手机号):
$ echo "18912345678" | grep -E ^1[3456789][0-9]{9}$
"{9}"代表匹配9次[0-9].
9.文本处理
9.1 cut
负责剪切文件中的数据。
cut [选项参数] filename
选项参数:
-f ---- 列号,提取第几列
-d ---- 分隔符,按照指定的分隔符分隔列,默认是制表符"\t"
-c ---- 按字符进行切割,后加n代表取第几列,如-c 2
例:
$ cut -d " " -f 1 hello.sh
9.2 awk
把文件逐行读入,以空格为默认分隔符将每行切片,切开的部分再进行分析处理。
awk [选项参数] '/pattern1/{action1} /pattern2/{action2}...' filename
pattern ---- 表示awk在数据中查找的内容,就是匹配模式;
action ---- 在找到匹配内容时所执行的一系列命令;
选项参数
-F ---- 指定输入文件分隔符;
-v ---- 赋值一个用户定义变量;
例:
$ cat /etc/passwd | awk -F : '/^root/{print $7}'