大数据学习第四天:
学习主题:shell编程
shell :弱类型、 解释型语言
解释器:shell ,(bash,ksh,zsh)
脚本的执行:当前的shell下启动一个子shell去执行(翻译)脚本
shell 是一个交互性命令解释器。shell独立于操作系统,这种设计让用户可以灵活选择适合自己的shell。shell让你在命令行键入命令,经过shell解释后传送给操作系统(内核)执行。 shell是一个命令处理器(command processor)——是一个读入并解释你输入的命令的程序。除了是一个命令中断器以外,shell还是一个程序设计语言。你可以编写shell可以解释的程序(被称为源程序),这些源程序可以包含shell程序设计命令等等。shell除了解释命令以外,还有其他工作,它也可以配置和编程。 shell拥有自己的语言允许用户编写程序并以一种复杂方式运行。shell编程语言具有许多常用的编程语言的特征,例如:循环和控制结构等。用户可以生成像其他应用程序一样复杂的shell程序。
一.shell变量
shell变量和一些编程语言不同,一般shell的变量赋值的时候不用带“ ” , 而 使 用 或 者 输 出 的 时 候 要 带 “ ”,而使用或者输出的时候要带“ ”,而使用或者输出的时候要带“”。加减乘除的时候要加两层小括号。括号外面要有一个“ ” , 括 号 里 面 的 变 量 可 以 不 用 “ ”,括号里面的变量可以不用“ ”,括号里面的变量可以不用“”。需要注意的是,变量赋值,变量使用的时候不能有空格,否则会被解析成命令,报错无此命令。
例子:
#!/bin/bash
a=10
b=20
c=“this is a test”
d=
(
(
a
+
b
)
)
e
=
((a+b)) e=
((a+b))e=((a-b))
f=
(
(
a
∗
b
)
)
g
=
((a*b)) g=
((a∗b))g=((a/b))
h=
(
(
a
i
=
((a%b)) i=
((ai=((a**3))
echo
c
e
c
h
o
"
a
=
"
c echo "a = "
cecho"a="a #输出a的值
echo "b = "KaTeX parse error: Expected 'EOF', got '#' at position 12: b #̲输出b的值 echo "a+b…{d} #输出a+b的值
echo "a-b = "KaTeX parse error: Expected 'EOF', got '#' at position 10: {e} #̲输出a-b的值 echo "a…{f} #输出a*b的值
echo "a/b = "KaTeX parse error: Expected 'EOF', got '#' at position 10: {g} #̲输出a/b的值 echo "a…{h} #输出a%b的值
echo "a^3 = "${i} #输出a的3次方的值
echo "a+b = "KaTeX parse error: Expected 'EOF', got '#' at position 10: ((a+b)) #̲输出a+b的值 echo "a…((a-b)) #输出a-b的值
echo "a*b = "KaTeX parse error: Expected 'EOF', got '#' at position 10: ((a*b)) #̲输出a*b的值 echo "a…((a/b)) #输出a/b的值
echo "a%b = "$((a%b)) #输出a%b的值
echo "a^3 = "$((a**3)) #输出a的3次方的值
echo $((a+b*a-b/a+a%b+a**2)) #表达式可以很长
变量的类型
shell 中有四种类型的变量:用户自定义变量、环境变量、位置参数变量和预定义变量。
1) 用户自定义变量
用户自定义变量只会在当前 shell 中生效,也就是“局部变量”,上面程序中的 name、age、address、money 等都是用户自定义变量,只能在变量所在的那个 shell 脚本中生效。用户自定义变量一般用小写字母来命名。
2) 环境变量
当一个 shell 脚本程序开始执行时,一些变量会根据环境设置中的值进行初始化,这些变量通常用大写字母做名字,以便与用户自定义变量做区分,被称为环境变量。环境变量可以在当前 shell 和这个 shell 的所有子 shell 中生效。如果把环境变量写入相应的配置文件(如 /etc/profile ),那么这个环境变量就会在所有的 shell 中生效。系统自带的环境变量的名字不可更改,但是值可以按需更改。用户也可以使用 export 命令在 shell 中自己创建环境变量:
export 变量名=变量值 # 创建环境变量并赋值
一些主要的系统环境变量如下:
环境变量 | 描述 |
---|---|
$HOME | 当前用户的家目录 |
$PATH | 以冒号分隔的用来搜索命令的目录列表,决定了 shell 将到哪些目录中去寻找命令或程序 |
$PS1 | 命令提示符,通常是 $ 字符,也可以自行设置 |
$PS2 | 二级提示符,用来提示后续的输入,通常是 > 字符 |
$IFS | 输入域分隔符。当 shell 读取输入时,它给出用来分隔单词的一组字符,通常是空格、制表符和换行符 |
$0 | shell 脚本的名字 |
$# | 传递给脚本的参数个数 |
$$ | shell 脚本的进程号(PID),脚本程序通常会用它来生成一个唯一的临时文件,如 /tmp/tmpfile_$$ |
3) 位置参数变量
位置参数变量主要用来向脚本中传递参数或数据,变量名不能自定义,变量作用也是固定的。主要有以下几种位置参数变量:
位置参数变量 | 描述 |
---|---|
$1、$2、… | 脚本程序的参数,分别代表程序的第1个参数、第2个参数、… 程序第10个以上的参数需要用大括号包含,如 ${10} |
$* | 代表命令行中的所有参数。在一个变量中将所有参数列出,各参数之间用环境变量 IFS 中的第一个字符分隔开。 |
$@ | 和 $* 一样,也包含了命令行中的所有参数,但是不使用 IFS 环境变量,即使 IFS 为空,参数也是分开显示的 |
关于 $0 和 $#,在有些资料上,也把这两个归为位置参数变量,本文是把它们归为了环境变量。其中, 0 代 表 s h e l l 脚 本 本 身 ( 不 算 在 参 数 行 列 ) , 0 代表 shell 脚本本身(不算在参数行列), 0代表shell脚本本身(不算在参数行列),# 代表传递给脚本的参数个数(不包括 $0)。
关于 $* 和 $@,这二者的区别就在 $* 使用 IFS 所定义的分隔符来分隔参数而 @ 没 有 使 用 。 @ 没有使用。 @没有使用。* 将所有的参数视为一个整体,而 $@ 将所有的参数分别视为单独的个体。一般来说,采用 $@ 来访问脚本程序的参数会比较好,不必担心 IFS 所设置的分隔符为空而导致各参数连在一起分不清楚。
4) 预定义变量
预定义变量是在 bash 中已经定义好了的变量,变量名不能自定义,变量作用也是固定的。实际上,位置参数变量就是预定义变量的一种。 除了上面介绍的一些外,这里再介绍两个:
$? :保存最后一次执行的命令的返回状态。如果 $? 的值为 0 ,则表明上一个命令成功执行;如果值非 0 ,则表明上一个命令没有成功执行。
$! :用于保存后运行的最后一个进程的 PID 号。
注意:脚本在执行时都会启动一个子shell进程:
命令行中启动的脚本会继承当前shell环境变量。
系统自动启动脚本(非命令行启动):则需要自我定义环境变量。
* 传递到脚本的参数,与位置变量不同,此选项参数可超过9个
脚 本 运 行 时 当 前 进 程 的 I D 号 , 常 用 作 临 时 变 量 的 后 缀 , 如 h a i s o n . 脚本运行时当前进程的ID号,常用作临时变量的后缀,如 haison. 脚本运行时当前进程的ID号,常用作临时变量的后缀,如haison.
$! 后台运行的(&)最后一个进程的ID号
@ 与 @ 与 @与#相同,使用时加引号,并在引号中返回参数个数
$- 上一个命令的最后一个参数
$? 最后命令的退出状态,0表示没有错误,其他任何值表明有错误
程序有两类返回值:
1、 执行结果 :标准,错误 :标准输出,错误输出
2、 执行状态,$? : 0:表示正确,1-255:错误
输出重定向:
>覆盖重定向
>> 追加重定向
2> 错误覆盖重定向
2>>错误追加重定向
&> 全部重定向
&>> 全部信息追加重定向
撤销变量:
unset 变量名
查看shell中变量:
set 命令
查看shell中的环境变量
printenv
env
export
引用变量:${变量名},一般可以省略{}
条件判断,控制结构:
然,在上课时,是通过人的大脑进行判断的;如果在程序语言中,就要通过 if 条件判断语句来判断了。
单分支 if 条件语句
单分支 if 条件语句最为简单,就是只有一个判断条件,如果符合条件则执行某个程序,否则什么事情都不做。语法如下:
if [条件判断式];then
程序
fi
在使用单分支 if 条件查询时需要注意几点:
- if 语句使用 fi 结尾,和一般语言使用大括号结尾不同。
- [条件判断式] 就是使用 test 命令判断,所以中括号和条件判断式之间必须有空格。
- then 后面跟符合条件之后执行的程序,可以放在 [] 之后,用":“分隔;也可以换行写入,就不需要”:"了,比如单分支 if 条件语句还可以这样写:
if [条件判断式]
then
程序
fi
单分支 if 条件语句非常简单,但是千万不要小看它,这是流程控制语句最基本的语法。而且在实现
Linux
管理时,我们的管理脚本一般都不复杂,单分支 if 条件语句使用的概率还是很大的。
举个例子,我想通过脚本判断根分区的使用率是否超过 80%,如果超过 80% 则向管理员报警,请他注意。 脚本就可以这样写:
[root@localhost ~]# df -h
#查看一下服务器的分区状况
文件系统 容量 已用 可用 已用% %挂载点
/dev/sda3 20G 1.8G 17G 10% /
tmpfs 306M 0 306M 0% /dev/shm
/dev/sda1 194M 26M 158M 15% /boot
/dev/srO 3.5G 3.5G 0100% /mnt/cdrom
[root@localhost ~]# vi sh/if1.sh
#!/bin/bash
#统计根分区的使用率
rate=$(df -h | grep “/dev/sda3” | awk ‘{print $5}’ | cut -d"%"-f1)
#把根分区使用率作为变量值赋予变量rate
if [$rate -ge 80 】
#判断rate的值,如果大于等于80,则执行then程序
then
echo 'Warning! /dev/sda3 is full!!"
#打印警告信息。在实际工作中,也可以向管理员发送邮件
fi
其实这个脚本最主要的地方是"rate=$(df-h|grep “/dev/sda3”|awk’{print$5}’|cut-d"%"-f1)“这条命令,我们来分析一下这条命令:先使用"df-h"列出系统中的分区情况;然后使用"grep"命令提取出根分区行;再使用"awk"命令列出第五列,也就是根分区使用率这一列(不过使用率是 10%,不好比较,还要提取出 10 这个数字);最后使用"cut"命令(cut 命令比 awk 命令简单),以”%"作为分隔符,提取出第一列。
这条命令的执行结果如下:
[root@localhost ~]# df -h | grep “/dev/sda3” |awk’{print $5}’|cut -d"%" -f1 10
提取出根分区的使用率后,判断这个数字是否大于等于 80,如果大于等于 80 则报警。至于报警信息,我们在脚本中直接输出到屏幕上。在实际工作中,因为服务器屏幕并不是 24 小时有人值守的,所以也可以给管理员发送邮件,用于报警。
脚本写好之后,就可以利用我们在前面章节中讲到的系统定时任务,让这个脚本每天或几天执行一次,就可以实现自动检测硬盘剩余空间了。后续的系统管理的脚本,如果需要重复执行,则也需要依赖系统定时任务。
双分支条件语句
在双分支 if 条件语句中,当条件判断式成立时,则执行某个程序;当条件判断式不成立时,则执行另一个程序。
语法如下:
if [条件判断式]
then
当条件判断式成立时,执行的程序
else
当条件判断式不成立时,执行的另一个程序
fi
例如,还记得我们在进行条件测试时是怎么显示测试结果的吗?
[root@localhost ~]# [-d /root/sh] && echo “yes” || echo “no”
#第一条判断命令如果正确执行,则打印"yes"; 否则打印"no"
yes
这样显示条件测试的结果还是非常不方便的。当时是因为我们还没有讲 if 语句,所以只能用逻辑与和逻辑或来显示测试结果。既然我们已经学习了 if 语句,就把这个条件测试改写为 if 语句吧。
#!/bin/bash
#判断输入的文件是否是一个目录
read -t 30 -p “Please input a directory:” dir #read接受键盘的输入,并存入dir变量
if[-d $dir]
#测试$dir中的内容是否是一个目录
then
echo “yes”
#如果是一个目录,则输出yes
else
echo “no”
#如果不是一个目录,则输出no
fi
解释一下这个脚本的思路:其实逻辑与和逻辑或也是在判断前一条命令的"$?"的返回值是不是 0,如果是 0,则前一条命令正确执行;如果不是 0,则证明前一条命令执行错误。双分支if条件语句的判断思路也是测试条件判断式是否成立,如果成立,则执行"then"中的命令;如果不成立,则执行"else"中的命令。
多分支 if 条件语句
在多分支 if 条件语句中,允许执行多次判断。也就是当条件判断式 1 成立时,执行程序 1;当条件判断式 2 成立时,执行程序 2;依次类推,当所有条件不成立时,执行最后的程序。语法如下:
if[条件判断式1]
then
当条件判断式1成立时,执行程序1
elif [条件判断式2]
then
当条件判断式2成立时,执行程序2
…省略更多条件…
else
当所有条件都不成立时,最后执行此程序、
fi
例如,用多分支 if 条件语句来判断用户输入的是一个个文件还是一个目录。
[root@localhost ~]#vi sh/if-elif.sh
#!/bin/bash
#判断用户输入的是什么文件
read -p “Please input a filename:” file
#接收键盘的输入,并赋予变量file
if[-z “$file”]
#判断file变量是否为空
then
echo “Error,please input a filename”
#如果为空,则执行程序1,也就是输出报错信息、
exit 1
#退出程序,并定义返回值为1 (把返回值赋予变量$?)
elif[!-e “$file”]
#判断file的值是否存在
then
echo 'Your input is not a file!"
#如果不存在,则执行程序2
exit 2
#退出程序,并定义返回值为2
elif[-f “$file”]
#判断file的值是否为普通文件
then
echo “$file is a regulare file!”
#如果是普通文件,则执行程序3
elif[-d"$file"]
#判断file的值是否为目录文件
then
echo “$file is a directory!”
#如果是目录文件,则执行程序4
else
echo is an other file!"
#如果以上判断都不是,则执行程序5
fi
解释一下脚本思路:这个脚本比较简单,需要说明的是 exit 这条命令。这条命令是退出执行程序的命令,如果符合条件 1(没有输入)和条件 2(输入的不是文件),则需要执行 exit 命令;否则程序还是会运行一下脚本的,这不符合我们的要求。至于 exit 后面的返回值,是自由定义的,主要用于把返回值赋予变量 $?。我们执行一下这个脚本:
[root@localhost ~]# chmod 755 sh/if-elif.sh
#赋予执行权限
[root@localhost ~]# sh/if-elif.sh
#执行脚本
Please input a filename:
#没有任何输入
Error,please input a filename
#报错信息是脚本中自己定义的
[root@localhost ~]# echo $?
1
#变量$?的返回值是我们自己定义的1
[root@localhost ~]# sh/if-elif.sh
Please input a filename: jkgeia
#随便输入不是文件的字符串
Your input is not a file!
#报错信息是自己定义的
[root@localhost ~]# echo $?
2
#变量$?的返回值是我们自己定义的2
逻辑运算来做条件判读 : && 或者 ||
我们在编写脚本中无论如何都少不了逻辑判断,我们通过逻辑判断来进行我们需要进行的操作,完成我们需要的功能。现在我们先看一下最基本的逻辑功能。
true:1;false:0
与:
1 与 1 = 1
1 与 0 = 0
0 与 1 = 0
0 与 0 = 0
或:
1 或 1 = 1
1 或 0 = 1
0 或 1 = 1
0 或 0 = 0
这是我们最基本的逻辑与和或的关系,我们所有的逻辑判断都是基于这些基本的逻辑判断上的。在基本的逻辑中还有一个判断那就是**“非”或“!”代表的是一个意思即!1=0,!0=1**
短路运算,异或
短路与(&&)
第一个为0,结果必定为0
第一个为1,第二个必须要参与运算
短路或(||)
第一个为1,结果必定为1
第一个为0,第二个必须要参与运算
异或(^)
异或的两个值,相同为假,不同为真
[](javascript:void(0)?
A && B
当A为0时,不再执行B
当A为1时,还会执行B
A || B
当A为1时,不再执行B
当A为0时,继续执行A
A ^ B
当A == B,为真
当A != B,为假
[](javascript:void(0)?
条件测试
在linux脚本中,我们需要一些工具来帮助我们完成逻辑的判断,通过判断真假条件,在通过逻辑运算符来指定判断出真假后我们需要进一步做些什么,在linux中,有评估布尔声明,以便用在条件执行中:
若真,返回0
若假,返回1
我们一般的判断命令如下:
test expression
[exoression]
[[expression]]
对于这三种判断方法,我们一般使用最多的是后两种
注意: EXPRESSION****前后必须有空白字符
示例:
[ “A”“A”" B" ] && echo “Strings are equal”
[ “A"−eq"A"−eq” B" ] && echo “Integers are equal”
[root@CT6 man]# A=23
[root@CT6 man]# B=24
[root@CT6 man]# [ $A -eq $B ] && echo "YES" || echo "NO"
NO
[root@CT6 man]#
判断一个变量是否设置,如果我们需要用一个变量,但不知道这个变来那个是否存在,我们可以使用-v来判断这个变量是否存在。
[ -v VAR ] && echo “变量存在” || echo “变量不存在”
[root@CT6 man]# set | grep "^A"
[root@CT6 man]# [ -v $A ] && echo "YES" || echo "NO"
YES
[root@CT6 man]#
数值测试
在脚本中那我们很多时候需要判断数字的大小,而linux中给我们提供了一种判断数字大小的方法,配合条件判断来进行使用:
-gt 是否大于
-ge 是否大于等于
-eq 是否等于
-ne 是否不等于
-lt 是否小于
-le 是否小于等于
示例:
[ 23 –gt 22 ] && echo “true” || echo “false”
[ 66 –ge 66 ] && echo “true” || echo “false”
[root@CT6 man]# [ 23 -gt 22 ] && echo "true" || echo "false"
true
[root@CT6 man]# [ 66 -ge 66 ] && echo "true" || echo "false"
true
字符串测试
除了数值测试完,还有字符串测试,用于测试字符是否相同等。
== 是否等于
> ascii码是否大于ascii码
< 是否小于
!= 是否不等于
=~ 左侧字符串是否能够被右侧的PATTERN所匹配
注意: **此表达式一般用于[[ ]]**中;扩展的正则表达式
[root@CT6 man]# str1=LiuDehua
[root@CT6 man]# str2=LiuDehua
[root@CT6 man]# [ "$str1" == "$str2" ] && echo "=" || echo '!='
=
[](javascript:void(0)?
[root@CT6 man]# A=string_1234
[root@CT6 man]# [[ $A =~ [[:alpha:]]+[_][0-9]* ]] && echo "匹配" || echo "不匹配"
匹配
[root@CT6 man]# [[ $A =~ ^[[:digit:]] ]] && echo "匹配" || echo "不匹配"
不匹配
[](javascript:void(0)?
-z "STRING“ 字符串是否为空,空为真,不空为假
-n "STRING“ 字符串是否不空,不空为真,空为假
注意:用于字符串比较时的用到的操作数都应该使用引号
[](javascript:void(0)?
[root@CT6 man]# [ -z " " ] && echo "true" || echo "false"
false
[root@CT6 man]# [ -z "" ] && echo "true" || echo "false"
true
[root@CT6 man]# [ -n "string" ] && echo "true" || echo "false"
true
[root@CT6 man]# [ -n "" ] && echo "true" || echo "false"
false
[](javascript:void(0)?
文件及属性测试
在脚本中我们很多时候要对文件进行处理,那么对文件的判断就显得尤为重要,包括文件是否存在,文件的类型,文件的权限,文件的大小,文件的修改访问时间等,我们通过一定的方式可以实现对文件的各种判断,然后就可以实现我们的目的。
存在性测试
-a FILE:同-e(and也是-a 一般使用-e)
-e FILE: 文件存在性测试,存在为真,否则为假
[](javascript:void(0)?
[root@CT6 tmp]# echo 12345 >> test1.txt
[root@CT6 tmp]# ll
total 4
-rw-r--r--. 1 root root 6 Aug 15 15:04 test1.txt
[root@CT6 tmp]# [ -e test1.txt ] && echo "true" || echo "false"
true
[root@CT6 tmp]# rm test1.txt
rm: remove regular file `test1.txt'? y
[root@CT6 tmp]# [ -e test1.txt ] && echo "true" || echo "false"
false
[](javascript:void(0)?
存在性及类别测试
-b FILE:是否存在且为块设备文件
-c FILE:是否存在且为字符设备文件
-d FILE:是否存在且为目录文件
-f FILE:是否存在且为普通文件
-h FILE 或 -L FILE:存在且为符号链接文件
-p FILE:是否存在且为命名管道文件
-S FILE:是否存在且为套接字文件
[](javascript:void(0)?
[root@CT6 tmp]# [ -b /dev/sda ] && echo "YES" || echo "NO"
YES
[root@CT6 tmp]# [ -d /dev ] && echo "YES" || echo "NO"
YES
[root@CT6 tmp]# [ -h /lib64 ] && echo "YES" || echo "NO"
NO
[](javascript:void(0)?
文件权限测试
-r FILE:是否存在且可读
-w FILE: 是否存在且可写
-x FILE: 是否存在且可执行
[](javascript:void(0)?
[du@CT6 ~]$ cd /tmp/
[du@CT6 /tmp]$ ll
total 0
---x--x--x. 1 root root 0 Aug 15 15:12 du1.doc
--w--w--w-. 1 root root 0 Aug 15 15:12 du2.doc
-rw-r--r--. 1 root root 0 Aug 15 15:12 du3.doc
-rw-r--r--. 1 root root 0 Aug 15 15:12 du4.doc
[du@CT6 /tmp]$ [ -r du1.doc ] && echo "YES" || echo "NO"
NO
[du@CT6 /tmp]$ [ -r du3.doc ] && echo "YES" || echo "NO"
YES
[du@CT6 /tmp]$ [ -w du1.doc ] && echo "YES" || echo "NO"
NO
[du@CT6 /tmp]$ [ -w du3.doc ] && echo "YES" || echo "NO"
NO
[du@CT6 /tmp]$ [ -w du2.doc ] && echo "YES" || echo "NO"
YES
[](javascript:void(0)?
文件特殊权限测试
-u FILE:是否存在且拥有suid权限
-g FILE:是否存在且拥有sgid权限
-k FILE:是否存在且拥有sticky权限
[](javascript:void(0)?
[root@CT6 tmp]# ll
total 0
-rwSr-Sr--. 1 root root 0 Aug 15 15:23 lianxi_1.doc
-rwSr--r--. 1 root root 0 Aug 15 15:23 lianxi_2.doc
-rw-r--r-T. 1 root root 0 Aug 15 15:23 lianxi_3.doc
-rw-r--r--. 1 root root 0 Aug 15 15:23 lianxi_4.doc
[root@CT6 tmp]# [ -u lianxi_2.doc ] && echo "YES" || echo "NO"
YES
[root@CT6 tmp]# [ -u lianxi_3.doc ] && echo "YES" || echo "NO"
NO
[root@CT6 tmp]# [ -k lianxi_3.doc ] && echo "YES" || echo "NO"
YES
[root@CT6 tmp]# [ -g lianxi_1.doc ] && echo "YES" || echo "NO"
YES
[](javascript:void(0)?
文件大小测试
-s FILE: 是否存在且非空
[](javascript:void(0)?
[root@CT6 tmp]# touch tty1
[root@CT6 tmp]# echo qqq > tty2
[root@CT6 tmp]# [ -s tty1 ] && echo "NOT empty" || echo "empty"
empty
[root@CT6 tmp]# [ -s tty2 ] && echo "NOT empty" || echo "empty"
NOT empty
[](javascript:void(0)?
文件是否打开
-t fd: fd表示文件描述符是否已经打开且与某终端相关
-N FILE:文件自动上一次被读取之后是否被修改过
-O FILE:当前有效用户是否为文件属主
-G FILE:当前有效用户是否为文件属组
[](javascript:void(0)?
[root@CT6 tmp]# [ -O tty2 ] && echo "YES" || echo "NO"
YES
[root@CT6 tmp]# [ -G tty2 ] && echo "YES" || echo "NO"
YES
[root@CT6 tmp]# ll
total 8
-rw-r--r--. 1 root root 0 Aug 15 15:27 tty1
-rw-r--r--. 1 root root 4 Aug 15 15:27 tty2
read命令
使用read来把输入值分配给一个或多个shell变量
-p 指定要显示的提示
-s 静默输入,一般用于密码
-n N 指定输入的字符长度N
-d ‘字符’ 输入结束符
-t N TIMEOUT为N秒
read 从标准输入中读取值,给每个单词分配一个变量
所有剩余单词都被分配给最后一个变量
read -p “Enter a filename: “ FILE
一般用于脚本中
bash的配置文件
按生效范围划分,存在两类:
全局配置:
/etc/profile
/etc/profile.d/*.sh
/etc/bashrc
个人配置:
~/.bash_profile
~/.bashrc
shell登录两种方式
交互式登录:
(1)直接通过终端输入账号密码登录
(2)使用“su - UserName” 切换的用户
执行顺序:/etc/profile --> /etc/profile.d/*.sh -->~/.bash_profile --> ~/.bashrc --> /etc/bashrc
非交互式登录:
(1)su UserName
(2)图形界面下打开的终端
(3)执行脚本
(4)任何其它的bash实例
执行顺序: ~/.bashrc --> /etc/bashrc -->/etc/profile.d/*.sh
配置文件的两种分类
按功能划分,存在两类:profile类和bashrc类
profile类:为交互式登录的shell提供配置
全局: /etc/profile, /etc/profile.d/*.sh
个人: ~/.bash_profile
功用:
(1) 用于定义环境变量
(2) 运行命令或脚本
bashrc类:为非交互式和交互式登录的shell提供配置
全局: /etc/bashrc
个人: ~/.bashrc
功用:
(1) 定义命令别名和函数
(2) 定义本地变量
编辑配置文件生效
修改profile和bashrc文件后需生效
两种方法:
1重新启动shell进程
2 . 或source
例:
. ~/.bashrc
Bash退出任务
保存在~/.bash_logout文件中(用户)
在退出登录shell时运行
用于
创建自动备份
清除临时文件
关于$-变量
h: hashall,打开这个选项后, Shell 会将命令所在的路径hash下来,避免每次都要查询。通过set +h将h选项关闭
i: interactive-comments,包含这个选项说明当前的 shell是一个交互式的 shell。所谓的交互式shell,在脚本中, i选项是关闭的。
m: monitor,打开监控模式,就可以通过Job control来控制进程的停止、继续,后台或者前台执行等。
B: braceexpand,大括号扩展
H: history, H选项打开,可以展开历史列表中的命令,可以通过!感叹号来完成,例如“!!”返回上最近的一个历史命令,“!n”返回第 n 个历史命令
[](javascript:void(0)?
[root@CT6 tmp]# echo $-
himBH
[root@CT6 tmp]# set +h
[root@CT6 tmp]# echo $-
imBH
[root@CT6 tmp]# set +B
[root@CT6 tmp]# echo $-
imH
一、for循环
1.数字段形式
代码如下:
for i in {1..10}
do
echo $i
done
2.详细列出(字符且项数不多)
代码如下:
$$
$$
for File in 1 2 3 4 5
do
echo $File
done
3.对存在的文件进行循环
代码如下:
for shname in ls *.sh
do
name=echo "$shname" | awk -F. '{print $1}'
echo $name
done
4.查找循环(ls数据量太大的时候也可以用这种方法)
复制代码 代码如下:
for shname in find . -type f -name "*.sh"
do
name=echo "$shname" | awk -F/ '{print $2}'
echo $name
done
5.((语法循环–有点像C语法,但记得双括号
复制代码 代码如下:
for((i=1;i<100;i++))
do
if((i%3==0))
then
echo $i
continue
fi
done
6.seq形式 起始从1开始
复制代码 代码如下:
for i in seq 100
do
if((i%3==0))
then
echo $i
continue
fi
done
二、while循环
1.while循环注意为方括号[],且注意空格
代码如下:
min=1
max=100
while [ min -le max ]
do
echo $min
min=expr $min + 1
done
2.双括号形式,内部结构有点像C的语法,注意赋值:i= ( ( (( ((i+1))
代码如下:
i=1
while(($i<100))
do
if(($i%4==0))
then
echo $i
fi
i=((i+1))
done
3.从配置文件读取,并可以控制进程数量
代码如下:
MAX_RUN_NUM=8
cat cfg/res_card_partition.cfg |grep -v '^$'|grep -v "#" | grep -v grep |while read partition
do
nohup sh inv_res_card_process.sh partition >log/resCardpartition.log 2>&1 &
while [ 1 -eq 1 ]
do
psNum=`ps -ef | grep "inv_res_card_process" | grep -v "grep" | wc -l`
if [ psNum -ge MAX_RUN_NUM ]
then
sleep 5
else
break
fi
done
done
三.循环控制语句
\# break 命令不执行当前循环体内break下面的语句从当前循环退出.
\# continue 命令是程序在本循体内忽略下面的语句,从循环头开始执行
case语句使用于需要进行多重分支的应用情况
格式:
[](javascript:void(0)?
case $变量名 in
模式1)
命令序列1
;;
模式2)
命令序列2
;;
*)
默认执行的命令序列
;;
esac
[](javascript:void(0)?
[](javascript:void(0)?
case语句结构特点如下:
case行尾必须为单词 in 每个模式必须以右括号 ) 结束
双分号 ;; 表示命令序列结束
case语句结构特点如下:
匹配模式中可是使用方括号表示一个连续的范围,如[0-9];使用竖杠符号“|”表示或。
最后的“*)”表示默认模式,当使用前面的各种模式均无法匹配该变量时,将执行“*)”后的命令序列。
inux shell 可以用户定义函数,然后在shell脚本中可以随便调用。下面说说它的定义方法,以及调用需要注意那些事项。
一、定义shell函数(define function)
linux shell 可以用户定义函数,然后在shell脚本中可以随便调用。
语法:
[ function ] funname [()]
{
action;
[return int;]
}
说明:
1、可以带function fun() 定义,也可以直接fun() 定义,不带任何参数。
2、参数返回,可以显示加:return 返回,如果不加,将以最后一条命令运行结果,作为返回值。 return后跟数值n(0-255
实例(testfun1.sh):
`#!/bin/sh`` ` ` ``fSum 3 2;`` ``function` `fSum()`` ``{`` ``echo` `$1,$2;`` ``return` `$(($1+$2));`` ``}`` ``fSum 5 7;`` ``total=$(fSum 3 2);`` ``echo` `$total,$?;`` ` `sh testfun1.sh``testfun1.sh: line 3: fSum: ``command` `not found``5,7``3,2`
从上面这个例子我们可以得到几点结论:
1、必须在调用函数地方之前,声明函数,shell脚本是逐行运行。不会像其它语言一样先预编译。一次必须在使用函数前先声明函数。
2、total=$(fSum 3 2); 通过这种调用方法,我们清楚知道,在shell 中 单括号里面,可以是:命令语句。 因此,我们可以将shell中函数,看作是定义一个新的命令,它是命令,因此 各个输入参数直接用 空格分隔。 一次,命令里面获得参数方法可以通过: 0 … 0… 0…n得到。 $0代表函数本身。
3、函数返回值,只能通过 ? 系 统 变 量 获 得 , 直 接 通 过 = , 获 得 是 空 值 。 其 实 , 我 们 按 照 上 面 一 条 理 解 , 知 道 函 数 是 一 个 命 令 , 在 s h e l l 获 得 命 令 返 回 值 , 都 需 要 通 过 ? 系统变量获得,直接通过=,获得是空值。其实,我们按照上面一条理解,知道函数是一个命令,在shell获得命令返回值,都需要通过 ?系统变量获得,直接通过=,获得是空值。其实,我们按照上面一条理解,知道函数是一个命令,在shell获得命令返回值,都需要通过?获得。
二、函数作用域,变量作用范围
先我们看一个实例(testfun2.sh ):
`#!/bin/sh` `echo` `$(``uname``);``declare` `num=1000;` `uname``()``{`` ``echo` `"test!"``;`` ``((num++));`` ``return` `100;``}``testvar()``{`` ``local` `num=10;`` ``((num++));`` ``echo` `$num;` `}` `uname``;``echo` `$?``echo` `$num;``testvar;``echo` `$num;`` ` ` ` `sh testfun2.sh``Linux``test``!``100``1001``11``1001`
我们一起来分析下上面这个实例,可以得到如下结论:
1、定义函数可以与系统命令相同,说明shell搜索命令时候,首先会在当前的shell文件定义好的地方查找,找到直接执行。
2、需要获得函数值:通过$?获得
3、如果需要传出其它类型函数值,可以在函数调用之前,定义变量(这个就是全局变量)。在函数内部就可以直接修改,然后在执行函数就可以读出修改过的值。
4、如果需要定义自己变量,可以在函数中定义:local 变量=值 ,这时变量就是内部变量,它的修改,不会影响函数外部相同变量的值 。
Shell echo命令
Shell 的 echo 指令与 PHP 的 echo 指令类似,都是用于字符串的输出。命令格式:
echo string
您可以使用echo实现更复杂的输出格式控制。
1.显示普通字符串:
echo "It is a test"
这里的双引号完全可以省略,以下命令与上面实例效果一致:
echo It is a test
2.显示转义字符
echo "\"It is a test\""
结果将是:
"It is a test"
同样,双引号也可以省略
3.显示变量
read 命令从标准输入中读取一行,并把输入行的每个字段的值指定给 shell 变量
#!/bin/sh
read name
echo "$name It is a test"
以上代码保存为 test.sh,name 接收标准输入的变量,结果将是:
[root@www ~]# sh test.sh
OK #标准输入
OK It is a test #输出
4.显示换行
echo -e "OK! \n" # -e 开启转义
echo "It is a test"
输出结果:
OK!
It is a test
5.显示不换行
#!/bin/sh
echo -e "OK! \c" # -e 开启转义 \c 不换行
echo "It is a test"
输出结果:
OK! It is a test
6.显示结果定向至文件
echo "It is a test" > myfile
7.原样输出字符串,不进行转义或取变量(用单引号)
echo '$name\"'
输出结果:
$name\"
8.显示命令执行结果
echo `date`
注意: 这里使用的是反引号 `, 而不是单引号 '。
结果将显示当前日期
Thu Jul 24 10:08:46 CST 2014