shell进阶

shell 中的高级用法

1.if

单重判断

if cmd; then
    cmd
    cmd
    cmd
fi

多重判断

单分支

if  cmd;then
    cmd
elif 
    cmd
fi

双分支

if cmd; then
    cmd
elif cmd;then
    cmd
elif cmd;then
    cmd
else
    cmd
fi

用于当判断条件的参数

逻辑判断

-a && 与
-o || 或
! 非
=~ 左边变量 ,右边扩展的正则表达式,不能写双引号
-z 判空
== 相等
!= 不相等

大小判断
-eq //等于
-ne //不等于
-gt //大于 (greater )
-lt //小于 (less)
-ge //大于等于
-le //小于等于

文件比较符
-e 判断对象是否存在
-d 判断对象是否存在,并且为目录
-f 判断对象是否存在,并且为常规文件
-L 判断对象是否存在,并且为符号链接
-h 判断对象是否存在,并且为软链接
-s 判断对象是否存在,并且长度不为0
-r 判断对象是否存在,并且可读
-w 判断对象是否存在,并且可写
-x 判断对象是否存在,并且可执行
-O 判断对象是否存在,并且属于当前用户
-G 判断对象是否存在,并且属于当前用户组
-nt 判断file1是否比file2新 [ ``"/data/file1" -nt ``"/data/file2" ]
-ot 判断file1是否比file2旧 [ ``"/data/file1" -ot ``"/data/file2" ]

2.case

要注意的是case 中使用的是引用变量,而不是声明变量名,$xxx

case支持glob风格的通配符:
*: 任意长度任意字符
?: 任意单个字符
[]:指定范围内的任意单个字符
a|b: a或b

case $num 变量引用 in  
1|2|3)     判断条件 ,可以使用通配符
    cmd1;
    ;;     根据;;来结束一个case段
4|5|6)
    cmd2;
    ;;
*)
    cmd3;
    ;;
esac

echo $passwd | passwd stdin user 设置用户密码

3.for

for 变量名(不是变量引用,不带$) in 列表;do
循环体
done

实例

for num in {1..10};do
    echo num is $num;
done

列表生成方式:

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

列表可以用任意的合集,用命令解析得到的合集也可以,比如填 ls /bin,支持通配符
{1..100..3} 1到100,每次步进3
unset sum 删除变量sum,防止影响
for中使用多行重定向的话 。需要把第一个EOF加- 或者把EOF结尾标志顶格。否则无法识别

for i in {1..10};do
    cat >>f1<<-EOF
    ASDASD
    ASDASD
    EOF
done

或者

for i in {1..10};do
    cat >>f1 <<EOF
    ASDASD
    ASDASD
EOF
done

4.for循环的第二种格式语法

sum=0
for ((i=1;i<=100;i++))
let sum+=i
done

for i in {1..3};do
for j in {1..10};do
if [ \(j -eq 5 ];then continue 2;fi echo j=\)j
done
done
这边就表示,当j等于5的时候,跳出第二层循环 。不是j循环的第二次,而是第二层!!!就是跳出i循环的当次循环,直接执行i的下次循环

5.参数移除

shift n

shift[n] 参数左移,n可以指定具体数字,表示每次抛弃的参数个数
比如 1 2 3
就会先处理完1,然后把1抛弃,处理2,以此类推

6.并行执行

并行执行 把所有语句用 {} 包裹,最后加上 & 就是把语句放在后台并行执行。

wait 脚本执行完成后自动退出,不需要用户按回车

为什么会输出两次192.168.30.1

let命令特性点:如果他操作的变量值为 0 返回的是假,
如果变量非0,返回是真

比如 i=0
leti++ ,那么这一次返回的是0

unset i
let i++
echo $? 为 假 1
unset i
let ++i
echo $? 为真 0

n=10;seq 1 \(n 这样用这个, n=10;echo {1..\)n} 这样是会报错的,不能这样执行

n=10;eval echo {1..$n} eval 会扫描并替换变量,然后执行该语句

用花括号分割变量

比如 \(ix\)j ,可能后面的\(j会识别成x\)j

写成
\(ix\){j} 即可

openssl rand -base64 20 | tr -dc '[: alpha :]'|head -c8

$[ 这样的格式里面可以做运算操作 ]

while 条件;do
循环体
done

: 返回真 等同于 true

selinux

用pgrep 查看进程是否存在,然后监控
pgrep 可以匹配到返回是true(0),而匹配不到会返回false(1)

向进程发送0信号,可以检测进程是否存在,因为0 信号不会对进程进行任何处理,但是会检查错误。

until 条件;do
循环体
done

与while相反,条件为假时进入循环,条件为真时,退出

who |grep USERNAME 查看该用户是否登录

7.while

while cmd;do
cmd
if cmd;then
continue 跳过当次执行
fi
done

while cmd;do
cmd
if cmd;then
break 退出当前循环块
fi
done

continue 和 break 后面可以跟数字,用于结束第N层的循环。
最里面的循环是第一层,往外层递增

whlie read line;do
循环体
done

while循环的特殊用法(遍历文件的每一行):
while read line; do
循环体
done < /PATH/FROM/SOMEFILE
依次读取/PATH/FROM/SOMEFILE文件中的每一行,且将行赋值给变量line

df | while read line;do
处理df的每一行,支持管道
done

8.until 循环

until cmd;do
    循环体
done

进入条件:cmd为假
退出条件:cmd为真
本质就是一个跟while相反的条件判断

9.select 创建菜单

PS3="Please choose the menu 1-4 : " 修改运行脚本时候的select提示符
select variable in list
do
循环体命令
done

$REPLAY存储用户手动输入的内容

自动将List转换为菜单,根据选择的项,给变量赋值

PS1命令行提示符
PS2多行重定向提示符
PS3 select 命令提示符

select 是个无限循环,因此要记住用 break 命令退出循环,或用 exit 命令终止脚本。也可以按 ctrl+c 退出循环
select 经常和 case 联合使用
与 for 循环类似,可以省略 in list,此时使用位置参量

下面是一个简单的示例:

#!/bin/bash
echo "What is your favourite OS?"
select var in "Linux" "Gnu Hurd" "Free BSD" "Other"; do
  break;
done
echo "You have selected $var"
</pre>

该脚本的运行结果如下:

What is your favourite OS?
    1) Linux
    2) Gnu Hurd
    3) Free BSD
    4) Other
#? 1
You have selected Linux</pre>

10.trap 捕获信号

trap 'echo press ctrl+c' int 捕获Int信号 ,转换为echo press ctrl +c
trap '' # 捕获信号,并且什么都不做,相当于拦截信号
trap '-' # 使信号恢复,恢复原信号的操作

可以捕捉15信号,使程序不可以正常关闭。但是无法捕捉9这个强制关闭的信号

脚本任务
alias.sh 配置别名
vim.sh 配置vim
yum.sh 配置yum
pack.sh 安装软件包

declare -f func4 查询是否存在func4函数

如果要在脚本里执行rm相关操作,需要检查对应的路径是否正确,变量是否赋值成功

11.函数

定义格式
语法一:

f_name ()
{
    ...函数体...
}

语法二:

function f_name
{
    ...函数体...
} 

语法三:

function f_name ()
{
    ...函数体...
}

函数的优先级比别名高
函数的生效范围是当前shell
local 改变变量的有效范围,让他只在该函数内有效

全局变量 > 普通变量 > local 变量
declare 声明的变量,也是local 类型的
declare -ig num=100 加g之后定义,变量就变成了普通变量,不再是local变量
return 退出函数本身
在函数中使用echo 输出变量 ,就可以把输出写在if中直接判断
version (){ echo 1 }
if [ version -eq 1 ] 判断是成立的
add() { echo $[$1+$2] }
add 1 2
输出3

函数复用

把函数保存为bash脚本,

新的脚本中使用
source functions (脚本名),相当于引用该脚本

之后就可以使用该脚本中的函数

action "commadn successful " 可以显示成功

action "xxx" /bin/false 表示失败
action "xxx /bin/true 表示成功

/etc/init.d/functions 储存了系统内置的函数

函数可以覆盖定义

export -f func1 将函数声明为全局函数,让子shell也可以使用该函数,定义函数的时候,不可以使用export

函数递归调用的时候,只有递归结束的时候,才会执行递归后的操作。

数组

关联索引 把数组的索引设置为自定义的格式,而不仅仅是数字

bash的数组支持稀疏索引(索引不连续),比如0,1, 3 有东西,2 没有 那么数组长度是3 ,但是他们不是连接的。输出2的话是空

declare -a ARRAY_NAME 普通数组
declare -A ARRAY_NAME 关联数组 必须先声明再使用

${a[1]}

echo ${name[* ]}
echo ${name[@]}
都代表输出数组中的所有元素

number=({1..10}) 这样也可以定义数组,()中存放任意生成多个字符串的命令都可以。通配符,正则表达式查找,bash命令,都能放

read -a title a b c 定义数组title ,内容是a b c
echo a b c | read -a s 定义数组s,内容是a b c (这样是错误的!!管理是不支持交互式赋值的)

关联数组一定要先定义再使用,否则数组变量会出问题

关联数组更像一个字典

${#name[* ]} 显示name数组的数组长度

因为使用了管道,所以开启了子shell,导致变量没有值
这样的话,在循环中使用的数组,声明周期单独只在循环中生效,因为这个时候while read line 使用的是开启的子shell

字符串处理

${var#*word}

str没有配置 表示变量不存在,没有声明

生成随机文件名
mktemp /data/tmpXXXXX 表示有五位的随机字符

expect

自动处理交互式命令,需要安装yum 包

自动传输文件

!/usr/bin/expect

spawn scp /etc/fstab 192.168.8.100:/app
expect {
"yes/no" { send "yes\n";exp_continue }
"password" { send “magedu\n" }
}
expect eof

spawn 表示捕获该命令 ,通过expect 来提交信息。当复制命令遇到yes或者no,就自动提交yes,然后继续执行。
当遇到password ,提交magedu

/etc/ssh/sshd_config

GSSAPIAuthentication no 关闭代理 79行
USEDNS NO 启动 115行

修改完成后 执行 systemctl sshd restart 重启sshd服务
加速sshd访问速度

自动登录

!/usr/bin/expect

spawn ssh 192.168.8.100
expect {
"yes/no" { send "yes\n";exp_continue }
"password" { send “magedu\n" }
}
interact

expect eof

interact 表示开启交互式
expect eof 结束expect 捕获

interact 搭配 #expect eof 表示登录终端后,释放对ssh的控制,这样expect就不会再继续捕获命令

expect 写一个控制脚本
用bash调用该脚本,做批量机器处理

矩阵转换

vim matrix.sh     
     #!/bin/bash

    arr=([00]=1 [01]=2 [02]=3 [10]=4 [11]=5 [12]=6 [20]=7 [21]=8 [22]=9)
    size=3

    showmatrix () {
    for ((i=0;i<size;i++));do
        for ((j=0;j<size;j++));do
            echo -e "${arr[$i$j]} \c"
        done
        echo
done
}

echo "Before convert"

showmatrix

for ((i=0;i<size;i++));do
    for ((j=i;j<size;j++));do
        if [ $i -ne $j ];then
            temp=${arr[$i$j]}
            arr[$i$j]=${arr[$j$i]}
            arr[$j$i]=$temp
        fi
    done
done

echo "After convert"
posted @ 2019-03-02 20:28  DreamDZhu  阅读(229)  评论(0编辑  收藏  举报