shell
变量
在bash shell中,环境变量分为两类
- 全局环境变量
- 局部变量
全局变量
系统变量基本上都是使用全大写字母,以区别于用户的环境变量
全局环境变量对于shell会话和所有生成的子shell都是可见的。局部变量则只对创建它们的shell可见。
查看全局变量,可以使用env或printenv命令
要显示个别环境变量的值,可以使用printenv,但是不要用env命令
[root@localhost ~]# printenv HOME
/root
[root@localhost ~]# env HOME
env: HOME: 没有那个文件或目录
局部环境变量
局部环境只能在定义它们的进程中可见。尽管它们是局部的,但是和全局环境变量一样重要。
set命令会显示为某个特定进程设置的所有环境变量,包括局部变量、全局变量以及用户定义变量和函数。
命令env、printenv和set之间的差异和细微。set命令会显示出全局变量、局部变量以及用户变量定义。它还会按照字母顺序对结果进行排序。env和printenv命令同set命令的区别在于前两个命令不会对变量排序,也不会输出全局变量和用户定义变量。
用户变量可以是任何字母、数字或下划线组成的文本字符串,长度不超过20个。用户变量区分大小写。
如果要给变量赋给一个含有空格的字符串值,必须单引号来界定字符串的首和尾,变量名、等号和值之间没有空格,这一点非常重要。如果在赋值表达式中加上了空格,bash shell就会把值当成一个单独的命令;
引用变量
- 可以通过${variable}形式引用变量
- 可以在变量名前加上美元符“$”
${} 来避免一些变量问题
[root@localhost ~]# echo "MySHELL=>$SHELLCode<="
MySHELL=><=
[root@localhost ~]# echo "MySHELL=>${SHELL}Code<="
MySHELL=>/bin/bashCode<=
父shell设置但并未导出的变量都是局部变量。子shell无法继承局部变量。
删除环境变量
a=abc
echo $a
abc
unset a
echo $a
数组变量
数组变量是能够存储多个值的变量。这些值可以单独引用,也可以作为整个数组来引用。
要给某个环境变量设置多个值,可以把值放在括号里,值于值之间用空格分隔。
[root@localhost ~]# mytest=(one two three four five)
[root@localhost ~]# echo $mytest
one
只有数组的第一个值显示出来了。要引用一个单独的数组元素,就必须用代表它在数组中位置的数值索引值。索引值要用方括号括起来。
[root@localhost ~]# echo ${mytest[2]}
three
环境变量数组的索引值都是从零开始。这通常会带来一些困惑。
要显示整个数组变量,可用星号作为通配符放在索引值的位置。
[root@localhost ~]# echo ${mytest[*]}
one two three four five
#也可以改变某个索引位置的值。
[root@localhost ~]# mytest[2]=seven
[root@localhost ~]# echo ${mytest[2]}
aaa
#甚至能用unset命令删除数组中的某个值,但是该索引为空。
[root@localhost ~]# unset mytest[2]
[root@localhost ~]# echo ${mytest[*]}
one two four five
bash shell 命令行参数
bash shell程序可使用命令行参数修改shell启动方式
参数 | 描述 |
---|---|
-c string | 从string中读取命令并进行处理 |
-i | 启动一个能够接受用户输入的交互shell |
-l | 以登录shell的形式启动 |
-r | 启动一个受限shell,用户会被限制在默认目录中 |
-s | 从标准输入中读取命令 |
linux 退出状态码
shell中运行的每个命令都使用退出状态码(exit status)告诉shell它已经运行完毕。退出状态码是一个0~255的整数值,在命令结束运行时由命令传给shell。可以捕获这个值并在脚本中使用
状态码 | 描述 |
---|---|
0 | 命令成功结束 |
1 | 一般性未知错误 |
2 | 不适合的shell命令 |
126 | 命令不可执行 |
127 | 没找到命令 |
128 | 无效的退出参数 |
128+x | 与linux信号x相关的严重错误 |
130 | 通过ctrl+c终止的命令 |
255 | 正常范围之外的退出状态码 |
exit 命令允许你在脚本结束时指定一个退出码,退出状态码是0~255的区间。
引号类型特殊符号
符号 | 解释说明 |
---|---|
‘ ’ (单引号) | 所见即所得,即输入单引号内容会将单引号内的所有内容都原样输出;或者描述为单引号里面看到的是什么就会输出什么,称为强引用 |
“ ”(双引号) | 输出双引号内的所有内容时,如果内容中有变量,特殊字符等信息;会将变量、特殊字符等信息解析出结果,然后再输出,称为弱引用 |
``(反引号) | ⼀般⽤于引⽤命令,执⾏的时候命令会被执⾏,相当于"$()" 赋值和输出都要将命令⽤反引号引起来 |
进程列表
进程列表
$(pwd ; ls ; cd etc )
$() 使命令列表变成了进程列表,生成一个子shell来执行对应的命令。
进程列表是一种命令分组( command greouping )。另一种命令分组是将命令放入花括号中,并在命令列表尾部加上分号’;’ 。语法为{ command; }。使用花括号进行命令分组并不像进程列表那样创建出子shell。
比较
数值比较
比较 | 描述 |
---|---|
n -eq m | 检查n是否与m相等 |
n -ge m | 检查n是否大于或等与m |
n -gt m | 检查n是否大于m |
n -le m | 检查n是否小于或等于m |
n -lt m | 检查n是否小于m |
n -ne m | 检查n是否不等于m |
字符串比较
比较 | 描述 |
---|---|
n = m | 检查n是否和m相同 |
n != m | 检查n是否和m不同 |
n < m | 检查n是否比m小 |
n > m | 检查n是否比m大 |
-n n | 检查n的长度是否非0 |
-z n | 检查n的长度是否为0 |
- 大于号和小于号必须转义,否则shell 会把它们当作重定向符号,把字符串值当作文件名;
- 大于和小于顺序和sort 命令所采用的不同,字符串比较中使用的是标准的ASCII顺序,根据每个字符的ASCII数值来决定排序结果。sort命令使用的是系统的本地化语言设置中定义的排序顺序。对于英语,本地化设置指定了在排序顺序中小写字母出现在大写字母前。
文件比较
比较 | 描述 |
---|---|
-d file | 检查file是否存在并是一个目录 |
-e file | 检查file是否存在 |
-f file | 检查file是否存在并是一个文件 |
-r file | 检查file是否存在并可读 |
-s file | 检查file是否存在并非空 |
-w file | 检查file是否存在并可写 |
-x file | 检查file是否存在并可执行 |
-O file | 检查file是否存在并属当前用户所有 |
-G file | 检查file是否存在并且默认组与当前用户相同 |
file1 -nt file2 | 检查file1 是否比file2 新 |
file1 -ot file2 | 检查file1是否比file2旧 |
以上比较可以用test 命令进行
expr命令
expr命令能够识别少数的数学和字符串操作符
操作符 | 描述 |
---|---|
ARG1 | ARG2 | 如果ARG1既不是null也不是零值,返回ARG1,否则返回ARG2 |
ARG1 & ARG2 | 如果没有参数是null或零值,返回ARG1,否则返回0 |
ARG1 < ARG2 | 如果ARG1小于ARG2,返回1,否则返回0 |
ARG1 <= ARG2 | 如果ARG1小于等于ARG2,返回1;否则返回0 |
ARG1 = ARG2 | 如果ARG1等于ARG2,返回1;否则返回0 |
ARG1 != ARG2 | 如果ARG1不等于ARG2,返回1,否则返回0 |
ARG1 >= ARG2 | 如果ARG1大于或等于ARG2,返回1;否则返回0 |
ARG1 > ARG2 | 如果ARG1大于ARG2,返回1;否则返回0 |
ARG1 + ARG2 | 返回ARG1和ARG2的算术运算和 |
ARG1 - ARG2 | 返回ARG1和ARG2的算数运算差 |
ARG1 * ARG2 | 返回ARG1和ARG2的算数乘积 |
ARG1 / ARG2 | 返回ARG1和ARGG2除的算术商 |
ARG1 & ARG2 | 返回ARG1被ARG2除的算术余数 |
STRING : REGEXP | 如果REGEXP匹配到了STRING中的某个模式,返回该模式匹配 |
match STRING REGEXP | 如果REGEXP匹配到了STRING中的某个模式,返回该模式匹配 |
substr STRING POS LENGTH | 返回起始位置为POS(从1开始计数)、长度为LENGTH个字符的子字符串 |
index STRING CHARS | 返回在STRING中找到CHARS字符串的位置;否则,返回0 |
length | 返回字符串STRING的数值长度 |
+ TOKEN | 将TOKEN 解释成字符串,即使是个关键字 |
(EXPRESSION) | 返回EXPRESSION的值 |
在bash中,在将一个数学运算结果赋给某个变量时,可以用美元符和方括号“$[ operation ]” 将数学表达式围起来
双括号命令符号
双括号命令的格式
(( expression ))
符号 | 描述 |
---|---|
val++ | 后增 |
val– | 后减 |
++val | 先增 |
–val | 先减 |
! | 逻辑求反 |
~ | 位求反 |
** | 幂运算 |
<< | 左位移 |
>> | 右位移 |
& | 位布尔和 |
| | 位布尔或 |
&& | 逻辑和 |
|| | 逻辑或 |
注意:不需要将双括号中表达式里的大于号转义。
结构化命令
if 语句
if-then 语句,格式如下
if command
then
commands
fi
bash shell 的if 语句会运行if 后面的那个命令。如果该命令的退出状态码是0,位于then部分的命令就会被执行。如果该命令的退出状态码是其他值。then部分的命令不会执行,bash shell 会继续执行脚本中的下一个命令。fi语句用来表示if-then语句到此结束。
另一种形式
if command; then
commands
fi
if-then-else 语句,格式如下
if command
then
commands
else
commands
fi
当if语句中的命令返回退出状态码0时,then部分中的命令会被执行,这跟普通的if-then语句一样。当if语句中的命令返回非零退出状态码时,bash shell 会执行else 部分中的命令。
嵌套if
if command
then
commands
elif command
then
commands
fi
elif 语句提供了另一个要测试的命令,这类似于原始的if语句行。如果elif后命令的退出状态码是0,则bash会执行第二个then语句部分的命令。
在elif语句中,紧跟其后的else语句属于elif代码块。他们并不属于之前的if-then代码块。
bash shell 会依次执行if语句,只有第一个返回退出状态码0的语句中的then部分会被执行。
复合条件测试
if-then语句允许你使用布尔逻辑来组合测试。
[ condition1 ] && [ condition2 ] #第一种
[ condition1 ] || [ condition2 ] #第二种
布尔逻辑是一种能够将可能的返回值简化为TRUE或FALSE的方法。
第一种布尔运算使用AND布尔运算符来组合两个条件。要让then部分的命令执行,两个条件都必须满足。
第二种布尔运算使用OR布尔运算符来组合两个条件。如果其中任意条件为TRUE,then 部分的命令就会执行。
case 命令
case 格式如下:
case variable in
pattern1 | pattern2) commands1;;
pattern3) commands3;;
*) default commands;;
esac
case 命令会将指定的变量与不同模式进行比较。如果变量和模式是匹配的,那么shell会执行为该模式指定的命令。可以通过竖线操作符在一行中分隔出多个模式。星号会捕获所有与已知模式不匹配的值。
for 语句
for 命令的基本格式:
for var in list
do
commands
done
在list 参数中,你需要提供迭代中要用到的一系列值。可以通过几种不同的方法指定列表中的值。
在每次迭代,变量var会包含列表中的当前值,第一次迭代会使用列表中的第一个值,第二次迭代使用第二个值,以此类推,直到列表中的所有值都过一遍。
在do和done语句之间输入的命令可以是一条或多条标准的bash shell命令。在这些命令中,$var变量包含着这次迭代对应的当前列表项中的值。
说明:只要你愿意,也可以将do语句和for语句放在同一行,但必须用分号将其同列表中的值分开:for var in list; do。
for循环假定每个值都是用空格分隔的,如果有包含空格的数据值,就必须用双引号将这些值圈起来。
在某个值两边使用双引号时,shell并不会将双引号当成值的一部分。
字段分隔符
内部字段分隔符(internal field separator):IFS环境变量定义了bash shell用作字段分隔符的一系列字符。默认情况下。bash shell 会将下列字符当作字段分割符:
- 空格
- 制表符
- 换行符
在处理代码变量较大的脚本时,可能在一个地方需要修改IFS的值,然后忽略这次修改,在脚本的其他地方能继续沿用IFS的默认值。一个可参考的安全实践是在改变IFS之前保存原来的IFS值,之后再恢复它。
这样的技术可以这样实现:
IFS.OLD=$IFS
IFS=$'\n'
<在代码中使用新的IFS值>
IFS=$IFS.OLD
这样就保证了在脚本的后续操作中使用的是IFS的默认值。
C语言的for命令
以下是bash中C语言风格的for循环的基本格式。
for (( variable assignment; condition; iteration process ))
for (( a=1, b=10; a<=10; a++, b-- ))
do
echo "$a - $b"
done
while命令
while的基本格式
while test command
do
other commands
done
while命令的关键在于所指定的test command的退出状态码必须随着循环中允许的命令而改变。如果退出状态码不发送变化,while循环就将一直不停的进行下去。只要测试条件成立,while命令就会不停的循环执行定义好的命令。在这些命令中,测试条件中用到的变量必须修改,否则就会陷入无限循环。
while命令允许你在while语句行定义多个测试命令。只有最后一个测试命令的退出状态码会被用来决定什么时候结束循环。注意:每个测试命令都出现在单独的一行上。
until 命令
until格式如下
until test commands
do
other commands
done
until 命令个while 命令工作方式完全相反。until 命令要求你指定一个通常返回非零退出状态码的测试命令。只有测试命令的退出状态码不为0,bash shell 才会执行循环中列出的命令。
嵌套循环
循环语句可以在循环内使用任意类型的命令,包括其他循环命令。这种循环叫做嵌套循环(nested loop)。注意,在使用嵌套循环时,你是迭代中使用迭代,与命令运行的次数时乘积关系。
for (( a = 1; a <= 3; a++ ))
do
echo "starting loop $a"
for (( b = 1; b <= 3; b++ ))
do
echo " Inside loop: $b"
done
done
这种被嵌套的循环(也被称为内部循环,inner loop)会在外部循环的每次迭代种遍历一次它所有的值。
控制循环
有两个命令能够帮助我们控制循环内部的清空。
- break 命令
- continue 命令
break 命令是退出循环的一个简单方法。可以用break命令来退出任意类型的循环。
1 跳出单个循环
for var1 in 1 2 3 4 5 6 7 8 9 10
do
if [ $var1 -eq 5 ]
then
break
fi
echo "iteration number: $var1"
done
echo "the for loop ls completed"
2 跳出内部循环
在处理多个循环时,break命令会自动终止你所在的最内层的循环。
for (( a = 1; a < 4; a++ ))
do
echo "Outer loop: $a"
for (( b = 1; b < 100; b++ ))
do
if [ $b -eq 5 ]
then
break
fi
echo " inner loop: $b"
done
done
3 跳出外部循环
有时你在内部循环,但需要停止外部循环。break 命令接受单个命令行参数值:break n 其中’n’指定了要跳出的循环层级。默认情况下,n 为 1,表明跳出的时当前的循环。如果你将n 设为2,break命令就会停止下一级的外部循环。
continue 命令可以提前中止某次循环中的命令,但并不会完全终止整个循环。可以在循环内部设置shell 不执行命令的条件。
for (( var1 = 1; var1 < 15; var1++ ))
do
if [ $var1 -gt 5 ] && [ $var -lt 10 ]
then
continue
fi
echo "Iteration nimber: $var1"
done
当if-then语句的条件被满足时(值大于5且小于10),shell 会执行continue命令,跳过此次循环中剩余的命令,但整个循环还会继续。当if-then的条件不再被满足时,一切又回到正轨。
和break命令一样,continue命令也允许通过命令行参数指定要继续执行哪一级循环:continue n 其中n定义了要继续的循环层级。
处理循环的输出
在shell 脚本中,你可以对循环的输出使用管道或经行重定向。这可以通过在done或fi 命令之后添加一个处理命令来实现。
for file in /home/*
do
if [ -d "$file" ]
then
echo "$file is a directory"
elif
echo "$file is a file"
fi
done > output.txt
for file in /home/*
do
if [ -d "$file" ]
then
echo "$file is a directory"
elif
echo "$file is a file"
fi
done | more
位置参数
bash shell 会将一些称为位置参数(positional parameter )的特殊变量分配给输入到命令行中的所有参数,这也包括shell 所执行的脚本名称。位置参数变量时标准的数字;$0是程序名,$1是第一个参数,$2 是第二个参数,依次类推,直到第九个参数$9。
每个参数都是用空格分隔的,所有shell会将空格当成两个值的分隔符,要在参数值中包含空格,必须要用引号(单引号或双引号均可)
说明:将文本字符串作为参数传递时,引号并非数据的一部分。它们只是表明数据的起止位置。
如果脚本需要的命令行参数不止9个,你仍然可以处理,但是需要稍微修改一下变量名。在第9个变量之后,你必须在变量数字周围加上花括号,比如${10}。
cat test4.sh
#!/bin/bash
total=$[ ${10} * ${11} ]
echo the tenth parameter is ${10}
echo the eleventh parameter is ${11}
echo the total is $total
./test4.sh 1 2 3 4 5 6 7 8 9 10 11 12
读取脚本名
shift 命令 移动变量
shift命令会根据它们的相对位置来移动命令行参数,默认情况下它会将每个参数变量向左移动一个位置。所以,变量$3 的值会移到$2中,变量$2的值1会移到$1中,而变量$1的值则会被删除(注意,变量$0的值,也就是程序名,不会改变。)
其他特殊变量名称 | 详解 |
---|---|
$# | 表示传递给脚本的参数个数。 |
$0 | 脚本名称,但是$0是带有完整的脚本路径的,如果不想使用带有脚本的完整路径,可使用 basename |
$@ | 所有参数列表,将所有参数当作一个单词保存。 |
$* | 所有参数列表,将所有参数单座一个独立的单词。 |
$$ | 表示当前进程的进程号 |
cat a.sh
#!/bin/bash
echo $0
basename $0
[root@localhost ~]# sh /root/a.sh
/root/a.sh
a.sh
[root@localhost ~]# cat a.sh
#!/bin/bash
for i in "$*";do
echo "$i"
done
for i in "$@";do
echo "$i"
done
[root@localhost ~]# sh a.sh 1 2 3 4
1 2 3 4
1
2
3
read命令 获得用户输入
read 命令从标准输入(键盘)或另一个文件描述符中接受输入。在收到输入后,read命令会将数据放进一个变量。
常用参数 | 含义 |
---|---|
-p | 设置提示信息 |
-t | 限定最长等待时间 |
-s | 输入字符不在屏幕显示 |
-r | 禁用转义符(\) |
-n | 定义输入文本的长度 |
-a | 定义一个数组,以空格为间隔符进行赋值 |
调试脚本
shell 函数
函数是一个脚本代码块,你可以为其命名并在代码中任何位置重用。要在脚本中使用该代码块时,只要使用所起的函数名就行了。
创建函数,有两种格式可以用来在bash 脚本中创建函数
第一种:采用关键字function,后跟分配给该代码的函数名。
function name {
commands
}
name 属性定义了赋予函数的唯一名称。脚本中定义的每个函数都必须有一个唯一的名称。 commands 是构成函数的一条或多条bash shell 命令。 在调用该函数时,bash shell 会按命令在函数中出现的顺序依次执行,就像在普通脚本中一样。
第二种: 这种格式更接近其他编程语言种定义函数的方式。
name() {
commands
}
name 后的空括号
表明正在定义的是一个函数。
列出当前shell下的所有函数
declare -f
declare -f [函数name]
删除函数
unset 函数name
函数名必须是唯一的,如果你重定义了函数,新定义会覆盖原理函数的定义
函数返回值
默认情况下,函数的退出状态码是函数中最后一条命令返回的退出状态码。在函数执行结束后,可以用标准变量$?来确定函数的退出状态码。
由于函数最后一条语句成功,函数的退出状态码就是0,尽管其中有一条命令并没有正常运行。使用函数的默认退出状态码是很危险的。
使用return命令
return命令来退出函数并返回特定的退出状态码。return命令允许指定一个整数来定义函数的退出状态码。
die() {
echo "aaaaa"
cd /etc/
ls
return 0
}
echo "kaishi"
die
echo “$?”
- 记住,函数一结束就取返回值;
- 记住,退出状态码必须是0~255。
如果在用 ? 变量提取函数返回值之前执行了其他命令,函数的返回值就会丢失。记住, ?变量提取函数返回值之前执行了其他命令,函数的返回值就会丢失。记住, ?变量提取函数返回值之前执行了其他命令,函数的返回值就会丢失。记住,?变量会返回执行的最后一条命令的退出状态码。
退出状态码必须小于256,函数的结果必须生成一个小于256的整数值。任何大于256的值都会产生一个错误值。
变量的作用域
作用域是变量可见的区域。函数中定义的变量与普通变量的作用域不同。
函数中使用两种类型的变量:
- 全局变量
- 局部变量
1、全局变量是在shell脚本中任何地方都有效的变量。如果你在脚本的主体部分定义了一个全局变量,那么可以在函数内读取它的值。类似地,如果你在函数内定义了一个全局变量,可以在脚本地主体部分读取它地值。
2、无需在函数中使用全局变量,函数内部使用地任何变量都可以被声明或被声明成局部变量。要实现这一点,只要在变量声明地前面加上local关键字就可以了。
local [变量name]
local 关键字保证了变量只局限在该函数中。如果脚本中在该函数之外有同样名字地变量,那么shell将会保持这两个变量的值是分离的。
声明函数
类似于环境变量,也可以定义环境函数,使子进程也可使用父进程定义的函数
export -f [function_name]
declare -xf [function_name]
向函数传数据参数
如果你试图将该数组变量作为函数参数,函数只会读取数组变量的第一个值
#!/bin/bash
aa=(1 2 3 4 5)
fun() {
echo "$@"
bb=$1
echo "is ${bb[*]}"
}
fun $aa
[root@localhost ~]# sh fun.sh
1
is 1
要解决这个问题,你必须将该数组变量的值分解成单个的值。然后将这些值作为函数参数使用,在函数内部,可以将所有的参数重新组合成一个变量。
#!/bin/bash
fun() {
bb=$@
echo "is ${bb[*]}"
}
aa=(1 2 3 4 5)
fun ${aa[*]}
[root@localhost ~]# sh fun.sh
is 1 2 3 4 5
函数递归和遍历目录
递归就是一个函数在它的函数体内调用它自身。执行递归函数将反复调用其自身,每调用一次就进入新的一层。所以递归要有两个基本要素,结束条件与递推关系。
递归的两个基本要素:
(1)边界条件:确定递归到何时结束,也被称为递归的出口
(2)递归所使用的模式:大问题分解成小问题。递归函数只有具有上述两个条件,才能计算出结果。
#!/bin/bash
cdder() {
local caco=$1
local cato=`ls $1`
for i in $cato
do
if [ -d "$i" ] && [ "$i" != '.' ] && [ "$i" != '..' ];then
cdder $caco/$i
else
echo $caco/$i
fi
done
}
cdder $1
函数库
我们可以事先创建一个函数库文件,在里面定义各种常用的函数,然后可以在别的shell脚本中直接引用这个函数库文件,使得不需要在新的shell脚本中再次定义函数而是可以直接调用此函数。
1、使用函数库的关键在于 source命令,source命令会在当前shell上下文中执行命令,而不是创建一个新shell。可以用source命令在shell脚本允许库文件脚本。这样脚本就可以使用库函数了,source命令有个快捷的别名,称作点操作符(dot operator)。
source /opt/function
. /opt/function
2、.引用函数库文件时候 ,建议使用绝对路径,避免文件丢失
3、可以在家目录.bashrc文件中定义函数,这样新shell重新载入的适合函数就可以生效了
-
直接定义函数
# .bashrc # User specific aliases and functions alias rm='rm -i' alias cp='cp -i' alias mv='mv -i' # Source global definitions if [ -f /etc/bashrc ]; then . /etc/bashrc fi addem() { echo "hello" }
-
读取函数文件
# .bashrc # User specific aliases and functions alias rm='rm -i' alias cp='cp -i' alias mv='mv -i' # Source global definitions if [ -f /etc/bashrc ]; then . /etc/bashrc fi . /opt/function
变量替换
语法 | 说明 |
---|---|
${变量名#匹配规则} | 从变量开头进行规则匹配,将符合最短的数据删除 |
${变量名##匹配规则} | 从变量开头进行规则匹配,将符合最长的数据删除 |
${变量名%匹配规则} | 从变量尾部进行规则匹配,将符合最短的数据删除 |
${变量名%%匹配规则} | 从变量尾部进行规则匹配,将符合最长的数据删除 |
${变量名/旧字符串/新字符串} | 变量内容符合旧字符串,则第一个旧字符串被新字符串取代 |
${变量名//旧字符串/新字符串} | 变量内容符合旧字符串,则第全部旧字符串被新字符串取代 |
字符串处理
计算字符串长度
语法 | 说明 |
---|---|
${#string} | |
expr length “$string” | string有空格,则必须加双引号 |
expect免交互
expect是建立在tcl(tool command language)语言基础上语言基础上的一个工具,常被用于进行自动化控制和测试,解决shell脚本中交互的相关问题
expect的主要作用
- 追踪(需要交互的)指定
- 捕捉提示进行交互的关键词
- 传入交互指定 回车执行
yum -y install expect
-
spawn命令用来启动新的进程,spawn后的expect和send命令都是和使用spawn启动的新进程进行交互。
-
expect通常用来等待一个进程的反馈,我们根据进程的反馈,再使用send命令发送对应的交互命令。
-
send命令接收一个字符串参数,并将该参数发送到进程。
-
interact命令主要用于退出自动化,进入人工交互。interact执行完成后保持交互状态, 把控制权交给控制台,会停留在目标终端而不是退回到原终端,这时候就可以手工操作了,interact后命令不再起作用,比如interact后添加exit,并不会退出root用户。而如果没有interact则登录完成后会退出,而不是留在远程终端上。
expect eof 与 interact 只能二选一。
总结expect自动应答的基本步骤
第一步: 运行一个程序或命令=> spawn 命令信息
第二步: 识别产生信息关键字=> expect 捕获关键字 {send 应答信息}
第三步: 根据识别关键做处理=> send 应答信息
#!/usr/bin/expect -f
read -p "请输入你想连接的IP地址:" ip
expect -c "
spawn ssh $ip
expect {
"yes/no" {send "yes\r";exp_continue}
"*asswd" {send "1\n"}
}"
exp_continue 表示允许 expect 继续向下执行指令
使用exp_continue时,如果跟踪像passwd这样输入密码后就结束进程的命令,expect {}外不要加上expect eof 因为spawn进程结束后悔默认向expect 发送eof,会导致后面的expect eof执行报错
变量问题
引用位置变量,定义位置变量比较麻烦。我们可以结合shell脚本中的方式实行嵌入式模式
#!/bin/bash
ip=$1
passwd="aaaaaa"
/usr/bin/expect -c "
spawn ssh root@$ip
expect {
\"yes/no\" {send \"yes\n\";exp_continue}
\"password:\" {send \"$passwd\n\"}
}
interact "
#!/usr/bin/expect
set ip "192.168.0.162"
set passwd "aaaa"
spawn ssh root@$ip
expect {
"yes/no" {send "yes\n";exp_continue}
"password:" {send "$passwd\n"}
}
expect 默认的超时时间是10秒
set timeout: 表示超时时间,单位为秒(s),当expect没有收到符合条件的字符串时就会一直等待,当超过timeout设置的时间时就会自动跳过expect往下执行。
set timeout -1 # 设置为-1代表永不超时
set timeout 100 #设置为100秒
“bash” “source” ”.“ 和 ”./“的区别
命令 | 说明 |
---|---|
bash | 不需要可执行权限,但会在新的子进程中执行脚本文件中的命令 |
source | 不需要可执行权限,但并不会在新的子进程中执行脚本文件中的命令 |
. | 不需要脚本文件具有可执行权限,但并不会在新的子进程中执行脚本文件中的命令 |
./ | 这种方式需要脚本文件具有可执行权限,并且在脚本文件的第一行指定解释器,例如 #!/bin/bash 。并且会在新的子进程中执行脚本文件中的命令 |
screen命令工具
Screen 是一个命令行终端的多路复用工具,可以在一个终端窗口中创建多个终端会话,哪怕退出当前shell
yum -y install epel-release
yum -y install screen
screen -ls #查看当前所以会话
screen -r name #连接到[name]的shell会话中
screen -R name #创建一个新的名叫[name]的shell会话中
Ctrl+a d 键组合来分离当前会话,此时会话仍在后台运行,但不再占用当前终端。
在 Screen 会话中,可以使用 exit 命令来关闭当前窗口。
文章评论