第三章 编程的基本元素
再识变量
实例对 各种情况的测试计算
[hcr@slave2 temp]$ vim variable.sh
#!/bin/bash
a=2334
let "a += 1"
echo "a = $a"
echo
#替换成字母
b=${a/23/BB}
echo "b = $b"
declare -i b
echo "b = $b"
let "b += 1"
echo "b =$b"
echo
#替换成数字
c=BB34
echo "c = $c"
d=${c/BB/23}
echo "d = $d"
let "d += 1"
echo "d = $d"
echo
# 为空值
e=""
echo "e = $e"
let e+=1
echo "e = $e"
# 为null
echo
echo "f = $f"
let "f += 1"
echo "f = $f"
echo
shell中有3种变量:用户变量、位置变量{pracessing parameter)和环境变量。其中用户变
量在编程过程中使用最多,位置变量在对参数判断和命令返回值判断时会使用,环境变量主要是在程序运行的时候需要设置。
用户变量
声明变量
VarName=varValue
声明本地变量 local
但是必须得在脚本中去写。
Shell中的特殊符号
特殊字符 | 含义 | 用法 |
~ | 主目录,相当于 $HOME | cd ~ |
# | shell脚木中的注释 | # 注释 |
·· | 命令替换.例如·pwd·返回pwd命令执行的结果字符串 | abc=·which hadoop· |
$ | 变量表达式符号 | "a = $a" |
& | 后台作业,将此符号置于命令末端,则让命令于后台运行 | sh start.sh & |
* | 字符串通配符 | ls a* >>f.txt |
() | 在括号中执行子shell命令 | (let "e = $b + $c";echo $e) |
\ | 转义下一个字符 | \\ |
| | 管道 | ps -ef |grep tomcat |
[] | 开始字符集通配符号和 if表达式 | ls [a-z]*.sh |
{} | 命令块 | a=${aa/12/34} |
; | shell命令分隔符 | (let "e = $b + $c";echo $e) |
‘’ | 强引用 | a='abc'; |
“” | 弱引用 | a="$a" |
< | 输入重定向 | cat < a.txt > b.txt |
> | 输出重定向 | cat a.txt > b.txt |
/ | 路径名目录分隔符 | cd / |
? | 单个任意字符 | ll ??.sh |
! | 逻辑NOT | ll [!a-b].txt |
替换运算符 |
| |
:- | 在符号前的变量如果存在且非空则返回符号前的变量,否则返回符号后的字符 | echo ${name:-'else'} |
:= | 在符号前的变量如果存在且非空则返回符号前的变量,否则讲把符号后的字符赋值给符号前的变量 | echo ${name:='else'} |
:? | 在符号前的变量如果存在且非空则返回符号前的变量,否则打印符号后的字符 | echo ${name:?'else'} |
:+ | 在符号前的变量如果存在则返回符号后的变量,否则返回null | echo ${name:+'else'} |
如果上边的替换运算符去掉冒号(:) ,就是一样用但是条件少了一个且非空 | ||
匹配运算符 |
| |
${varname#pattern} | 必须以开头匹配pattern模式的最短字符串将被删除,返回剩余的字符串。否则返回全部 | echo ${path#/*sr/} |
${varname##pattern} | 必须以开头匹配pattern模式的最长字符串将被删除,返回剩余的字符串。否则返回全部 | echo ${path##/*sr/} |
${varname%pattern} | 必须以结尾匹配pattern模式的最短字符串将被删除,返回剩余的字符串。否则返回全部 | echo ${path%/*} |
${varname%%pattern} | 必须以结尾匹配pattern模式的最长字符串将被删除,返回剩余的字符串。否则返回全部 | echo ${path%%/*} |
${varname/pattern/string} | 匹配开始pattern模式的第一个字符串将被替换成string,如果string为null那么匹配的东西将被删除,返回处理后的数据。 | echo ${path/hadoop/hadoop-0.2} |
${varname//pattern/string} | 匹配开始pattern模式的所有字符串将被替换成string,如果string为null那么匹配的东西将被删除,返回处理后的数据。 | echo ${path//hadoop/hadoop-0.2} |
${varname/#pattern/string} | 必须以开头匹配开始pattern模式的最短字符串将被替换成string,如果string为null那么匹配的东西将被删除,返回处理后的数据。 | echo ${path/#\/usr/\/www}或者 |
${varname/%pattern/string} | 必须以结尾匹配开始pattern模式的最短字符串将被替换成string,如果string为null那么匹配的东西将被删除,返回处理后的数据。 | echo ${path/%\/usr/\/www} |
位置变量
位置变量也称系统变量、位置参数,是shell脚本运行时传递给脚本的参数,同时也表示在shell函数内部的函数参数。它们的名称是以数字命名(由于历史原因,直接引用的位置参数只能从$0到$9超过这个范围则必须用括号括起来,如${10}。
.
shell内置了一个shift命令,shift命令可以“截去”参数列表最左端的一个。执行了shift后,$1的值将永远一丢失,而$2的旧值会被赋给$1,以此类推。表达参数总数的$#将会减一。shift是个可带参数的命令,例如,shift 2表示截去两个参数,而单纯的shift命令的含义是shift 1。
[root@ebsdi-23260-oozie tmpFile]# cat shift.sh
#!/bin/bash
while [ -e $1 ];
do
cat $1
shift
done
[root@ebsdi-23260-oozie tmpFile]# vim for.sh
#!/bin/bash
for file in $*
do
cat $file
done
~
环境变量
常用的环境变量
名称 | 描述 |
PATH | 命令搜索路补,以胃号为分隔符。注意与D0S不同的是, |
HOME | 用户home目录的路径名,是cd命令的默认参数 |
COLUMNS | 定义了命令编辑模式下可使用命令行的长度 |
EDITOR | 默认的行编辑器 |
VISUAL | 默认的可视编辑器 |
FCEDIT | 命令fc使用的编辑器 |
HISTFILE | 命令历史文件 |
HISTSIZE | 命令历史文件中最多可包含的命令条数 |
HISTFILESIZE | 命令历史文件中包含的最大行数 |
IFS | 定义shell使用的分隔符 |
| 指向一个需要shell监视其修改时问的文件。 |
MAILCHECK | shell检查MAIL文件的周期,单位是秒。 |
SHELL | shell的路径名 |
TERM | 终端类型 |
TMOUT | shell自动退出的时间,单位为秒,若设为0,则禁正shell自动退出 |
PROMPT_COMMAND | 指定在卞命令提示符前应执行的命令 |
PS1 | 主命令提示符 |
PS2 | 二级命令提示符,命令执行过程中要求输入数据时用 |
PS3 | select的命令提示符 |
PS4 | 调试命令提示符 |
MANPATH | 寻找手册页的路径,以胃号分隔 |
LOGNAME | 用户登录名 |
MAILPATH | 功能与MAIL类似。但可以用一组文件, |
LD_LIBARAY_PATH | 寻找库的路径,以冒号分割 |
启动文件
shell使用一些启动文件来协助创建一个运行环境,其中每个文件都有特定的用途,对登录
和交互环境的影响也各不相同。/etc目录下的文件提供全局设置,如果用户主目录下存在同名文件,它将覆盖全局设置。
使用/bin/login读取/etc/passwd文件成功登陆后,启动了一个交互登录shell。用命令行可以启动一个交互非登录shell(例如[prompt] $/hin/bash )。非交互shel l通常出现在shell脚本运行的时候,之所以称为非交互的,因为它正在运行一个脚本,而且命令与命令之间并不等待用户的输入。
无论运行什么shell,文件/etc/enviranment都先运行。即使用rexedc和rshd开始的shell,也应该设置定义在/etc/enviranment文件中的环境变量。
/etc/enviranment设置诸如最小搜索路径、时区、语言等用户环境。这个文件不是一个
shellscript,并且只接受以下数据格式:
name=<value>
(环境变量名=变量值)
init开始的所有进程都要执行这个文件,它影响所有的登录shell .
函数
作为一种完整的编程语言,Linuxshell必定不能缺少函数( function)支持:一段独立的程序
代码用于执行一个完整的单项工作。函数复用是优质代码的一大特征,故在一些大型的程序里,常常可以见到函数的身影。
当shell执行函数时,并不独立创建子进程。常用的做法是,将函数写入其他文件中,当需要的时候才将它们载入脚本。
执行命令的顺序
交互shell在获得用户输入时,并不是直接就在PATH路径中查找,而是按照固定顺序依次移
找命令位置。搜索顺序为;
别名 即使用aliascommand="..",创建的命令。
关键字 如if, fvro
函数自定义的一些函数
内置命令 如cd, pwd等命令。
外部命令 即脚木或可执行程序,这才在path路径中查找
由此可见,在同名时,函数的优先级高于脚本。可以使用内置命令 command,buildin 和enable改变优先级顺序,它允许你将函数、别名和脚本文件定义成相同的名字,井选抒执行其中之一。
如果想要知道执行的命令是哪种类型,可以使用type命令。当重命名时,type命令会告诉你真正被执行的命令的来源是别名、函数或外部命令。
[houchangren@ebsdi-23260-oozie shell]$ type user_login
user_login is a function
user_login ()
{
if who | grep $1 >> /dev/null; then
echo " user $1 is on";
else
echo " user $1 is off";
fi
}
[houchangren@ebsdi-23260-oozie shell]$ type ll
ll is aliased to `ls -l --color=tty'
[houchangren@ebsdi-23260-oozie shell]$ type ls
ls is aliased to `ls --color=tty'
[houchangren@ebsdi-23260-oozie shell]$ type cat
cat is hashed (/bin/cat)
函数的使用规则
函数使用时,遵循一些重要规则:
》函数必须先定义,后使用。
》函数在当前环境下运行,共享调用它的脚木中的变量,并且,函数允许你以给位置参数赋值的方式向函数传递参数。函数体内部可以使用local限定词创建局部变量。
》如果在函数中使用exit命令,会退出脚本。如果想退回到原本调用函数的地方,则使用 return命令。
》函数的return语句返回函数执行最后一条命令的退出状态。
》使用内置命令export -f 可以将函数等出到子shell中。
》如果函数保存在其他文件巾,可以使用source或dot(就是圆点./start.sh)命令将它们装入当前脚木。
》函数可以递归调用,并且没有调用限制。
》可以使用declare -f 找到登录会话中定义的函数。函数会按照字母顺序打印所有的函数定义。这个定义列表可能会很长,需要使用文本阅读器more或less查看。如果仅仅想看函数名,则使用declare -F语句。
自动加载
如果想在每次启动系统时自动加载函数,则只需要将函数写入启动文件中。例如
$HOME/.profile中即可,则每次启动时,source$HOME/.profile都会自动加载函数.
函数定义
函数定义
[houchangren@ebsdi-23260-oozie shell]$ cat user_login.sh
#!/bin/bash
function user_login(){
if who | grep $1 >> /dev/null ; then
echo " user $1 is on"
else
echo " user $1 is off"
fi
}
[houchangren@ebsdi-23260-oozie shell]$ source user_login.sh
[houchangren@ebsdi-23260-oozie shell]$ user_login2 23
bash: user_login2: command not found
[houchangren@ebsdi-23260-oozie shell]$ user_login erpmerge
user erpmerge is on
[houchangren@ebsdi-23260-oozie shell]$ user_login hbase
user hbase is off
[houchangren@ebsdi-23260-oozie shell]$ unset -f user_login
使用source 读入文件内容
然后使用操作
unset -f 命令可以取消函数
函数的参数和返回值
由于函数是在当前shell中执行,所以变量对于函数和shell都可见。在函数内部对变量做的任何改动也会影响shell的环境。
参数 你可以像使用命令一样,向函数传递位置参数,位置参数是函数私有的,对位置参数的任何操作并不会影响函数外部使用的枉何参数。
局部变量限定词local 当使用local时,定义的变量为函数的内部变量。内部变量在函数退出时消失,不会影响到外部同名的变量。
返回方式return return命令可以在函数体内返回函数被调用的位置。如果没有指定return的参数,则函数返回最后一条命令的退出状态。return命令同样也可以返回传给它的参数。按照规定,return命令只能返回D到255之间的整数。如果在函数体内使用exit命令,则退出整个脚本。
[houchangren@ebsdi-23260-oozie shell]$ cat add.sh
#!/bin/bash
_add(){
let "sum=$1+$2"
return $sum
}
[houchangren@ebsdi-23260-oozie shell]$
[houchangren@ebsdi-23260-oozie shell]$ _add 12 23
[houchangren@ebsdi-23260-oozie shell]$ echo $?
35
条件控制和流程控制
条件控制
If/else
if who | grep 'sdfsdfds';
then echo 'right';
elseif who | grep 'sdfsdfsdfsd';
echo 'right';
else
echo 'wrong';
fi
If [test] ;
then
do cmd
elseif [test];
then
docmd;
else
do cmd;
fi
ifelse组合,
if else
if elseif
if elseif if
退出状态
每条命令或函数,在退出时都会返回一个小的整数值给调用它的程序。这就是命令或函数的退出状态(exit status )。与c语言稍有不同的是,在判断语句中,条件(cnndition)实际上是语句列表,而不是般的布尔表达式。
按照惯例,函数以及命令的退出状态用0来表示成功,而非零表示失败。
POSIX中定义了退出状态对应的值
值 | 描述 |
0 | 成功 |
>0 | 在重定向或者单词展开期间(~、变量、命令、算数展开、单词切割等)操作失败 |
1-125 | 命令退出失败。特定退出值的定义,参见不同命令的定义 |
126 | 命令找到而无法执行命令文件 |
127 | 命令无法找到 |
>128 | 命令因受到信号而死亡 |
退出状态和逻辑操作
Not and or
If [ !condition];
then
statement
if
If [ condition && condition2];
then
statement
if
If [ condition || condition2];
then
statement
if
条件测试
If语句
if test 2 -gt 3; then echo '大于'; fi
if [ 2 -gt 3 ]; then echo '大于'; fi
注意点,在使用 [ ]这个的时候一定要记得参数和符号之前有空格
字符串比较
-z string | 字符串string 为空串(长度为0)时返回真 |
-n string | 字符串string 为非空串时返回真 |
str1 = str2 | 字符串str1 和字符串str2 相等时返回真 |
str1 == str2 | 同 = |
str1 != str2 | 字符串str1 和字符串str2 不相等时返回真 |
str1 < str2 | 按字典顺序排序,字符串str1 在字符串str2 之前 |
[houchangren@ebsdi-23260-oozie shell]$ cat if.sh
#!/bin/bash
if [ $1 = "test" ]
then
echo "right"
fi
[houchangren@ebsdi-23260-oozie shell]$ sh if.sh test
right
[houchangren@ebsdi-23260-oozie shell]$ sh if.sh test3
[houchangren@ebsdi-23260-oozie shell]$
字符串的操作符两边都必须有空格哦。
[houchangren@ebsdi-23260-oozie shell]$ cat checkUserIsExist.sh
#!/bin/sh
line=|grep $1 /etc/passwd |
if [ -n $line ]
then
echo "user $1 exist"
fi
[houchangren@ebsdi-23260-oozie shell]$ sh checkUserIsExist.sh houchangren
user houchangren exist
整数数值比较
int1 -eq int2 | 如果int1 等于int2,则返回真 |
int1 -ne int2 | 如果int1 不等于int2,则返回真 |
int1 -lt int2 | 如果int1 小于int2,则返回真 |
int1 -le int2 | 如果int1 小于等于int2,则返回真 |
int1 -gt int2 | 如果int1 大于int2,则返回真 |
int1 -ge int2 | 如果int1 大于等于int2,则返回真 |
< | 小于(在双括号里使用) | (("$a" < "$b")) |
<= | 小于等于 (在双括号里使用) | (("$a" <= "$b")) |
> | 大于 (在双括号里使用) | (("$a" > "$b")) |
>= | 大于等于(在双括号里使用) | (("$a" >= "$b")) |
参考 :http://blog.csdn.net/ruishenh/article/details/17998921
文件属性检查
-b filename | 当filename 存在并且是块文件时返回真(返回0) |
-c filename | 当filename 存在并且是字符文件时返回真 |
-d pathname | 当pathname 存在并且是一个目录时返回真 |
-e pathname | 当由pathname 指定的文件或目录存在时返回真 |
-f filename | 当filename 存在并且是正规文件时返回真 |
-g pathname | 当由pathname 指定的文件或目录存在并且设置了SGID 位时返回真 |
-h filename | 当filename 存在并且是符号链接文件时返回真 (或 -L filename) |
-k pathname | 当由pathname 指定的文件或目录存在并且设置了"粘滞"位时返回真 |
-p filename | 当filename 存在并且是命名管道时返回真 |
-r pathname | 当由pathname 指定的文件或目录存在并且可读时返回真 |
-s filename | 当filename 存在并且文件大小大于0 时返回真 |
-S filename | 当filename 存在并且是socket 时返回真 |
-t fd | 当fd 是与终端设备相关联的文件描述符时返回真 |
-u pathname | 当由pathname 指定的文件或目录存在并且设置了SUID 位时返回真 |
-w pathname | 当由pathname 指定的文件或目录存在并且可写时返回真 |
-x pathname | 当由pathname 指定的文件或目录存在并且可执行时返回真 |
-O pathname | 当由pathname 存在并且被当前进程的有效用户id 的用户拥有时返回真(字母O 大写) |
-G pathname | 当由pathname 存在并且属于当前进程的有效用户id 的用户的用户组时返回真 |
file1 -nt file2 | file1 比file2 新时返回真 |
file1 -ot file2 | file1 比file2 旧时返回真 |
f1 -ef f2 | f1和f2硬链接到的是一个文件是为真 |
测试脚本
[houchangren@ebsdi-23260-oozie shell]$ ll
总计 24
-rw-rw-r-- 1 houchangren houchangren 51 01-08 10:46 add.sh
-rw-rw-r-- 1 houchangren houchangren 85 01-08 13:38 checkUserIsExist.sh
-rwxrwxr-x 1 houchangren houchangren 284 01-08 13:55 testalg.sh
[houchangren@ebsdi-23260-oozie shell]$ cat testalg.sh
#!/bin/bash
file=$1
if [ -d $file ]
then
echo "$file is dir"
elif [ -f $file ]
then
echo "$file is file "
if [ -r $file ] && [ -w $file ] && [ -x $file ]
then
echo "You have read,write,execute permission on $file";
fi
else
echo "$file is neither a file and a directory"
fi
[houchangren@ebsdi-23260-oozie shell]$ sh testalg.sh /etc
/etc is dir
[houchangren@ebsdi-23260-oozie shell]$ sh testalg.sh ../
../ is dir
[houchangren@ebsdi-23260-oozie shell]$ sh testalg.sh testalg.sh
testalg.sh is file
You have read,write,execute permission on testalg.sh
[houchangren@ebsdi-23260-oozie shell]$ sh testalg.sh /dev/null
/dev/null is neither a file and a directory
[houchangren@ebsdi-23260-oozie shell]$
Case语句
当存在很多个逻辑判断的时候if用起来就显着那么臃肿了。Case可以很简洁的指定
case也是一个流程控制结构。Parscal中的case语句和C语言中的switch语句被用来测试诸
如整数和字符的简单值。shell中的case语句。可以依据可包含通配符的模式测试字符串。
[houchangren@ebsdi-23260-oozie shell]$ cat testcase.sh
#!/bin/bash
case $1 in
-f) echo "参数1是-f";;
-d) echo "参数1是-d";;
-x) echo "参数1是-x";;
-w) echo "参数1是-w";;
*) echo "输入的参数不是预先估的参数!";;
esac
[houchangren@ebsdi-23260-oozie shell]$ sh testcase.sh -w
参数1是-w
[houchangren@ebsdi-23260-oozie shell]$ sh testcase.sh -ll
输入的参数不是预先估的参数!
循环控制
在众多高级语言中,循环总是不可缺少的元素。循环一让我们控制某些代码的重复行为,或允许对多个对象操作。
for循环是最简单的循环,我们在许多编程语言中都可以见到它的身影。在shell脚本里,对象可以是命令行参数、文件名,或者任何、可以以列表格式建立的东西。
[houchangren@ebsdi-23260-oozie shell]$ for file in *.sh
> do
> echo $file
> done
add.sh
a.sh
checkUserIsExist.sh
if.sh
testalg.sh
testcase.sh
user_login.sh
[houchangren@ebsdi-23260-oozie shell]$
在for循环中,如果in list被省略,则默认为in"$@",即命令行参数的引用列表。就好像你已经输入了for name in “$@”。
[houchangren@ebsdi-23260-oozie shell]$ cat testfor.sh
#!/bin/sh
for param
do
echo $param
done
[houchangren@ebsdi-23260-oozie shell]$ sh testfor.sh 1
1
[houchangren@ebsdi-23260-oozie shell]$ sh testfor.sh 1 2
1
2
[houchangren@ebsdi-23260-oozie shell]$ sh testfor.sh 1 2 3 4
1
2
3
4
[houchangren@ebsdi-23260-oozie shell]$
While /until
While 和传统中的操作一样
While [ condition ]
do
docmd
done
until [ condition ]
do
docmd
done
while和until的操作一样,就是条件处理循环的时候是相反的。While是条件=true接着循环
跳出循环
break和continue语句允许对循环的运行精确控制:跳出循环或重新执行循环。
当嵌套层超过一层的时候可以指定break或者continue后加数字跳出(继续)指定的层
[houchangren@ebsdi-23260-oozie shell]$ cat testwhile.sh
#!/bin/bash
num=0
while true
do
echo "while1- $num"
while true
do
echo "while2- $num"
while true
do
echo "while3- $num"
num=$(($num+1))
case $num in
5 ) break 2;;
10 ) break 3;;
* ) break 1 ;;
esac
done
done
if [ $num -ge $1 ];
then
break 1
fi
done
[houchangren@ebsdi-23260-oozie shell]$ sh testwhile.sh 10
while1- 0
while2- 0
while3- 0
while2- 1
while3- 1
while2- 2
while3- 2
while2- 3
while3- 3
while2- 4
while3- 4
while1- 5
while2- 5
while3- 5
while2- 6
while3- 6
while2- 7
while3- 7
while2- 8
while3- 8
while2- 9
while3- 9
[houchangren@ebsdi-23260-oozie shell]$
实例
[houchangren@ebsdi-23260-oozie shell]$ cat whileexample.sh
#!/bin/bash
author=false
list=false
file=""
while [ $# -ge 0 ]
do
case $1 in
-f) file=$2;echo "filename:$file"; shift ;;
-l) list=true;;
-a) author=true;;
--) shift;break ;;
-*) echo $0:$1:unrecognized option ;;
* ) break ;;
esac
shift
done
[houchangren@ebsdi-23260-oozie shell]$ sh whileexample.sh -f whileexample.sh
filename:whileexample.sh
[houchangren@ebsdi-23260-oozie shell]$ sh whileexample.sh -fx wer
whileexample.sh:-fx:unrecognized option
[houchangren@ebsdi-23260-oozie shell]$
用getopts工具来格式化参数
Getopts参考
http://www.2cto.com/os/201309/246174.html
[houchangren@ebsdi-23260-oozie shell]$ cat whileexample2.sh
#!/bin/bash
author=false
list=false
file=""
while getopts alf: opt
do
case $opt in
f) file=$OPTARG;echo "file:$file" ;;
l) list=true ;;
a) author=ture;;
esac
done
shift $(($OPTIND -1))
[houchangren@ebsdi-23260-oozie shell]$ sh whileexample2.sh -f whileexample.sh
file:whileexample.sh
[houchangren@ebsdi-23260-oozie shell]$