顺序执行
选择执行
循环执行正则表达式
顺序执行则是最简单的流程,按照输入指令的顺序逐条执行shell
就是根据一些判断的语句,选择性的执行某些分支命令,不执行某些命令。例:if语句;case语句编程
根据一些条件来判断,是执行true执行true那一部分,是false则退出循环语句。数组
If选择语句分为两种,一种就是单分支if,一种就是多分支语句。If语句支持嵌套。bash
语法格式
if 条件 ;then
命令块1(能够是多个命令)
else
命令块2(能够是多个命令)
fi
若是符合该条件,则执行命令块1处的命令不执行命令块2处的命令,若是条件不符合,则跳过命令块1处的命令不执行,直接执行命令块2处的命令,条件多为一个判断命令,返回值为0(true)执行命令块1,返回值为1(false)执行命令块2
例如:
if $(id $1 &> /dev/null);then
echo "$1 alread exist"
exit 1
else
useradd $1
fi服务器
语法格式:
If 条件1 ;then
命令块1
elif 条件2 ;then
命令块2
elif 条件3;then
命令块3
……
elif 条件n;then
命令块n
else
命令块n+1
fi
执行顺序是,先判断条件1,返回值为true则执行命令块1,而后退出if语句
若是条件1,返回值为false,则继续判断条件2,返回值为true,则执行命令块2,而后退出if语句,若条件2 返回值为false,则继续判断条件3……,若是全部条件返回 值都为false,则执行else后面的命令块n+1,而后退出if语句
例如:
#!/bin/bash
if [ $1 -lt 3 ];then
echo redhat
elif [ $1 -eq 3 ];then
echo green
elif [ $1 -gt 3 -a $1 -lt 5 ];then
echo yellow
else
echo white
fiapp
case条件判断语句是一个多分支结构的,适合用于分支多的状况,并且比if多分支语句更加简洁一点,可是case语句不可嵌套。ssh
case 变量 in
变量值1)命令块1;;
变量值2)命令块2;;
变量值4)命令块3;;
变量值n)命令块n;;
esac
执行顺序是,先调用一个变量,而后判断变量值是否符合变量值1,若是相等,则执行命令块1,若是不符合则判断变量值2,……一直到变量值n,若是都不符合则则什么都不执行或执行*后面的操做,*表明除了上面值全部值
例如:
case $1 in
1)echo redhat;;
2)echo yellow;;
3)echo green;;
*)echo blue;;
esac 编程语言
循环执行的特色:
将某代码段重复运行屡次
重复运行多少次:
循环次数事先已知
循环次数事先未知
有进入条件和退出条件ide
for循环语句的格式1:
for 变量名 in 变量值列表;do
循环体
done
执行机制:依次将列表中的元素赋值给“变量名”;每次赋值后即执行一次循环体;直到列表中的元素耗尽,循环结束,for循环是先赋值在判断,
列表生成方式:
(a){start..end}
(b) $(seq [start [step]] end)
$(cmd)
$@,$*
while循环语句语法格式:
while 循环控制条件 ;do
循环体
done
执行机制:进入循环以前,先作一次判断;每一次循环以后会再作判断;条件为true则执行一次循环;知道条件测试状态为false终止循环。所以循环控制条件通常应该有变量,而变量的值会再循环体中发生变化,并且变量的第一次赋值应该在循环的前面,不然没法进行判断,
使用while建立无限循环
while true;do
循环体
done
until循环语句格式:
until 循环控制条件;do
循环体
done
执行机制:until跟while语法格式差很少,并且都是先判断再循环,不一样的是while是条件测试返回值为真进入循环,为假则退出循环,而until则是条件测试返回值为假则进入循环,为真时退出循环
使用unti建立无限循环
until false;do
循环体
done
Continue用于循环体中用于提早结束本轮的循环,再也不执行后续的命令,而直接进入下一轮循环,而不是将整个循环体都退出,好比在循环体内写入某一个条件,当知足这一条件是,执行continue这个命令,就不会再执行循环体内continue后面的那些命令了,而后直接进入下一轮的循环,一直到循环结束。
示例:编写一个脚本shuzi.sh,输出1到10 的数字内容以下:
如图所示,脚本中输出1到10的数字,可是当在第5轮的循环中,符合条件i等于5将会执行continue命令,而本轮循环中后面的echo $i就不会再执行了,而是退出这第五轮的循环,直接进入下一轮循环。因此这1到10 的数字中不会输出5输出结果以下图:
Break用于循环体内,用退出整个循环体,再也不执行后续的循环,好比原本须要执行10次循环的,但当符合第5层循环时,符合某以条件而执行了break命令,那么就会退出当前的循环体,然后续的循环也再也不执行了。
例:编写一个脚本shuzi2.sh,输出1到10的数字
如图所示,脚本中依旧是输出1到10的数字,不一样的就是,当i等于5时执行break命令,如此就会退出这个循环,而不输出5及以后的全部数字,执行结果以下:
在嵌套循环中,也就是多层循环体中break仅退出包含break命令的当前循环,而不会退出其余的循环体或本层循环的上一层循环。
例:编写一个脚本qt.sh,打印九九乘法表
上图中是有两层循环体的,这就是循环的嵌套使用,循环中包含着一个循环,在这种状况中,break在里面那一层循环中,当符合m不小于i时就会执行break,也就是在第一轮循环中i=1;m=1,而后判断m是否小于等于i ,返回值为true,输出1x1=1;而后执行里面的那个循环的第二轮,m++=2,i=1,判断m是否小于等于i,返回值为false,执行break,退出了当前循环,可是外面那一层循环体并不会退出,会依旧循环执行i++=2,m=1,判断m是否小于等于i,返回值为true,输出1x2=2;而后执行内循环的第二轮循环i=2,m++=2,再次判断m是否小于等于i,返回值为true,输出2x2=4;执行内循环的第三轮,i=2,m++=3,判断m是否小于等于i,返回值为false,执行break,退出内循环,继续外循环的第三轮循环......执行 结果以下图:
区别在于continue是退出某一循环体的某一轮的循环,可是循环体自己不退出,不、妨碍后续的循环,break是果断退出当前的整个循环体,使当循环体再也不循环。
shift用于将参数列表左移指定次数,缺省为左移一次,。参数列表一旦被移动,最左端的那个参数就从列表中删除。While循环遍历位置参数列表时,经常使用到shift
用法:shift [n]
以下图中所示,编写一个脚本,使用while循环显示全部的参数时,若是不使用shift,则参数数量不会发生变化,使其成为了死循环,一直显示全部的参数
以下图中所示,在循环中添加一个语句shift,则会在每次循环时,将参数从左向右移一次,并且移动后,坐左边的那个参数就被删除了,也再也不显示,
示例:显示全部的参数,一次显示一个参数,一个参数占一行
While循环还有一个特殊用法,能够遍历文件的每一行,下面咱们编写一个脚本和一个文件以下图中,使用脚本读取gushi中的每一行美容并显示出来:
而后咱们执行如下看看结果,这个执行步骤就是,依次读取/app/gushi文件中的每一行,且将值赋值给变量line,直到把全部的行都赋值给变量而后才结束循环;这个方法就有点跟for循环类似了,给变量赋多个值,每个值执行一次循环,知道把全部的值读取完循环就结束,
双小括号使用方法,即((...))格式
var=$((算术表达式))
((i++))
for ((控制变量初始值;条件判断表达式;控制变量的修正表达式))
do
循环体
done
控制变量的初始化值:仅在运行到循环的第一轮循环会执行一次,
条件判断表达式:在每一轮循环执行开始以前都会执行一次,这个判断表达式的值为true则执行循环一次,若是值为false,则退出这个循环。
控制变量的修正表达式:每轮循环结束会先进行控制变量修正运算,然后在作条件判断,这里一般会使变量的值发生改变。
示例:以下图图1中所示,第一轮循环i=1,而后输出1,;第二轮开始先给执行i++,而后i的值就是2了,再判断i是否小于等于10,值为true则执行一次循环体输出i的值2;而后进入第三轮循环,先执行i++,这个时候i的值编程了3,在判断i的值是否小于等于10,值为true执行循环体输出i的值3,进入下一轮循环……进入第10轮循环,先执行i++,i的值是10 ,判断i是否小于等于10,值为true,执行循环体输出i的值10;进入第十轮循环,执行i++,i的值为11,判断i是否小于等于10,值为false,退出循环。因此虽然最后只输出到10,可是由于for循环是先执行控制变量的修正表达式,而后在执行判断表达式判断值是真是假再决定是否继续执行循环体,因此循环体结束后i的值已是11了,以下图图2显示,在循环以外再显示一次i的值
Select的特色:
1.Select循环主要用于建立菜单,按数字顺序排列的菜单项将显示在标准错误上,并显示PS3提示符,等待用户输入
2.用户输入菜单列表中的某个数字,执行相应的命令
3.用户输入被保存在内置变量REPLY中
4.Select是个无限循环,所以可使用break命令退出循环,或用exit命令终止脚本,也能够按Ctrl+c退出循环
5.Select常常跟case联合使用
6.与for循环相似,能够省略in list ,此时使用位置参量
7.Select的list的参数值以空格分隔,能够是英文,也能够是中文,如参数中包含有空格,得加双引号
8.Select循环能够嵌套
Select的语法格式:
select 变量名 in list
do
循环体命令
done
示例:
如上图中所示,我编写了一个简单的菜单系统,选择要吃的饭;输入提示符能够更改变量PS3的值,在想要执行变量为其中某个值时要作的事,需输入相应的序号,当输入一个没有的序号或其余字符时则输出一行空行继续循环,并且这个循环一旦执行是不能退出的,输入exit或quit都没有用,要退出仍是得Ctrl+c强制退出。
以前学过进程管理的应该都知道kill命令,进程管理就是向进程发送一些控制信号,来完成对进程的管理控制,咱们能够经过kil -l或trap -l来查看当前系统可用的信号
经常使用的信号有:
SIGHUP:1 通知进程重读配置文件以让新的配置生效,无需从新启动进程
SIGINT:2终止正在运行中的进程,至关于键盘组合键Ctrl+c
SIGQUIT:3 至关于Ctrl+\
SIGKILL:9 强行终止正在运行中的进程
SIGTREM:15 强行终止正在运行中的进程
SIGSTOP:19 暂停前台的进程,使其在后台休眠,至关于ctrl+z
SIGCONT:18 继续运行指定的进程
信号的表示方法:
1.完整名称,例如:SIGINT
2.简写名称,例如:INT
3.数字表示,例如:2
Trap的做用就在于可以捕捉这些信号,使在使用这些信号对进程进行管理时,不执行原操做,而执行咱们所指定的操做,好比在执行一个脚本时按Ctrl+c或者执行命令kill -19会终止或中断这个脚本的运行,可是我想屏蔽掉这个信号,使其在运行过程当中这个进程是杀不死的,trap就能够实现这个功能,可是有一个信号没法屏蔽,那就是9 SIGKILL,这个是强制性的终止进程,其它的是能够屏蔽掉的。
Trap的语法:
trap ‘触发指令’ 信号
自定义进程收到系统发出的指定信号后,将执行触发指令,而不会执行原操做,当单引号内为空什么都没有时,就是什么都不作,仅忽略那个信号。
trap “ ” 信号 :忽略信号的操做
trap “ - ” 信号 :恢复信号的操做
trap -p :列出自定义信号操做
每隔0.5秒输出一个数字,忽略信号int,也就是Ctrl+c这个信号,前10个数字忽略输出“根本就停不下来”,10至20什么都不作,20至30之间恢复信号的操做
函数function是由若干条shell命令组成的语句块,实现代码重用和模块化编程
它与shell程序形式上是类似的,不一样的是它不是一个单独的进程,不能独立运行,而是shell程序的一部分
函数和shell程序比较类似,区别在于:
Shell程序在子shell中运行
而shell函数在当前shell中运行,所以在当前shell中,函数能够对shell中变量进行修改
Shell中的代码都是被逐行读取并运行的,而函数部分的命令在被读取的时候是不会被运行的,只有在调用函数的名字时,才会运行已经定义好的函数中的那一部分代码,适用于当某一段语句块须要屡次运行的状况下,这些命令就须要屡次被读取执行,而调用函数只用编写一遍,后面须要用到的地方调用函数名便可,这样节省咱们编写shell脚本的时间
函数由两部分组成:函数名和函数体
函数名部分是用来调用函数体部分的命令
函数体部分就是由须要被屡次执行的命令语句块组合成的
语法一:
function_name (){
...函数体...
}
语法二:
funtion function_name {
...函数体...
}
语法三:
function function_name () {
...函数体...
}
Function_name :函数名
{}中间部分就是函数体,在调用函数名的时候回被执行的命令块
示例:使用函数编写一个脚本,计算从1加到100的和
函数的定义和使用:
可在交互式环境下定义函数
可将函数放在脚本文件中做为它的一部分
可放在只包含函数的单独文件中
函数的调用:
调用:给定函数名
函数名出现的地方,会被自动替换为函数代码
函数的生命周期:被调用时建立,返回时终止
函数有两种返回值:
使用echo等命令进行输出
函数体中调用命令的输出结果
默认取决于函数中执行的最后一条命令的退出状态码
自定义退出状态码,其格式为:
return 从函数中返回,用最后状态命令决定返回值
return 0 无错误返回
return 1-255 有错误返回
函数的执行结果返回值就是函数体内的全部命令的执行结果的输出结果
函数的退出码就是执行完函数后,返回的一个值,就跟在终端上执行的每个命令都会有一个返回值同样,执行的命令为0,说明这个命令是正确的,并执行成功了,为除了0之外的全部值皆为错误,错误的返回值再0-255之间,函数也是这样,在函数中没有指定返回值,那么这个函数的返回值皆由这个函数的函数体内的最后一条命令的返回值为函数的返回值,要若是指定函数的返回值,就在函数体内最后一行编辑return命令,自定义函数返回值,return命令必定要在最后一行,由于函数中读取到return命令后,那么其后面的命令就不不会再执行,而后退出函数,函数中return命令就好像脚本的exit命令会退出这个脚本,return命令会退出这个函数,可是函数外的命令依旧会执行。
示例:
交互式环境下定义函数就是不在脚本中定义的函数,在终端上定义的函数
语法:
function_name (){
> 函数体
> }
定义函数后,调用函数是在命令行界面输入函数名便可,就会执行函数体部分的全部命令
该函数在定义后将一直保留到用户从系统推出,或执行了卸载函数的命令:
unset 函数名
示例:
函数在使用前必须定义,所以颖将函数定义放在脚本开始部分,直至shell首次发现它后才能使用,调用函数仅使用函数名便可
示例:
能够将常用的函数存入函数文件而后将函数文件载入shell
文件名能够任意选取,但最好与相关任务有某种联系。例如:functions.main
一旦函数文件载入shell,就可在命令行或脚本中调用函数。可使用set命令查看全部定义的函数,其输出列表包括已经载入shell的全部函数
若要改动函数,首先用unset命令从shell中删除函数。改动完毕后,再从新载入此文件
建立函数文件依旧须要在首行编辑/bin/bash
函数文件不须要加执行权限就能够用
一个函数文件中能够定义多个函数,不必定只能有一个,当载入这个函数文件时,这个文件的全部函数都会被载入,能够随时调用使用
函数文件建立好后,要将它载入shell,定位函数文件并载入shell的格式:
. Filename 或 source filenname
注意:<点><空格><文件名> :这里的文件名要带正确路径
示例:
使用set命令检查函数是否已载入,set命令将在shell中显示全部的载入函数
要执行函数,简单的键入函数名便可
对函数作一些改动后,须要先删除函数,使其对shell不可用,使用unset命令完成删除函数,使用unset后,再键入set命令,该函数将再也不显示
命令格式:unset 函数名
环境函数:
在当前shell中定义的函数,在子bash中是不可用,想要这个函数在其子进程中也可用能够在定义函数后再使用export -f 函数名,声明全局函数
声明:export -f 函数名
查看:export -f 或declare
示例:
示例:
函数变量就是在函数中定义的变量
变量做用域:
环境变量:当前shell和子shell有效
本地变量:只在当前shell进程有效,为执行脚本会启动专用子shell进程;所以,本地变量的做用范围是当前shell脚本程序文件,包括脚本中的函数
局部变量:函数的生命周期;函数结束时变量被自动销毁
注意:若是函数中有局部变量,若是其名称同本地变量,使用局部变量
在函数中定义局部变量的办法
local NAME=VALUE
示例:
函数内是能够本身调用其余的函数或函数自己,
函数递归:
函数直接或间接调用自身
注意递归层数
示例:利用函数达成无限循环的目的
Fork×××是一种恶意程序,它的内部是一个不断在fork进程的无限循环,是指是一个简单的递归程序,因为程序时递归地,若是没有任何限制,这会致使这个简单的程序迅速耗尽系统里面的全部资源
函数实现
:(){ :|:& };:
Bomb() { bomb|bomb $ }; bomb
脚本实现
#!/bin/bash
./$0|./$0&
示例:
数组是计算机编程语言上,是用于存储多个相同类型数据的集合,把有限个类型相同的变量用一个名字命名,而后用编号区分它们的变量的集合,这个名字称为数组名,编号称为它们的下标,组成数组的各个变量为数组的元素,数组是在程序设计中为了处理方便,把具备相同类型的若干变量按有序的形式组织起来的一种形式,这些按序列排列的同类数据元素的集合称为数组,
变量:存储单个元素的空间
数组:存储多个元素的连续的内存空间,至关于多个变量的集合
索引数组:从0 开始,属于数值索引,下标是数字
关联数组:可支持使用自定义的格式,下标也能够是字符串
注意:数组的下标需放在中括号内
变量需先声明在赋值使用
declare -x :声明或显示环境变量和函数
declare -g :设置函数为全局函数
declare -xf :设置环境函数
declare -a 数组名 :声明索引数组
declare -A 数组名: 声明关联数组
declare -a :查看全部索引数组
declare -A :查看全部关联数组
注意:索引数组和关联数组二者不可相互转换,声明索引数组时,能够省略declare -a,直接赋值就可以使用;声明关联数组时,必须使用declare -A声明,不然没法使用
title[0]=1 : 数组名是title,下标是0,这个元素的值是1
title[1]=2 : 数组名是title,下标是1,这个元素的值是2
title=(1 2 3 4 5) :数组名是title,默认是索引数组,因此下标从0开始,括号内的值分别赋值给title的每一个元素,这种方法适用于元素的多个下标连续的状况下
title=([0]=pig [1]=big [3]=bird [5]=fish) :当想要一次性给数组的多个元素赋值可是下标又不连续时
title=({1..10}) :给数组10个元素赋10个连续的值
title=(/root*.sh) :文件名可使用文件通配符
title=($(echo {1..10})) :把命令的结果赋值给元素时命令必须放在$()中
read -a title :使用read命令手动输入元素的值,以只能赋一个元的值,title是数组名,并且这个值只能赋值下标0这个元素
declare -a :查看全部已声明的数组
declare -a |grep 数组名 :查看某一已声明的索引数组及其全部值
declare -A :查看全部已声明的关联数组
declare -A |grep 数组名 :查看某一已声明的关联数组及其全部值
echo $title :不输入下标,仅引用数组的下标为0的值
echo ${title[n]} :引用指定下标的元素,下标为n
echo ${title[*]} :显示数组的全部值
echo ${title[@]} :显示数组的全部值
echo ${#title[*]} :显示数组总共有多少个元素
echo ${title[${#title[*]}-1]} :显示数组的最后一个值(这个数组必须是索引数组,并且下标是连续的数值)
echo ${test[@]} :显示数组的每个值,以空格分隔
unset 数组名:删除这个数组的全部值
unset 数组名[下标] :删除这个数组的其中一个值,致使稀疏模式
数组切片就是数组中有不少个值嘛,想要取出其中几个连续的值。
示例:
test[${#test[*]}]=cat :给索引数组的加值,加到最后一个索引的后面,(前提是这个数组的索引必须是连续无空缺的)
关联数组赋值前必须先声明,不然会默认为时索引数组调用时会出错
方法一:先声明再赋值
declare -A 数组名 :声明一个关联数组,可是先不赋值
数组名[下标]=元素值 :给这个关联数组其中一个元素赋值
Array_name=([idx_name1]=’val1’ [idx_name2]=’val2’...) :一次性给多个下标赋值,(array_name:数组名;idx_name:数组下标;val:元素值)
方法二:声明并赋值
declare -A 数组名[下标]= 元素值 :声明一个关联数组,并给一个元素赋值
declare -A Array_name=([idx_name1]=’val1’ [idx_name2]=’val2’...) :声明一个数组并同时给数组的多个元素赋值
示例:
声明10 个随机数保存于数组中,并找出其最大值和最小值
${#var}:返回字符串变量var的长度
${var:offset}:返回字符串变量var中从第offset个字符后(不包括第offset个字符)的字符开始,到最后的部分,offset的取值在0到${#var}-1之间,(bash4.2后,容许负值)
${var:offset:number}:返回字符串变量var中从第offset个字符后 (不包括第offset个字符)的字符开始,长度为number的部分
${var: -length}:取字符串的最右侧几个字符 (注意:冒号后必须有一空白字符)
${var:offset:-length}:从最左侧跳过offset字符,一直向右取到 距离最右侧lengh个字符以前的内容
${var: -length:-offset}:先从最右侧向左取到length个字符开始 ,再向右取到距离最右侧第offset个字符之间的内容(注意:-length前空格)
${var#*word}:其中word能够是指定的任意字符
功能:自左而右,查找var变量所存储的字符串中,第一 次出现的word, 删除字符串开头至第一次出现word字符之间的 全部字符
${var##*word}:同上,贪婪模式,不一样的是,删除的 是字符串开头至最后一次由word指定的字符之间的全部内容
${var%word*}:其中word能够是指定的任意字符;
功能:自右而左,查找var变量所存储的字符串中,第一 次出现的word, 删除字符串最后一个字符向左至第一次出现 word字符之间的全部字符;
${var%%word*}:同上,只不过删除字符串最右侧的字符向 左至最后一次出现word字符之间的全部字符;
${var/pattern/substr}:查找var所表示的字符串中,第 一次被pattern所匹配到的字符串,以substr替换之
${var//pattern/substr}: 查找var所表示的字符串中, 全部能被pattern所匹配到的字符串,以substr替换之
${var/#pattern/substr}:查找var所表示的字符串中, 行首被pattern所匹配到的字符串,以substr替换之
${var/%pattern/substr}:查找var所表示的字符串中, 行尾被pattern所匹配到的字符串,以substr替换之
${var/pattern}:删除var所表示的字符串中第一次被 pattern所匹配到的字符串
${var//pattern}:删除var所表示的字符串中全部被 pattern所匹配到的字符串
${var/#pattern}:删除var所表示的字符串中全部以 pattern为行首所匹配到的字符串
${var/%pattern}:删除var所表示的字符串中全部以 pattern为行尾所匹配到的字符串
${var^^}:把var中的全部小写字母转换为大写
${var,,}:把var中的全部大写字母转换为小写
Shell变量通常是无类型的,可是bash shell提供了declare和typeset两个命令用于指定变量的累心,两个命令是等价的
变量能够先声明变量名,而后再赋值,也能够声明时在其后面同时赋值
使用declare或typeset命令声明变量为某一类型后
仅declare命令做用是查看全部函数
语法:declare [选项] 变量名[=value]
选项:
-r:声明或显示只读变量(只读变量声明赋值后,值不可更改,unset命令也不可删除)
-i:将变量定义为整型数
-a:将变量定义为索引数组
-A:将变量定义为关联数组
-f:显示已定义的全部函数名及其内容
-F:仅显示已定义的全部函数名
-x:声明或显示环境变量和函数
-l:声明变量为小写字母 declare -l var=UPPER (使用该选项声明变量后,不管给变量赋的值是大写仍是小写字母都会被转换为小写字母)
-u:声明变量为大写字母 declare -l var lower (同上-l选项)
${var:-value}或${var-value}:若是变量var为空或为设置,那么返回value;不然返回var的值
${var:+value}:若是var非空,value,不然返回空值
${var:=value}:若是var为空或未设置,那么返回value,并将value赋值给var,不然返回var的值
${var:?value}:若是var为空或未设置,那么在当前终端打印value;不然返回var的值
为脚本程序使用配置文件,实现变量赋值:
定义文本文件,每行定义“name=value”
在脚本中source此文件便可
eval命令将会首先扫描命令行进行全部的置换,而后再执行该命令。该命令适用于那些一次扫描没法实现其功能的变量,该命令对变量进行两次扫描
这个解释不太容易理解,就是说,有一个变量的值是一条shell中的命令,那么在调用这个变量时,并非想调用变量自己的值,而是调用变量的值的那一条命令的结果,eval即可以实现这个功能,
好比给变量a赋值为whoami这个命令:a=whoami;在这种状况下a的值就是whoami这串字符串,可是whoami又是shell中一个命令,我想要调用a的值的时候是这条命令的显示结果,而不是这串字符串,就可使用eval命令
可是eval只能转换一层,不能多层转换,好比,a=b;b=whoami,使用eval调用b的值是能够调出whoami的显示结果,可是调用a时却不能再一层调用为whoami的结果
示例:
间接变量引用:若是第一个变量的值是第二个变量的名字,从第一个变量引用第二个变量的值就称为间接变量引用
好比:var1的值是var2,而var2又是变量名,var2的值为value,间接变量引用是指经过var1得到变量值value的行为
var1=var2
var2=value
Bash shell提供了三种格式实现间接变量引用
eval echo \$$var1
echo ${!var1}
eval echo ${!var1}
示例:
引用当前bash的进程号:echo $$
在编辑脚本时,在脚本中有时还须要穿件一些其余文件供读取使用,或者存放一些输出的信息,建立文件时就可能出现文件名存在冲突,
或者一个程序同时被多个终端多个用户访问时,临时文件名若是同样的话,将会没法同时被编辑使用,因此临时文件最好使用mktemp命令,建立随机文件名的临时文件,可避免冲突
mktemp:建立并显示临时文件;
仅mktemp命令建立的临时文件保存在/tmp下以tmp开头,后缀是10个字符的随机字母或数字,
语法:mktemp [option] [template]
option:
-d:建立临时目录;(文件名也能够包括其绝对路径)
-p DIR或--tmpdir=DIR:指明临时文件所存放目录位置,建立临时文件
template:filename.XXX
X至少要出现3个,X表明的是一个随机大小写字母或数字,根据本身要建立的临时文件名的长度肯定多少个X
示例:
Install命令是安装或升级软件或备份数据,它的使用权限是全部用户。Install命令和cp米宁雷士,均可将文件目录拷贝至指定的路径下,可是install比cp命令更强大的一点就是它能够容许你控制目标文件的属性,install一般用于程序的Makefile,使用它来说程序拷贝到目标(安装)目录
语法:install [option] [参数] source dest :将源文件source拷贝至目标目录dest
option:
-d 或 --directory:将后面全部的参数都做为目录处理,而后建立指定目录的全部主目录
-D source dest:当目标文件目录dest不存在时,会自动递归建立建立目标目录,并源文件拷贝至目标文件
-g 组名 或 --group=GROUP:定义复制后的目标文件的所属组
-o 用户名 或 --owner=OWNER :定义复制后的目标文件的所属主
-m 权限 或 --mode=MODE:定义复制后的目标文件的权限(默认755)
-p :以源文件的文件访问、修改时间做为相应的目标文件的时间属性
-v :显示复制的过程
-t dest_dir source :将source文件件拷贝至dest_dir目录下,dest_dir必须是个已存在的目录,source能够是单个或多个文件文件,单不能是目录,不能拷贝整个目录下的全部文件
-T source dest;单文件传输,将文件source拷贝至dest,并将文件名更改成目标文件名,source只能是一个文件,不能是目录,dest是目标文件,不能是目录,并且目标文件若是已存在,则会以source文件的内容覆盖目标文件,若是目标文件不存在,则目标文件所在的目录必须存在
注意:当install命令什么选项都不带的状况下,语法为:install source dest;目标文件的默认权限为755,更改时间是当前时间,如目标目录不存在不会递归建立。
当源文件source是单个文件,dest是目录时,将源文件复制到dest目录下,dest不能是未存在目录
当源文件source是单个文件,dest是已存在的文件时,则将source的内容覆盖至dest文件中去
当源文件source是单个文件,dest是未存在的文件时,则dest文件的目录必须存在,而后会在dest的目录下建立一个与dest文件同名的文件,并将source的内容覆盖进去
当源文件是多个文件时,dest必须是一个已存在的目录
Expect是由Don Libes基于Tcl(Tool Command Language)语言开发的,主要应用于自动化交互式才作的场景,借助expect处理交互式的命令,能够将交互式过程:ssh登陆,ftp登陆等写在一个脚本上,使之自动化完成。尤为适用于须要对多台服务器执行相同操做的环境中,能够大大提升系统管理人员的工做效率
要使用expect必须先安装软件才可使用,包名就是expect,这个包还依赖于tcl包
expect [选项] [-c cmds] [[-f|b] cmdfile] [args]
选项
-c :聪明航执行expect脚本,默认expect是交互地执行的
示例:expect -c ‘expect “\n” {send “pressed enter\n”}’
-d :不执行,仅输出调试信息
示例:expect -d ssh.exp
send:用于向进程发送字符串
expect:从晋城接收字符串
spawn:启动新的进程
Interact:容许用户交互
exp_continue:匹配多个字符串在执行动做后加此命令
send命令接收一个字符串参数,并将该参数发送到进程
示例:send “hello there!\n” 输出hello there
Expect命令和send命令正好相反,expect一般是用来等待一个进程的反馈。 Expect能够接收一个字符串参数,也能够接收正则表达式参数。和上文的send命令结合
示例:
expect “hi\n” {send “you said hi\n”} : 当在标准输入中输入hi\n的时候,输出you said hi\n,(\n:换行符)
expect “hi\n” {send "you ";send “said\n”} : 当在标准输入中输入hi\n时,输出you said;(大括号内能够有多个命令以分号隔开,当标准输入或输出中没有空格符号时,能够不加双引号)
Expect最经常使用的语法是来自tcl语言的模式-动做。这种语法及其灵活。
单一分支式语法:
expect “hi\n” {send “you said hi\n”} :匹配到hi时,会输出you said hi
多分支模式语法:
expect “hi\n” {send “you said hi\n”} \
“hello” {send “hello yourself\n”} \
“bye” {send “good bye\n”}
匹配到hi、hello、bye任意一个字符时,执行相应的输出,等同于以下写法:
expect {
“hi\n” {send “you said hi\n”}
“hello” {send “hello yourself\n”}
“bye” {send “good bye\n”}
}
Spawn:上文中的全部演示都是和标准输入输出进行交互,可是咱们更但愿他能够和某一个进程进行交互,Spawn命令就是用来启动新的进程的,Spawn后send和expect命令都是和spawn打开的进程进行交互
示例:
注意:\r:\r的做用跟\n同样,表示换行符
到如今为止,咱们已经可结合spawn、expect、send自动化的完成不少任务了,可是,如何让人在适当的时候干预这个过程了。好比下载完ftp文件时,扔能够停留在ftp命令行状态,以便手动的执行后续命令。Interact能够达到这些目的,
示例:
在expect多分支模式下,虽有多个匹配的字符,可是匹配到哪个分支的字符,才执行后面相应的动做,而后其余的都没用了;若是想要匹配完上一个字符串执行过动做后继续匹配下一个字符串执行动做,就在动做后加上exp_continue,能够继续执行下面的匹配,相比较写多个单分支模式来实现这个功能,语句简便了许多
示例:
直接输入expect命令,进入expect模式,能够在这个模式下执行expect脚本,exit命令退出这个模式
示例:
使用-c选项,从命令行执行expect脚本,默认expect是交互地执行
示例:
编写expect脚本时,其后缀名必须是.exp;开头的沙邦必须为#!/usr/bin/expect
示例:
在shell脚本中使用expect命令,脚本后缀名依然是.sh;开头是#!/bin/bash;其余命令都正常使用,只有交互部分放在expect \<\<EOF与EOF之间
示例:
就是已经编辑好了的expect须要早另外一个shell脚本中运行执行这个expect脚本
如须要执行expect脚本的绝对路径为/app/lx/login.exp;那么就在shell脚本中编辑命令expect /app/lx/login.exp;在expect命令后跟expect脚本的绝对路径便可,后面也能够跟参数,例如:expect /app/lx/login.exp a b c;
示例:调用login.exp进行批量管理,给多台服务器建立用户
第一步:编写expect脚本
第二步:编辑存储IP和用户密码的文件
第三步:编辑shell脚本,调用执行expect自动化脚本
第四步:执行shell脚本
在expect脚本中定义变量与shell中不一样,需使用set命令;语法格式以下:
set 变量名 值
例:set var abcd 给变量var赋值为abcd,变量名和值之间没有等于号
调用变量跟shell脚本中同样在变量名前加上$;例:$var
超时时间timeout是一个固定的变量,它是用来设置超时时间的,就是在执行完这个expect脚本后,再过多少秒后退出这个脚本,timeout为-1时为永不超时,例如设置超时为10秒,命令为set timeout 10 ;单位是秒。
Expect脚本能够接受从bash传递过来的参数,这些参数都存储在数组argv中,可使用[lindex $argv n]得到,n从0开始,分别是第一个,第二个,第三个...参数;这种参数是位置参数
另外expect的命令行参数参考了c语言的,与bash shell有点不同,argc存储了参数的个数;argv存储了全部的参数,argv0存储了脚本的名字,[lrange $argv 0 0]表示第一个参数,[lrange $argv 0 4]表示第一个参数到第五个参数
示例:
结束符expect eof 的做用是当执行到这一条命令时,超时时间为多少秒,就停顿多少秒后再执行后续的expect语句,停顿期间任何命令也不能执行,用户也不能交互式,输入任何字符都没有反应,就至关于shell脚本中sleep命令,停顿多少秒后继续运行后续命令
退出expect脚本的命令依旧是exit,exit命令后续的全部命令将再也不执行
示例:登陆成功后,由于timeout是-1,永不超时,因此会一直停顿,
当使用expect匹配一个字符串时,后面相对应的能够执行多个send语句的,这种状况下就是当连续的多个expect匹配的字符串相同,然后面的命令不一样时,
示例: