shell 编程基础
变量类型
Shell中按照变量的作用域和生命周期,Shell变量可分为四大类:
(1)永久环境变量:需要修改配置文件,变量永久生效。
(2)临时环境变量:使用export命令行声明即可,变量在Shell脚本进程结束后仍然有效,但在关闭当前Shell会话后失效。
(3)全局变量:在脚本中定义,仅在当前Shell脚本中有效,其他Shell脚本进程不能访问,其作用域从定义的位置开始,到脚本结束或被显示删除的地方为止。注意,全局变量既可以在Shell函数内定义,也可以在Shell函数外定义,因为Shell函数内定义的变量默认为global,且作用域从“函数被调用时执行变量定义的地方”开始,到脚本结束或被显示删除的地方为止。
#!/bin/bash global globalVar=dablel #全局变量
(4)局部变量。在Shell脚本中函数内显示使用local关键字定义的变量。其作用域局限于函数内。同名local变量会屏蔽global变量。
#!/bin/bash functiontest() { local localVar=dablel #局部变量 } test echo ${localVar} #输出为空
定义变量
Shell 支持以下三种定义变量的方式:
var=value var='value' var="value" #举便如下: name='dablelv' home="安徽" age=28
var是变量名,value是赋给变量的值。如果value不包含任何空白符(例如空格、Tab等),那么可以不使用引号;如果value 包含了空白符,那么就必须使用引号包围起来。使用单引号和使用双引号也是有区别的,稍后我们会详细说明。注意,赋值号的周围不能有空格,这可能和你熟悉的大部分编程语言都不一样。
Shell变量的命名规范和大部分编程语言都一样:
(1)变量名由数字、字母、下划线组成;
(2)必须以字母或者下划线开头;
(3)不能使用Shell里的关键字(通过help命令可以查看保留关键字)。
访问变量
使用一个定义过的变量,只要在变量名前面加美元符号$即可,如:
name="dablelv" echo $name echo ${name}
变量名外面的花括号{}是可选的,加不加都行,加花括号是为了帮助解释器识别变量的边界,比如下面这种情况:
skill="Java" echo"I am good at ${skill}Script"
如果不给skill变量加花括号,解释器就会把$skillScript当成一个变量(其值为空),代码执行结果就不是我们期望的样子了。
推荐给所有变量加上花括号{ },这是个良好的编程习惯
单引号和双引号的区别
定义变量时,变量的值可以由单引号包围,也可以由双引号包围,它们到底有什么区别呢?
#!/bin/bash name='dablelv' str1='我的名字是${name}' str2="我的名字是${name}" echo $str1 echo $str2
运行结果:
我的名字是${name} 我的名字是dablelv
以单引号包围变量的值时,单引号里面是什么就输出什么,即使内容中有变量和命令(命令需要反引起来)也会把它们原样输出。这种方式比较适合定义显示纯字符串的情况,即不希望解析变量、命令等的场景。
以双引号包围变量的值时,输出时会先解析里面的变量和命令,而不是把双引号中的变量名和命令原样输出。这种方式比较适合字符串中附带有变量和命令并且想将其解析后再输出的变量定义。
建议:如果变量的内容是数字,那么可以不加引号;如果真的需要原样输出就加单引号;其他没有特别要求的字符串等最好都加上双引号,定义变量时加双引号是最常见的使用场景。
将命令的结果赋值给变量
Shell 也支持将命令的执行结果赋值给变量,常见的有以下两种方式:
variable=`command` variable=$(command)
第一种方式把命令用反引号包围起来,反引号和单引号非常相似,容易产生混淆,所以不推荐使用这种方式;第二种方式把命令用$()包围起来,区分更加明显,所以推荐使用这种方式。
例如将两个数值相加赋给某一变量。
var1=1 var2=2 varAdd1=`expr $var1 + $var2` varAdd2=$(expr $var1 + $var2)
只读变量
使用readonly命令,或者使用declare -r或typeset -r可以将变量定义为只读变量,只读变量的值不能被改变。
name="dablelv" readonly name declare -r name="dablelv" typeset -r name="dablelv"
删除变量
使用unset内置命令可以删除变量。语法:
unset variable_name
unset命令不能删除只读变量。变量被删除后不能再次使用。如
#!/bin/bash name="dablelv" unset name echo $name
上面的脚本没有任何输出。
系统变量
$0:当前程序的名称;
$n:当前系统的第n个参数,n=1,2,3,4,5,6,7,8,9
$*:当前参数的所有参数(不含程序本身)
$# 当前程序的参数个数(不包括程序本身)
$?:命令或者程序执行后的状态,返回0表示成功;这里跟我们常识感觉可能不一样,因为我们一般以0为无,没有的意思;通常认为0表示失败,这里恰恰相反,大家注意;
$UID:当前用户的ID;
$PWD:当前所在的目录;(这里的PWD不是密码的意思,不是表示password,而是print working directory,一般英语大写缩写都是多个单词的缩写,这里注意,容易误解);
变量名 |
函数 |
$FUNCNAME |
函数名称 |
$RANDOM |
随机数 |
${BASH_REMATCH[@]} |
匹配正则表达式的列表 |
$0 |
脚本名称 |
$1~$n |
脚本位置参数,10及以上表示为${10},shift进行偏移获取参数值 |
$@ |
所有位置参数(列表),无论是否加上双引号,每个位置的参数都被当作一个独立的单词来处理 |
$* |
所有位置参数(字符串),使用 $* 时,如果加上双引号,即 "$* " 的形式,那所有位置的参数会被当作一个单词来处理,如果不包含双引号,即 $* 的形式,则每个位置的参数都被当作一个独立的单词来处理 |
$# |
位置参数的个数 |
$_ |
上一条命令最后一个参数 |
$? |
上条命令的返回值,获取执行上一个指令的执行状态返回值(0为成功,非零为失败) |
$$ |
脚本的PID |
$PPID |
父进程ID |
$LINENO |
脚本执行的行号 |
$PWD |
当前目录 |
$SECONDS |
脚本已经运行的时间(s) |
$TMOUT |
超时退出时间(s) |
$UID |
当前用户ID |
$HOME |
用户home目录 |
$GROUPS |
当前用户组ID |
$IFS |
此变量用于 Bash 识别字符串或单词边界,默认值是空格,脚本中根据需要可以修改此变量的值 当 $IFS 为 : 时,字符串 "a:b:c"被解析成 a b c 当 $IFS 为 - 时,字符串 "x-y-z"被解析成 x y z 当 $IFS 为 , 时,字符串 "e,f,g"被解析成 e f g |
现有#!/bin/bash cnt=1 echo 'test 1111' for var in "$*" do echo "arg$cnt="$var let "cnt+=1" done echo cnt=1 echo 'test 2222' for var in $* do echo "arg$cnt="$var let "cnt+=1" done echo cnt=1 echo 'test 3333' for var in "$@" do echo "arg$cnt="$var let "cnt+=1" done echo cnt=1 echo "test 4444" for var in $@ do echo "arg$cnt="$var let "cnt+=1" done
执行 ./c.sh 1 2 3,结果如下:
[root@ecs-centos-7 ~]# ./c.sh 1 2 3 test 1111 arg1=1 2 3 test 2222 arg1=1 arg2=2 arg3=3 test 3333 arg1=1 arg2=2 arg3=3 test 4444 arg1=1 arg2=2 arg3=3
从上面的结果可以看出,对于 $* 来说,加了双引号之后所有位置参数就会被视为一个单词
对于 $@ 来说,是否加双引号,结果都是一样的
所以,仅在使用双引号时,$*和 $@ 才会有差异
执行结果输入输出
command > file 将输出重定向到 file。 command < file 将输入重定向到 file。 command >> file 将输出以追加的方式重定向到 file。 n > file 将文件描述符为 n 的文件重定向到 file。 n >> file 将文件描述符为 n 的文件以追加的方式重定向到 file。 n >& m 将输出文件 m 和 n 合并。 n <& m 将输入文件 m 和 n 合并。 << tag 将开始标记 tag 和结束标记 tag 之间的内容作为输入。 $ command > /dev/null /dev/null 是一个特殊的文件,写入到它的内容都会被丢弃;如果尝试从该文件读取内容,那么什么也读不到。但是 /dev/null 文件非常有用,将命令的输出重定向到它,会起到"禁止输出"的效果。 如果希望屏蔽 stdout 和 $ command > /dev/null 2>&1
二元比较
-eq 等于
= 等于
== 等于
-ne 不等于
!= 不等于
-lt 小于
< 小于 (ASCII) *
-le 小于等于
-gt 大于
> 大于 (ASCII) *
-ge 大于等于
-z 字符串为空
-n 字符串不为空
> 大于
>= 大于等于
< 小于
<= 小于等于
文件测试类型
-e 文件是否存在
-s 文件大小不为0
-f 是一个标准文件
-d 是一个目录
-r 文件具有读权限
-w 文件具有写权限
-x 文件具有执行权限
-h 文件是一个符号链接
-L 文件是一个符号链接
-b 文件是一个块设备
-c 文件是一个字符设备
-g 设置了sgid标记
-p 文件是一个管道
-u 设置了suid标记
-S 文件是一个socket
-k 设置了"粘贴位"
-t 文件与一个终端相关联
-N 从这个文件最后一次被读取之后, 它被修改过
-O 这个文件的宿主是你
-G 文件的组id与你所属的组相同
F1 -nt F2 文件F1比文件F2新 *
F1 -ot F2 文件F1比文件F2旧 *
F1 -ef F2 文件F1和文件F2都是同一个文件的硬链接 *
! "非" (反转上边的测试结果)
参数替换和扩展
表达式 含义
${var} 变量var的 值, 与$var相同
${var-DEFAULT} 如果var没 有被声明, 那么就以$DEFAULT作为其值 *
${var:-DEFAULT} 如果var没 有被声明, 或者其值为空, 那么就以$DEFAULT作为其值 *
${var=DEFAULT} 如果var没 有被声明, 那么就以$DEFAULT作为其值 *
${var:=DEFAULT} 如果var没 有被声明, 或者其值为空, 那么就以$DEFAULT作为其值 *
${var+OTHER} 如果var声 明了, 那么其值就是$OTHER, 否则就为null字符串
${var:+OTHER} 如 果var被设置了, 那么其值就是$OTHER, 否则就为null字符串
${var?ERR_MSG} 如果var没 被声明, 那么就打印$ERR_MSG *
${var:?ERR_MSG} 如果var没 被设置, 那么就打印$ERR_MSG *
${!varprefix*} 匹配之前所有以varprefix开头进行声明的变量
${!varprefix@} 匹配之前所有以varprefix开头进行声明的变量
${#string} $string的 长度
${string:position} 在$string中, 从位置$position开始提取子串
${string:position:length} 在$string中, 从位置$position开始提取长度为$length的子串
${string#substring} 从 变量$string的开头, 删除最短匹配$substring的子串
${string##substring} 从 变量$string的开头, 删除最长匹配$substring的子串
${string%substring} 从 变量$string的结尾, 删除最短匹配$substring的子串
${string%%substring} 从 变量$string的结尾, 删除最长匹配$substring的子串
${string/substring/replacement} 使用$replacement, 来代替第一个匹配的$substring
${string//substring/replacement} 使 用$replacement, 代替所有匹配的$substring
${string/#substring/replacement} 如 果$string的前缀匹配$substring, 那么就用$replacement来代替匹配到的$substring
${string/%substring/replacement} 如果$string的后缀匹配$substring, 那么就用$replacement来代替匹配到的$substring
expr match "$string" '$substring' 匹配$string开头的$substring* 的长度
expr "$string" : '$substring' 匹 配$string开头的$substring* 的长度
expr index "$string" $substring 在$string中匹配到的$substring的第一个字符出现的位置
expr substr $string $position $length 在$string中 从位置$position开始提取长度为$length的子串
expr match "$string" '\($substring\)' 从$string的 开头位置提取$substring*
expr "$string" : '\($substring\)' 从$string的 开头位置提取$substring*
expr match "$string" '.*\($substring\)' 从$string的 结尾提取$substring*
expr "$string" : '.*\($substring\)' 从$string的 结尾提取$substring*
中括号
if [ CONDITION ] 测 试结构
if [[ CONDITION ]] 扩 展的测试结构
Array[1]=element1 数 组初始化
[a-z] 正 则表达式的字符范围
大括号
${variable} 参数替换
${!variable} 间 接变量引用
{ command1; command2; . . . commandN; } 代码块
{string1,string2,string3,...} 大 括号扩展
圆括号
( command1; command2 ) 子shell中 执行的命令组
Array=(element1 element2 element3) 数组初始化
result=$(COMMAND) 在 子shell中执行命令, 并将结果赋值给变量
>(COMMAND) 进 程替换
<(COMMAND) 进 程替换
双圆括号
(( var = 78 )) 整型运算
var=$(( 20 + 5 )) 整型运算, 并将结果赋值给变量
后置引用
result=`COMMAND` 在 子shell中运行命令, 并将结果赋值给变量