19.Shell编程进阶,数组,字符串,(for,select,while read line)

for循环

for 变量名 in 列表;do
循环体
done

  • 执行机制:
    依次将列表中的元素赋值给“变量名” ; 每次赋值后即执行一次循环体; 直到列表中的元素耗尽,循环结束
列表生成方式:

列表生成方式有多重,详情可查看其他博客所总结。

  1. 直接给出列表
  2. 整数列表:
    (a) {start..end}
    (b) $(seq [start [step]] end)
  3. 返回列表的命令
    $(COMMAND)
  4. 使用glob通配符,如:*.sh
  5. 变量引用
    $@, $*

for特殊格式

双小括号方法,即((…))格式,也可以用于算术运算
双小括号方法也可以使bash Shell实现C语言风格的变量操作
I=10
((I++))

  • for循环的特殊格式:
    for ((控制变量初始化;条件判断表达式;控制变量的修正表达式))
    do
    循环体
    done

  • 控制变量初始化:仅在运行到循环代码段时执行一次
  • 控制变量的修正表达式:每轮循环结束会先进行控制变量修正运算,而后再做条件判断
这种写法双小括号内的判断是按照C语言风格,而不是-eq 这类shell中test风格的

while循环

while CONDITION; do
循环体
done

  • CONDITION:循环控制条件;进入循环之前,先做一次判断;每一次循环之后会再次做判断;条件为“true”,则执行一次循环;直到条件测试状态为“false”终止循环
  • 因此:CONDTION一般应该有循环控制变量;而此变量的值会在循环体不断地被修正

进入条件:CONDITION为true
退出条件:CONDITION为false

until循环

until CONDITION; do
循环体
done

  • 进入条件: CONDITION 为false
  • 退出条件: CONDITION 为true

循环控制语句

continue

用于循环体中
continue [N]:提前结束第N层的本轮循环,而直接进入下一轮判断;最内层为第1层

while CONDTIITON1; do
CMD1
...
if CONDITION2; then
continue
fi
CMDn
...
done

break

用于循环体中
break [N]:提前结束第N层循环,最内层为第1层
while CONDTIITON1; do
CMD1
...
if CONDITION2; then
break
fi
CMDn
...
done

注意点:

  1. continue只是结束此这一次循环(默认最内层),然后继续下一次循环。它仍然在此循环内,只是continue下面的语句在此次循环中不再执行了。如果后面加上数字,就是跳到数字所在的循环层数内继续执行数字所在层的后面所剩的循环次数(根据判断条件判断的),而比数字小的循环则不再执行(相当于break掉了比数字小的循环)
  2. break相当于此循环直接停止并跳出(默认最内层),后面加上数字就代表了它直接跳出数字所在层的循环(跳到数字所在层的更外一层,然后执行更外一层的命令,而continue是跳到数字所在层继续执行数字所在层的剩下的循环)。
  3. 注意break只是循环退出了,并是不exit脚本退出。
  4. 还有一点就是假如有2层或者多层循环,break 退出当前层循环之后它是继续执行外层循环中在当前循环后面的命令,而contine 2则跳到外层循环之后直接从外层循环的开头重新进行下一轮循环了。

shift命令

shift [n]
用于将参量列表 list 左移指定次数,缺省为左移一次。
参量列表 list 一旦被移动,最左端的那个参数就从列表中删除。 while 循环遍历位置参量列表时,常用到 shift
例如:
./doit.sh a b c d e f g h
./shfit.sh a b c d e f g h

示例:doit.sh
#!/bin/bash
# Name: doit.sh
# Purpose: shift through command line arguments
# Usage: doit.sh [args]
while [ $# -gt 0 ] # or while (( $# > 0 ))
do
echo $*
shift
done

示例:shift.sh
#!/bin/bash
#step through all the positional parameters
until [ -z "$1" ]
do
echo "$1"
shift
done
echo
创建无限循环

while true; do
循环体
done

until false; do
循环体
Done

特殊用法(很实用)

遍历文件每一行这个写法很实用,参考自己写的创建用户的脚本读入每一行的命令就知道为何实用了(因为它只把换行符当做分隔符,而不把空格当做分隔符,每一行当做一个长字符串存入变量中)

while循环的特殊用法(遍历文件的每一行)

while read line; do
循环体
done < /PATH/FROM/SOMEFILE

  • 依次读取/PATH/FROM/SOMEFILE文件中的每一行,且将行赋值给变量line

select循环与菜单

PS3="Please input a number:"
select variable in list
do
循环体命令
done

  • select 循环主要用于创建菜单,按数字顺序排列的菜单项将显示在标准错误上,并显示 PS3 提示符,等待用户输入
  • 用户输入菜单列表中的某个数字,执行相应的命令
  • 用户输入被保存在内置变量 REPLY 中(和read的一样)
  • select 是个无限循环,因此要记住用 break 命令退出循环,或用 exit 命令终止脚本。也可以按 ctrl+c 退出循环
  • select 经常和 case 联合使用
    注意 :与 for 循环类似,可以省略 in list,此时使用位置参量
PS3="please input a number:"
select menu in backup clean config start stop status restart quit;do
        case $menu in
        *)
                echo $REPLY
                echo $menu;;
        esac
done      

函数介绍

  1. 函数function是由若干条shell命令组成的语句块,实现代码重用和模块化编程
  2. 它与shell程序形式上是相似的,不同的是它不是一个单独的进程,不能独立运行,而是shell程序的一部分

函数和shell程序比较相似,区别在于

  1. Shell程序在子Shell中运行
  2. 而Shell函数在当前Shell中运行。因此在当前Shell中,函数可以对shell中变量进行修改

定义函数

函数由两部分组成:函数名和函数体
help function
语法一:
f_name (){
...函数体...
}
语法二:
function f_name {
...函数体...
}
语法三:
function f_name () {
...函数体...
}

函数使用

  1. 函数的定义和使用:
    可在交互式环境下定义函数
    可将函数放在脚本文件中作为它的一部分
    可放在只包含函数的单独文件中
  2. 调用:函数只有被调用才会执行
    调用:给定函数名
    函数名出现的地方,会被自动替换为函数代码
  3. 函数的生命周期:被调用时创建,返回时终止

函数返回值

函数有两种返回值:
  1. 函数的执行结果返回值:
    (1) 使用echo等命令进行输出
    (2) 函数体中调用命令的输出结果
  2. 函数的退出状态码:
    (1) 默认取决于函数中执行的最后一条命令的退出状态码
    (2) 自定义退出状态码,其格式为:
    return 从函数中返回,用最后状态命令决定返回值
    return 0 无错误返回
    return 1-255 有错误返回

环境函数

使子进程也可使用
声明:export -f function_name
查看:export -f 或 declare -xf

函数递归示例

  • 函数递归:
    函数直接或间接调用自身
    注意递归层数
  • 示例:阶乘

fork×××

fork×××是一种恶意程序,它的内部是一个不断在fork进程的无限循环,实质是一个简单的递归程序。由于程序是递归的,如果没有任何限制,这会导致这个简单的程序迅速耗尽系统里面的所有资源

  • 函数实现
    :(){ :|:& };:
    bomb() { bomb | bomb & }; bomb
  • 脚本实现
    cat Bomb.sh
    #!/bin/bash
    ./$0|./$0&
func_factorial(){
        local input=$1
        if func_ispositint $input ;then
                [ $input -gt 1 ] && echo $[`func_factorial $[input-1]`*input] || echo 1
        else
                echo -e "Please input a positive integer"
                return 1 
        fi      
}                  

注意知识点:

  1. 函数就像一个普通命令,声明定义之后(source之后)直接使用即可,看不出和命令的区别。因此命名函数的时候最好以func_funcname的方式进行命名加以区分。
  2. 删除函数和删除变量一样 用unset func_name命令即可
  3. 注意下面这两个命令,它俩显示是已经加载入内存中正在被引用的函数

    • declare -F :只看函数名,后面可以跟函数名查询特定的函数,下同
    • declare -f : 看函数的定义内容,也就是/etc/init.d/functions文件中的内容
  4. 可以把自己常用的函数写在一个文件里,然后source它引用。
    比如系统中/etc/init.d/function中的actions()函数就可拿来直接用,先source或者.它即可.注意脚本中也要source它,这样它才能在开启的子shell中也运行。
action string true :它就会显示正确结果  action string false : 它就会显示错误结果
  1. 注意因为函数是在当前shell中运行,因此它的变量会影响当前脚本或者shell中的变量结果(也就是两个变量名字相同会造成变量污染),因此 函数中的变量一般都定义为 local variable=赋值;

    • 三种常见变量1.普通变量直接赋值无需定义,当前shell中有效;2.export 全局变量(环境变量) 可以定义的时候赋值,也可以普通变量赋值完之后再定义,子shell和当前shell有效;3.局部变量local 函数中使用
  2. 三个返回:return 返回出函数;break,continue 返回出循环;exit 返回出shell(脚本) ;

    • 一个等待:wait 后台命令结束后自动返回前台并打印PS1提示符
    • 注意所有的返回都是返回到执行这个函数,脚本,循环的上一层环境命令处,返回命令后面的命令(在这个函数,脚本,循环中)则不再执行了。
  3. { cmd; cdm;} 也可以看作是一个匿名函数(没有名字的函数,只能调用一次),更详细的关于括号的解释参考其他博客。
  4. return 后面不加数字的话就是返回上面的最后一个命令的$?值作为这整个函数的返回值,如果加上数字则把这个数字当做这个函数整体的返回值$?.
  5. 函数类似脚本后面也可以跟参数,用法一模一样。注意$*和$@的区别,加上双引号引用此参数传递到子函数或者子脚本中,前者代表一整个字符串作为一个参数,后者代表分别作为一个一个参数。不加上双引号没区别,这也是因为不加双引号会把里面的空格作为分隔符。

    • 注意如果是脚本中调用函数,则脚本后面的参数调入到函数中时,如若函数后的参数是脚本所用的参数,一定要注意参数用$加数字方式的时候,写好顺序。同时如果想要函数对这些参数进行改变,一定注意函数内部的变量不要定义为局部变量才可。
  6. 函数如果写在脚本中要写在最前面以供后面的命令调用,注意shell中的函数没有先声明后定义的用法,只能写在最前面,不像C语言可以先声明后面再定义。
  7. 注意函数递归是先递归到最里层,然后最里层返回一个返回值,然后在从最里层往外返回到最外层。因此最里层一定要写好返回值的条件,不然会永远递归下去消耗系统资源。递归×××就从此而来。
  8. 通过阶乘的编写需要注意递归函数的编写:
    • return返回的值是函数的返回值存在$?中,不能作为函数名字的结果被上一层递归计算
    • 用echo 返回出值,然后就可以代表这个函数名字当前层级的值,可以被上一层级来利用它进行计算
    • 注意echo 返回值不能写在最后,只能写在递归的判断里面,否则每次递归都会echo 一次,会导致每次递归函数名的返回值是两个(2层) 3个(三层).... 这样的话就没办法把函数本身当做一个数值来进行计算了。
    • 最底层没有返回结果则会永远递归下去,不断的开子进程,folk×××就是利用此

信号捕捉trap

  1. trap '触发指令' 信号
    进程收到系统发出的指定信号后,将执行自定义指令,而不会执行原操作
  2. trap '' 信号
    忽略信号的操作
  3. trap '-' 信号
    恢复原信号的操作
  4. trap -p
    列出自定义信号操作
  5. trap finish EXIT
    当脚本退出时,执行finish函数

注意点:

  1. trap的用处就是在shell(主要体现在脚本)中,让某些命令无法进行操作,比如说kill -9 命令等等。可以保证脚本的各种运行状态
  2. 还有一种情况就是脚本执行一半因为某些特殊情况非正常退出,或者说遇到某些条件判断退出了,则在退出的时候捕获到退出信号EXIT(EXIT信号并未在kill -l中列出),则会执行退出时指定的命令或者函数(需要脚本前面提前定义函数等)(If a SIGNAL_SPEC is EXIT (0) ARG is executed on exit from the shell.)
  3. trap的生效在脚本中也是从前到后的,因此trap的信号捕获命令一定要写在脚本的最前端。同时后面的信号可以写对应的数字标号,特殊的0表示shell(脚本)退出信号。
  4. 注意下面trap中写的echo命令会打断sleep,如果想要不打断,则中间的命令部分什么也不写空着最好,这样int命令将没办法执行任何操作了。
trap示例
#!/bin/bash
trap 'echo “signal:SIGINT"' int
trap -p

for((i=0;i<=10;i++))
do
sleep 1
echo $i
done

trap '' int
trap -p

for((i=11;i<=20;i++))
do
sleep 1
echo $i
done

trap '-' int
trap -p

for((i=21;i<=30;i++))
do
sleep 1
echo $i
done

数组

  1. 变量:存储单个元素的内存空间
  2. 数组:存储多个元素的连续的内存空间,相当于多个变量的集合
  3. 数组名和索引
    索引:编号从0开始,属于数值索引
    • 注意:索引可支持使用自定义的格式,而不仅是数值格式,即为关联索引,从bash4.0版本之后开始支持

声明数组:

declare -a ARRAY_NAME 普通数组:可以不用先声明,直接使用即可
declare -A ARRAY_NAME 关联数组:必须先声明才可以使用,如果先使用了,必须unset之后再重新声明才可以使用
注意:两者不可相互转换

数组注意点:

  1. bash的数组支持稀疏格式(索引不连续),非常实用,但是要注意稀疏格式的数组总个数就是有元素的个数,而不是数组的下标的最大值的个数
  2. 注意字符串(也就是变量名)取长度的时候不要加下标${#varialble},只有包含多个元素的数组取长度的时候才加下标${#array[*]}。同时字符串想要取中间的部分字符,只能用${var:#:#}的这种方式,因为var它就相当于数组只有一个元素它本身(或者说这个字符串开头的地址var[0])
  3. 而数组中的每个元素中内容想要切片的话则要加上下标${array[INDEX]:#:#},(此时就是把这个带下标的数组元素名当做一个地址,看作是本身的下标0了)。取数组中的各个元素的话则是${array[*|@]:#:#}的方式。
  4. 不写下标默认为0,因此字符串数组(也就是变量)就是代表整个字符串,而数组就是代表数组里面的第一个元素。
17:47[root@centos7 /etc/sysconfig/network-scripts]# abc=12334
17:54[root@centos7 /etc/sysconfig/network-scripts]# echo ${abc[0]}
12334
17:54[root@centos7 /etc/sysconfig/network-scripts]# echo ${abc}
12334
17:54[root@centos7 /etc/sysconfig/network-scripts]# echo ${#abc}
5

17:57[root@centos7 /data/scriptest]# array=([1]=12wad [5]=ff [6]=cvv [8]=1wd234)
17:57[root@centos7 /data/scriptest]# echo $array

17:57[root@centos7 /data/scriptest]# echo ${array[0]}

17:58[root@centos7 /data/scriptest]# echo ${array[1]}
12wad
17:58[root@centos7 /data/scriptest]# echo ${#array[1]}  :第一个元素的长度
5
17:58[root@centos7 /data/scriptest]# echo ${#array[*]}  :数组总长度,稀疏数组只看有几个元素就是几
4
17:58[root@centos7 /data/scriptest]# echo ${array[1]:2:3} :第一个元素内容切片
wad
17:58[root@centos7 /data/scriptest]# echo ${array[*]}
12wad ff cvv 1wd234
20:00[root@centos7 /data/scriptest]# echo ${array[*]:2:2} :取不同的数组元素,注意和元素内容切片的区分
ff cvv
20:00[root@centos7 /data/scriptest]# echo ${array[*]:2:3}
ff cvv 1wd234

数组赋值

数组元素的赋值

(1) 一次只赋值一个元素
ARRAY_NAME[INDEX]=VALUE
weekdays[0]="Sunday"
weekdays[4]="Thursday"
(2) 一次赋值全部元素(注意尽量要加上引号,虽然空格也可以作为分隔符)
ARRAY_NAME=("VAL1" "VAL2" "VAL3" ...)
(3) 只赋值特定元素
ARRAY_NAME=([0]="VAL1" [3]="VAL2" ...)
(4) 交互式数组值对赋值(注意有一个-a的选项)
read -a ARRAY

显示所有数组:declare -a

特殊的赋值方式:

num=({1..10}) :1到10赋值给num[0]到num[9]
file=(*.sh) :当前目录中sh后缀的文件名赋值给file数组

稀疏方式赋值注意:如果先一个一个赋值,然后又一次赋值特定元素(3中的方式),就算赋值的元素下标并不一样,但这样仍然则会将一个一个赋值的结果给覆盖掉。但是反过来如果先一次性赋值(3中的方式),然后再一个一个赋值下标不同的数组元素,则不会覆盖掉一次性赋值的元素例子:
19:40[root@centos7 /data]# testarray[2]=12
19:40[root@centos7 /data]# testarray[5]=1234
19:40[root@centos7 /data]# echo ${testarray[2]} 
12
19:40[root@centos7 /data]# declare -a
declare -a testarray='( [2]="12" [5]="1234")'
19:40[root@centos7 /data]# testarray=( [1]=wade [3]=waef )
19:40[root@centos7 /data]# echo ${testarray[2]} 

19:40[root@centos7 /data]# declare -a
declare -a testarray='([1]="wade" [3]="waef")'
上面会覆盖,然后继续,下面的不会覆盖:
19:40[root@centos7 /data]# testarray[2]=12
19:44[root@centos7 /data]# testarray[5]=1234
19:44[root@centos7 /data]# declare -a
declare -a testarray='([1]="wade" [2]="12" [3]="waef" [5]="1234")'

引用数组

  1. 引用数组元素
    ${ARRAY_NAME[INDEX]}
    • 注意:省略[INDEX]表示引用下标为0的元素
  2. 引用数组所有元素
    ${ARRAY_NAME[*]}
    ${ARRAY_NAME[@]}
  3. 数组的长度(数组中元素的个数)
    ${#ARRAY_NAME[*]}
    ${#ARRAY_NAME[@]}
  4. 删除数组中的某元素:导致稀疏格式
    unset ARRAY[INDEX]
  5. 删除整个数组(类似的还有删除变量,删除函数,注意都是从内存中删除掉)
    unset ARRAY

数组数据处理

引用数组中的元素:
  1. 数组切片:
    ${ARRAY[*]:offset:number}
    offset 要跳过的元素个数
    number 要取出的元素个数
  2. 取偏移量之后的所有元素
    ${ARRAY[*]:offset}
  3. 向数组中追加元素(因为下标从0开始):
    ARRAY[${#ARRAY[*]}]=value
  4. 关联数组:
    declare -A ARRAY_NAME
    ARRAY_NAME=([idx_name1]='val1' [idx_name2]='val2‘...)
    注意:关联数组必须先声明再调用

示例

  • 生成10个随机数保存于数组中,并找出其最大值和最小值
    (优化算法:不要每次都判断是否是第一个,把第一个赋值写在for循环外面,然后for循环直接从1开始即可)
    #!/bin/bash
    declare -i min max
    declare -a nums
    for ((i=0;i<10;i++));do
    nums[$i]=$RANDOM
    [ $i -eq 0 ] && min=${nums[$i]} && max=${nums[$i]}&& continue
    [ ${nums[$i]} -gt $max ] && max=${nums[$i]}
    [ ${nums[$i]} -lt $min ] && min=${nums[$i]}
    done
    echo “All numbers are ${nums[*]}”
    echo Max is $max
    echo Min is $min

示例

  • 编写脚本,定义一个数组,数组中的元素对应的值是/var/log目录下所有以.log结尾的文件;统计出其下标为偶数的文件中的行数之和
#!/bin/bash
#
declare -a files
files=(/var/log/*.log)
declare -i lines=0
for i in $(seq 0 $[${#files[*]}-1]); do
if [ $[$i%2] -eq 0 ];then
let lines+=$(wc -l ${files[$i]} | cut -d' ' -f1)
fi
done
echo "Lines: $lines."

字符串切片

  1. ${#var}:返回字符串变量var的长度
  2. ${var:offset}:返回字符串变量var中从第offset个字符后(不包括第offset个字符)的字符开始,到最后的部分,offset的取值在0 到 ${#var}-1 之间(bash4.2后,允许为负值)
  3. ${var:offset:number}:返回字符串变量var中从第offset个字符后(不包括第offset个字符)的字符开始,长度为number的部分
  4. ${var: -length}:取字符串的最右侧几个字符
    注意:冒号后必须有一空白字符
  5. ${var:offset:-length}:从最左侧跳过offset字符,一直向右取到距离最右侧lengh个字符之前的内容
  6. ${var: -length:-offset}:先从最右侧向左取到length个字符开始,再向右取到距离最右侧offset个字符之间的内容
    注意:-length前有空格(中间的那个必须有空格,:最后的一项可以不加空格)

字符串处理

  1. 基于模式取子串
    ${var#*word}:其中word可以是指定的任意字符
    功能:自左而右,查找var变量所存储的字符串中,第一次出现的word, 删除字符串开头至第一次出现word字符串(含)之间的所有字符
  2. ${var##*word}:同上,贪婪模式,不同的是,删除的是字符串开头至最后一次由word指定的字符之间的所有内容

    • 示例:
      file=“var/log/messages”
      ${file#/}: log/messages
      ${file##
      /}: messages
  3. ${var%word*}:其中word可以是指定的任意字符
    功能:自右而左,查找var变量所存储的字符串中,第一次出现的word, 删除字符串最后一个字符向左至第一次出现word字符串(含)之间的所有字符

    • 例子:
      file="/var/log/messages"
      ${file%/*}: /var/log
  4. ${var%%word*}:同上,只不过删除字符串最右侧的字符向左至最后一次出现word字符之间的所有字符
查找替换
  1. ${var/pattern/substr}:查找var所表示的字符串中,第一次被pattern所匹配到的字符串,以substr替换之
  2. ${var//pattern/substr}:查找var所表示的字符串中,所有能被pattern所匹配到的字符串,以substr替换之
  3. ${var/#pattern/substr}:查找var所表示的字符串中,行首被pattern所匹配到的字符串,以substr替换之
  4. ${var/%pattern/substr}:查找var所表示的字符串中,行尾被pattern所匹配到的字符串,以substr替换之
查找并删除
  1. ${var/pattern}:删除var表示的字符串中第一次被pattern匹配到的字符串,从左往右匹配
  2. ${var//pattern}:删除var表示的字符串中所有被pattern匹配到的字符串,从左往右匹配
  3. ${var/#pattern}:删除var表示的字符串中所有以pattern为行首匹配到的字符串
  4. ${var/%pattern}:删除var所表示的字符串中所有以pattern为行尾所匹配到的字符串
字符大小写转换

${var^^}:把var中的所有小写字母转换为大写
${var,,}:把var中的所有大写字母转换为小写

变量赋值的其他形式

image

  • 注意里面的expr是一个字符串,不是一个变量。直接就是双引号引起来的字符串。

高级变量用法-有类型变量

  • Shell变量一般是无类型的,但是bash Shell提供了declare和typeset两个命令
  • 它可用于指定变量的类型,两个命令是等价的
declare [选项] 变量名

-r 声明或显示只读变量
-i 将变量定义为整型数
-a 将变量定义为数组
-A 将变量定义为关联数组
-f 显示已定义的所有函数名及其内容
-F 仅显示已定义的所有函数名
-x 声明或显示环境变量和函数 :或者export
-l 声明变量为小写字母 declare –l var=UPPER :即使输入大写字母,也会变成小写字母,下同相反
-u 声明变量为大写字母 declare –u var=lower

eval命令 :很实用,但是要会用它

eval命令将会首先扫描命令行进行所有的置换,然后再执行该命令。该命令适用于那些一次扫描无法实现其功能的变量.该命令对变量进行两次扫描

  • 示例:
    [root@server ~]# CMD=whoami
    [root@server ~]# echo $CMD
    whoami
    [root@server ~]# eval $CMD
    root
    [root@server ~]# n=10
    [root@server ~]# echo {0..$n}
    {0..10}
    [root@server ~]# eval echo {0..$n}
    0 1 2 3 4 5 6 7 8 9 10
间接变量引用:可以增加脚本灵活性,比如变量中存放了一个命令(其实直接写上$CMDvariable就能直接使用,要结果的话用echo加上反向单引号也能用`$CMDvariable`,)
  • 如果第一个变量的值是第二个变量的名字,从第一个变量引用第二个变量的值就称为间接变量引用
  • variable1的值是variable2,而variable2又是变量名,variable2的值为value,
  • 间接变量引用是指通过variable1获得变量值value的行为
    variable1=variable2
    variable2=value

  • bash Shell提供了两种格式实现间接变量引用
    eval tempvar=\$$variable1 :注意不加\的话则会把当前shell的PID显示出来($$)
    tempvar=${!variable1}
  • 示例:
    [root@server ~]# N=NAME
    [root@server ~]# NAME=wang
    [root@server ~]# N1=${!N}
    [root@server ~]# echo $N1
    wangxiaochun
    [root@server ~]# eval N2=\$$N
    [root@server ~]# echo $N2
    wang

创建临时文件

mktemp命令:创建并显示临时文件,可避免冲突
mktemp [OPTION]... [TEMPLATE]
TEMPLATE: filenameXXX
X至少要出现三个

  • OPTION:
    -d: 创建临时目录
    -p DIR或--tmpdir=DIR:指明临时文件所存放目录位置
  • 示例:
    mktemp /tmp/testXXX
    tmpdir=`mktemp –d /tmp/testdirXXX`
    mktemp --tmpdir=/testdir testXXXXXX

注意

  1. 它创建临时文件的时候会有默认的输出,因此可以把它命令的结果存入到变量中比如file=`mktemp /data/tempXXXX`
  2. 平常创临时文件的时候直接就写上了路径 ,不过也可以分开写 要难过-p 指定dir 然后后面只写临时文件名字就行了。不过一般不这么麻烦
  3. 自己单独编译安装的时候某些文件就要用到临时文件

安装复制文件

install命令:

install [OPTION]... [-T] SOURCE DEST 单文件
install [OPTION]... SOURCE... DIRECTORY
install [OPTION]... -t DIRECTORY SOURCE...
install [OPTION]... -d DIRECTORY...创建空目录

  • 选项:
    -m MODE,默认755
    -o OWNER
    -g GROUP
  • 示例:
    install -m 700 -o wang -g admins srcfile desfile
    install –m 770 –d /testdir/installdir

注意:

  1. install相当于cp ,chown ,chgrp ,chmod的集合
  2. 如果直接install 来进行拷贝,它和cp命令相比,它会让文件全部加上执行权限(ugo都加上)(而且它默认不是交互式,直接覆盖)
  3. 命令用法如其名,实现脚本一键安装,修改文件属性
  4. 注意他可以直接创建一个新的空文件夹,cp没有这功能(虽然cp拷贝的时候可以对应创建文件夹,但不能直接命令来单独创建一个空文件夹)

expect介绍

expect 是由Don Libes基于Tcl( Tool Command Language )语言开发的,主要应用于自动化交互式操作的场景,借助 expect 处理交互的命令,可以将交互过程如:ssh登录,ftp登录等写在一个脚本上,使之自动化完成。尤其适用于需要对多台服务器执行相同操作的环境中,可以大大提高系统管理人员的工作效率

它执行的时候会捕获屏幕上出现的关键字,然后根据出现的key来自动提交(输入)内容

expect命令:需要额外安装,默认不安装

  • expect 语法:
    expect [选项] [ -c cmds ] [ [ -[f|b] ] cmdfile ] [ args ]
  • 选项
    -c:从命令行执行expect脚本,默认expect是交互地执行的
    示例:expect -c 'expect "\n" {send "pressed enter\n"}
    -d:可以输出输出调试信息
    示例:expect -d ssh.exp
  • expect中相关命令
    spawn 启动新的进程
    send 用于向进程发送字符串
    expect 从进程接收字符串
    interact 允许用户交互 :比如只ssh登陆,然后登录后继续手工命令
    exp_continue 匹配多个字符串在执行动作后加此命令
expect最常用的语法(tcl语言:模式-动作)
  1. 单一分支模式语法:
    expect “hi” {send “You said hi\n"}
    匹配到hi后,会输出“you said hi” ,并换行
  2. 多分支模式语法:
    expect "hi" { send "You said hi\n" } \
    "hehe" { send "Hehe yourself\n" } \
    "bye" { send "Good bye\n" }
    • 匹配hi,hello,bye任意字符串时,执行相应输出。等同如下:
      expect {
      "hi" { send "You said hi\n"}
      "hehe" { send "Hehe yourself\n"}
      "bye" { send " Good bye\n"}
      }

注意:

  1. expect是一个程序命令包,它里面包含了一个同名的expect命令。
  2. expect默认交互式,进去之后再输入命令(比如再输入spawn expect等)
    示例:
示例
#!/usr/bin/expect
spawn scp /etc/fstab 192.168.8.100:/app
expect {
"yes/no" { send "yes\n";exp_continue }
"password" { send "www.123\n" }
}
expect eof

#!/usr/bin/expect
spawn ssh 192.168.8.100
expect {
"yes/no" { send "yes\n";exp_continue }
"password" { send "www.123\n" }
}
interact
#expect eof

示例:变量,变量设置和赋值用set,后面不加等号
#!/usr/bin/expect
set ip 192.168.8.100
set user root
set password magedu
set timeout 10
spawn ssh $user@$ip
expect {
"yes/no" { send "yes\n";exp_continue }
"password" { send "$password\n" }
}
interact

示例:位置参数:变量从0开始而不是shell中的从1开始
#!/usr/bin/expect
set ip [lindex $argv 0]
set user [lindex $argv 1]
set password [lindex $argv 2]
spawn ssh $user@$ip
expect {
"yes/no" { send "yes\n";exp_continue }
"password" { send "$password\n" }
}
interact
#./ssh3.exp 192.168.8.100 root www

示例:执行多个命令
#!/usr/bin/expect
set ip [lindex $argv 0]
set user [lindex $argv 1]
set password [lindex $argv 2]
set timeout 10
spawn ssh $user@$ip
expect {
"yes/no" { send "yes\n";exp_continue }
"password" { send "$password\n" }
}
expect "]#" { send "useradd haha\n" }
expect "]#" { send "echo www |passwd --stdin haha\n" }
send "exit\n"
expect eof
#./ssh4.exp 192.168.8.100 root www

示例:shell脚本调用expect,就是用多行重定向的方式来使用expect
#!/bin/bash
ip=$1
user=$2
password=$3
expect <<EOF
set timeout 20
spawn ssh $user@$ip
expect {
"yes/no" { send "yes\n";exp_continue }
"password" { send "$password\n" }
}
expect "]#" { send "useradd hehe\n" }
expect "]#" { send "echo www |passwd --stdin hehe\n" }
expect "]#" { send "exit\n" }
expect eof
EOF
#./ssh5.sh 192.168.8.100 root www

转载于:https://blog.51cto.com/14228129/2379087

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Shell字符串数组是一种存储多个字符串的数据结构,可以通过下标访问数组中的元素,也可以通过循环遍历数组中的所有元素。在Shell脚本中,可以使用declare命令或者直接赋值的方式来创建字符串数组。例如,使用declare命令创建一个包含三个字符串数组: declare -a arr=("hello" "world" "shell") 然后可以通过下标访问数组中的元素: echo ${arr[]} # 输出hello 也可以使用循环遍历数组中的所有元素: for i in "${arr[@]}" do echo $i done 输出结果为: hello world shell ### 回答2: Shell中的字符串数组是一种特殊的变量类型,用于存储多个字符串。在Shell脚本中,字符串数组可以用于存储一系列相关的字符串,并且可以对其进行遍历、操作和处理。 定义字符串数组可以使用以下语法: ``` array=("string1" "string2" "string3") ``` 其中,array是数组的名称,而"string1"、"string2"、"string3"是数组中的元素。 也可以逐个添加元素到数组中: ``` array[0]="string1" array[1]="string2" array[2]="string3" ``` 访问数组元素可以使用以下语法: ``` element=${array[index]} ``` 其中,index是数组中元素的索引,element是获取的数组元素。 对于数组的长度,可以使用以下语法: ``` length=${#array[@]} ``` 使用这个语法,可以获取数组中元素的个数。 遍历字符串数组可以使用循环语句: ``` for element in "${array[@]}" do echo $element done ``` 这个例子中,通过遍历${array[@]},我们可以访问数组中的每个元素,并将其打印出来。 使用字符串数组,可以方便地存储和处理一系列相关的字符串数据,从而实现更加灵活和高效的脚本编写。 ### 回答3: Shell字符串数组Shell编程语言中的一种数据结构,它可以存储多个字符串,并通过索引访问和操作。在Shell中,字符串数组可以通过以下方式定义和初始化: ```bash my_array=("string1" "string2" "string3") ``` 上述代码定义了一个名为my_array的字符串数组,其中有三个元素,分别是"string1"、"string2"和"string3"。我们可以通过数组索引来访问特定的元素,如: ```bash echo ${my_array[0]} # 输出第一个元素"string1" echo ${my_array[1]} # 输出第二个元素"string2" echo ${my_array[2]} # 输出第三个元素"string3" ``` 我们还可以通过下标的方式遍历整个数组,并对每个元素进行操作,如: ```bash for element in ${my_array[@]}; do echo $element done ``` 上述代码会将数组中的每个元素逐行输出。 在Shell中,我们可以使用数组来存储需要进行批量处理的数据,方便对多个字符串进行统一操作,比如切割、连接等。同时,字符串数组还可以作为函数的参数传递,方便将多个字符串作为一个整体进行处理。总的来说,Shell字符串数组是一种非常有用的数据结构,可以简化Shell脚本编写,提高代码的可读性和维护性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值