美文网首页
shell脚本

shell脚本

作者: test小星星 | 来源:发表于2019-06-10 11:14 被阅读0次

    操作系统简史

    OS时代

    • 1973 贝尔实验室Unix AT&T Unix
    • 1982 BSD Unix
    • 1991 SUN Solarls

    PC时代

    • 1975 乔布斯Apple
    • 1980 比尔盖茨DOS

    GUI时代

    • 1979 乔布斯Mac
    • 1990 比尔盖茨Windows
    • 1994 Liunx

    移动OS时代

    • 2005 Google收购Android
    • 2005 乔布斯IOS

    shell

    • 1977 sh
    • 1989 bash

    bash变量类型

    # 整型
    a=1  
    # 字符串
    b="hello word"    
    # 布尔
    c=true 
    # 数组
    array=(a b c) 
    # 函数
    foo() { echo hello world } 
    
    = 左右两边不要有空格
    

    变量的使用

    # 定义变量a
    a=1  
    # 定义变量b
    b="hello wrod"
    
    # 输出变量a的值  echo相当于python的print() $引用变量
    echo $a  
    输出:1
    
    # 输出变量b的值
    echo $b
    输出:hello wrod
    
    # 双引号可以加变量,单引号加变量话当成字符串
    c="b=$b"
    输出:b=hello wrod
    c='b=$b'
    输出:b=$b
    
    # shell里面输出没有定义过的变量也不会报错,只是显示空
    echo $bbb
    
    # 字符串拼接变量
    echo ${b} hello shell
    输出:hello wrod hello shell
    

    浮点数的计算

    bash默认不支持浮点数的除法,如果要使用可以使用下面的方式

    # 计算浮点数
    awk 'BEGIN{printf 1/3}'
    输出:0.333333
    
    # 格式化显示
    awk 'BEGIN{printf("%.2f\n", 1/3)}'
    输出:0.33
    

    预定义变量

    echo $PWD  # 当前路径和小写pwd一样
    echo $USER # 当前用户
    echo $HOME # 当前用户的家目录
    echo $PATH  # 当前的环境变量
    

    which 查看当前项目安装的路径

    # 查看python安装的路径
    which python 
    # which只能找在环境变量存在的路径
    

    export 把项目加入环境变量中

    export PATH=$PATH:要加入环境变量的路径
    # 实例,将home加入环境变量中
    export PATH=$PATH:/home
    source 通常用于重新执行刚修改的初始化文件,使之立即生效,而不必注销并重新登录
    

    unset 取消变量

    # 定义变量
    a="hello"
    echo $a
    输出:hello
    
    # 取消变量
    unset a
    echo  $a
    输出:   #输出是空的,因为变量a不存在了
    

    数组变量

    # 定义数组
    array=(1 2 3 4 5)
    
    # 引用数组,并输出数组所有值
    echo ${array[@]} 
    echo ${array[*]} 
    
    # 取出数组第三个值
    echo ${array[2]}
    
    # 取出数组最后一个值
    echo ${array[-1]}
    
    # 将命令赋值给数组
    array=(`ls`)
    echo array  # 你在哪一层目录将ls赋值给array的,就会显示那一层ls下的所有文件
    
    数组使用*和@的区别
    # 加上双引号时
     array=(1 2 3);for line in "${array[@]}";do echo $line; done
    输出:
    1
    2
    3
    array=(1 2 3);for line in "${array[*]}";do echo $line; done
    输出:
    1 2 3
    
    # 不加上双引号时
    array=(1 2 3);for line in ${array[@]};do echo $line; done
    输出:
    1
    2
    3
    array=(1 2 3);for line in ${array[*]};do echo $line; done
    输出:
    1
    2
    3
    
    • 由此可见当加上双引号后echo ${array[@]} 是以数组的方式一次输出一个值,echo ${array[*]}是以字符串的方式拼接成一个值输出。
    • 不加上双引号两者是没有区别的

    特殊符号

    • " "双引号用于括起一段字符串值,支持$var形式的变量替换
    • ' '单引号也表示其内容是字符串值,不支持转义
    • ` ` 反引号的作用就是将反引号里面的内容当做是命令在执行。必须是shell真的存在的命令
    a=`ls`
    echo a  # 相当于是ls命令了
    # 将ls命令赋值给a 
    
    • $(ls) 表示执行ls后的结果。与` `类似。不过可以嵌套

    • 反引号和$()的区别:
      1.容易和单引号混淆;
      2.在多层嵌套使用时必须额外的跳脱(\`)处理,而使用$(ls)就没有这样的问题。

    • \ 转义符

    echo  -e "a\nbb"  
    输出:
      a
      bb
    
    参数
    # -e 开启转义
    # \ 转译符
    # \n 换行
    # \b 删除它前面一个字符
    # \a 发出警告声
    
    echo -e "a\bb"
    输出:b
    
    
    • $(())对变量进行操作,+ - * / % & | !等
    a=1
    b=2
    echo $((a+b))
    输出:3
    
    echo $((2+3))
    输出:5
    
    • (()) 是整数扩展,把里面的变量当做整数去处理
    a=1
    b=2
    
    (($a>$b)); echo $?  # 表示1是否大于2 返回的是false
    输出:1
    
    (($a<$b)); echo $? # 表示1是否小于2 返回的是true
    输出:0
    说明:在shell中0表示true,其他非0代表false
    
    ((a=a+b)); echo $a
    输出:3
    
    ((a++)); echo $a
    输出:2
    
    $(()) 和 (())的区别
    $是变量的标志
    (())是运算的标志
    $((运算))代表运算结果
    
    • seq 生成一个序列
    array=(`seq 1 10`)
    echo array
    输出:1 2 3 4 5 6 7 8 9 10
    
    • ({1...10}) 等价于 seq 1 10,表示1到10
    • ${ } 用来作变量替换用的,一般情况下,$var 与 ${var} 并没有啥不一样。但是用 ${ } 会比较精确的界定变量名称的范围。
    • $?命令执行返回都结果,是true 还是 false

    字符串操作

    切片

    s="hello shell"
    echo ${s:6} # 下标第6个位置开始取字符串hello shell ,下标从0开始哦
    输出:shell 
    

    切片取字符串长度

    s="hello shell"
    echo ${s:6:3}  # 表示从第6个位置开始取长度3个字符
    输出:she
    

    计算字符串长度

    s="hello shell"
    echo ${#s}
    输出:11
    

    掐头去尾 (非贪婪模式,只取第一个匹配到的字符)

    s="hello world"
    
    echo ${s#hello}  # 掐头 把hello 去掉 使用 #
    输出:world
    
    echo ${s#*w}  #  *表示匹配任意字符,只要碰到w前面的所有字符都去掉(包括w)
    输出:orld
    
    echo ${s%world} # 去尾,使用 %
    输出:hello
    
    echo ${s%w*} # 去尾,只要碰到w后面的所有字符都去掉(包括w)
    输出:hello
    

    掐头去尾(贪婪模式,一直匹配到最后一个对应的字符)

    s="hello shell"
    echo ${s##*s} # 掐头 使用 ##
    echo ${s%%s*}  # 去尾 使用 %%
    

    字符串替换 (非贪婪模式)

    s="hello shell"
    echo ${s/shell/python} # 表示把shell替换成python 使用 /
    输出:hello python 
    

    字符串替换 (贪婪模式)

    s="hello shell"
    echo ${s//l/x} # 表示把所有l替换成x 使用 //
    输出:hexxo shexx 
    

    加上双引号不忽略前面都空格

    s="hello shell"
    echo "${s#hello}"
    输出: shell  # 注意shell前面有个空格的
    

    字符串比较

    • [ string1 = string2 ] 如果两个字符串相同,则结果为真
    s1="abc"
    s2="bcd"
    [ "$a" = "$b"]; echo $?
    输出:false
    
    • [ string1 != string2 ] 如果两个字符串不相同,则结果为真
    • [ -n string ] 如果字符串不是空, 则结果为真
    • [ -z string ] 如果字符串是空,则结果为真
    • [[ "xxx" == x* ]] 在表达式中表示0或者多个字符
    s="hello"
     [[ "$s" == h* ]] ; echo $? # 表示$s 是否等于以h开头后面任意字符的字符串
    输出:0
    
    • [[ "xxx" == x?? ]] 在表达式中表示单个字符
    s="hello"
     [[ "$s" == m?? ]] ; echo $? # 表示$s 是否等于以m开头的单个字符串
    输出:1
    
    • [[ ]]是[ ]的扩展语法,在老的sh里并不支持,推荐使用[ ]
    • 字符串比较最好都加上双引号
    # 如果不加上双引号这里有个坑
    a="";
    b="abc";
    [ $a = $b ]; echo $?
    输出:-bash: [: =: unary operator expected
    
    # 加上双引号后
    [ "$a" = "$b" ]; echo $?
    输出:1 # 表示不成立
    
    说明:如果字符串为空时,不加上双引号会引发错误,所以建议在对字符串操作时都加上双引号
    

    算数判断

    • [ 2 -eq 2 ] 相等
    • [ 2 -ne 2 ] 不等
    • [ 3 -gt 1 ] 大于
    • [ 2 -ge 2 ] 大于等于
    • [ 3 -lt 4 ] 小于
    • [ 2 -le 2 ] 小于等于
    • (())也可以表示算术比较,((10>=8)), ((100==100)) 推荐使用这种

    逻辑运算

    • 逻辑与
    # -a 表示逻辑与
    [ 2 -ge 1 -a 3 -ge 4 ]; echo $?  # 表示2大于1 并且 3大于4
    输出:1 # 代表条件不成立
    
    扩展语法
    # && 表示逻辑与
    [[ 2 -ge 1 && 3 -ge 4 ]]; echo $?  # 表示2大于1 并且 3大于4
    输出:1 # 代表条件不成立
    
    • 逻辑或
    # -o 表示逻辑或
    [ 2 -ge 1 -o 3 -ge 4 ]; echo $?  # 表示2大于1 或者 3大于4
    输出:0 # 代表条件成立
    
    扩展语法
    # || 表示逻辑或
    [[ 2 -ge 1 || 3 -ge 4 ]]; echo $?  # 表示2大于1 或者 3大于4
    输出:0 # 代表条件成立
    
    • 逻辑非
    # ! 表示逻辑非
    [ ! 2 -ge 1 ]; echo $?  # 表示 取反
    输出:1 # 代表条件不成立
    

    内置判断

    • -e file 如果文件存在,则结果为真
    • -d file 如果文件是一个子目录,则结果为真
    • -f file 如果文件是一个普通文件,则结果为真
    • -r file如果请文件是可读,则结果为真
    • -w file 如果请文件是可写,则结果为真
    • -x file 如果请文件是可执行,则结果为真
    • -s file 如果文件的长度不为0,则结果为真
    # 判断是不是目录
    [ -d 文件名 ]; echo $?
    

    逻辑控制

    if结构

    • if [ 条件 ] ; then ...; fi
    • if [ 条件] ; then ...; else ...fi
    • if [ 条件 ]; then ... ; elif; then ... fi
    • 简单的逻辑可以使用 && || 去替代
    • 条件可以使用命令返回值代替
    # 判断文件是否存在,如果文件存在打印exist,如果文件不存在打印 not exist
    if [ -e test ]; then echo exist; else echo not exist; fi  
    
    # 判断ls命令下是否有文件
    if ls test; then echo exist; else not exist; fi # 如果ls命令下存在文件则输出exist否则输出not exist
    
    # 简写 如果执行ls命令返回的是true,则输出exist,如果返回的是false 则输出not exist
    ls test && echo exist || echo not exist  
    
    #  简写并不完全等价于if逻辑判断,这里有个坑。例如
    ls test || echo exist && echo not exist  # 这样的话会输出 not exist
    
    # 原因,执行||后面的语句 时, 只有当||前面的语句返回的是false时才会执行,如果是true就不会执行了,直接执行 && 后面的语句了。
    
    # 试试下面的例子
    echo "1" && echo "2" || echo "3" && echo "4" || echo "5" || echo "6" && echo "7" && echo "8" || echo "9"
    
    

    for循环

    • for (( i=0; i<10; i++)) ;
      do
      ....
      done

    • for x in ${array[@]};
      do ... ;
      done

    # 普通循环
    for (( i=0; i<10; i++ ));
    do
    echo $i; # 打印 i
    done
    
    # 循环打印数组
    array=(1 2 3 4 5)
    for ((i=0; i<${#array[@]}; i++)); do echo ${array[i]}; done
    
    # for 遍历循环
    array=(1 2 3 4 5)
    for x in ${array[@]}; do echo $x; done
    
    # 循环取目录下的所有文件
    for x in *; do echo $x; done # * 代表当前目录的所有文件 或者 用`ls`(但是用`ls`有个坑 如果文件名称中间有空格会被当成两个文件)
    

    while循环

    i = 0
    while [ $i -lt 3 ];
    do 
    echo $i;
    (( i++ ));
    done
    输出:
    0 
    1
    2
    

    退出循环

    • return 函数返回
    • exit 脚步退出
    • break 退出当前循环,默认为1
    • break 2 退出两层循环
    • continue 跳过当前的循环,进入下一次循环
    • continue 2 跳到上层循环的下次循环中

    Shell运行环境概念

    • bash 是一个进程
      • bash下还可以再重新启动一个shell,这个shell是子shell, 原shell会复制自身给它
      • 在子shell中定义的变量,会随着子shell的消亡而消失,相当于python中函数中定义的变量,随着函数的执行完成函数中的变量也消失了
      • 父程序的自定义变量是无法在子程序内使用的。但是通过 export 将变量变成环境变量后,就能够在子程序下面应用了
    • ( )在子shell中运行,外层是取不到( )中的变量的
    # 子shell中运行
    a=2
    >(a=1; echo $a); echo $a
    输出:
    1
    2
    可以看出在外层中echo $a 不会因为在(a=1)重新新赋值而受到影响。
    
    # 设置变量并可以在子程序中使用
    >name=jack
    >bash <==进入到所谓的子程序
    >echo $name <==子程序:再次的 echo 一下;
      <==嘿嘿!并没有刚刚设置的内容喔!
    >exit <==子程序:离开这个子程序
    >export name
    >bash <==进入到所谓的子程序
    >echo $name <==子程序:在此执行!
    jack <==看吧!出现设置值了!
    >exit <==子程序:离开这个子程序
    
    • { } 当前shell中运行
    • $?命令执行返回都结果,是true 还是 false
      • 任何命令执行都会有一个返回值
      • 0表示正确
      • 非0表示错误
    true
    echo $?
    输出: 0
    
    false 
    echo $?
    输出:1
    
    ls
    echo $?
    输出: 0
    
    • $$ 当前bash(脚本)执行的pid

    • & 后台执行

    for ((i=0; i<5; i++)); do echo $i; sleep 2; done &
    # sleep 休眠2秒
    # &在后台运行
    
    • $! 运行在后台的最后一个进程PID
    • jobs 查看后台运行的任务现在执行的状态和任务序号
    • bg num 当使用crlt + z 把前台任务切换到后台时,会暂停任务,使用 bg + 任务的序号 可以继续执行任务
      image.png
    • fg num 把后台任务显示到前台,有这样一种场景,当你使用vim编辑文本时,想先退出去做其他的操作,这时可以使用crlt + z把vim切到后台,等其他操作完成时 使用 fg + 任务序号 在把vim切换到前台,继续操作。

    vim常用快捷键

    • i进入编辑模式
    • v 剪切复制模式
    • esc 退出编辑模式

    剪切复制模式下

    • y 复制
    • d 剪切
    • p 粘贴

    esc 模式下

    • gg 跳到文件头
    • G 跳到文件尾
    • nG 跳到指定行 n代表行号
    • dd 删除整行

    冒号模式下输入命令 在esc下按 shift + : 进入冒号命令行模式

    • q 退出不保存
    • q! 强制退出不保存
    • wq 保存退出
    • wq! 强制保存退出
    • set nu 显示行号
    • set nonu 不显示行号
    • /string 搜索指定字符串

    Shell 输入输出

    • read用来读取输入,并赋值给变量,相当于python的input
    # 让用户输入内容
    read re
    如输入:abc
    echo $re
    输出 abc
    
    # 有提示信息的输入
    read -p "请输入内容" re; echo $re # -p 后面可以加提示信息
    
    # 使用whlie循环读取文件的内容
    while read line; do echo $line; done < txet(文件名)
    
    # 将输入的内容写入到文件中
    while read line; do echo $line; done > txet(文件名)
    
    • echo,printf 可以简单输出变量
    • > file 将输出重定向到另一个文件,等价于tee
    # 将内容写入到文件中,会覆盖原有的内容
    echo "hello shell" > txt
    
    • >>表示追加等价于tee -a
    # 将内容追加写入到文件中,不会覆盖原有的内容
    echo "hello python" >> txt
    
    • < file 输入重定向
    # 将原本是用键盘输入的重定向给了txt文件,变成了以文件的内容输入,
    # 所以就是将文件的内容输出
    read x < txt # read一次只读取一行
    
    • | 表示管道,也就是前一个命令的输出作为下一个命令的输入
    • tee 会同时将数据流分送到文件去与屏幕
    tee [-a] file
    选项与参数:
    -a :以累加 (append) 的方式,将数据加入 file 当中!
    
    # 实例
    echo "abcd" | tee a.txt
    abcd
    cat a.txt 
    abcd
    

    文件描述符

    # 将执行命令时提示错误的信息输入到文件中
    # 如ls一个不存在到目录会提示:No such file or directiry
    ls aaa > txt 2>&1
    # 说明:当我们执行一个命令的时候会有一个true的状态和false的状态,
    # 当命令执行成功返回true时会重定向到1中(1现在被我改成了txt文件)
    # 当执行不成功返回false时会重定向2中(2默认是输出到命令窗口)
    # >&1的作用就是告诉机器把错误的信息不要打印到命令窗口了,你跟我打印到txt文件中去
    
    输入/输出
    printf格式输出
    # 格式
    printf '打印格式' 实际内容
    
    %ns 那个 n 是数字, s 代表 string ,亦即多少个字符;
    %ni 那个 n 是数字, i 代表 integer ,亦即多少整数;
    %N.nf 那个 n 与 N 都是数字, f 代表 floating (浮点),
    假设我共要十个位数,但小数点有两位,即为 %10.2f 啰!
    
    # 实例
    printf '%s %i %.2f\n' $(echo "abc" 123 10.89)
    abc 123 10.89
    
    

    函数

    • $0 表示执行的程序,是相对于执行目的的路径
    • $1,$2,$3 分别表示第几个参数,shell默认只能支持9个参数,使用shift可以传递更多的参数。
    • $@,$* 表示所有的参数,不包含$0。
    • ${#*}和${#@}表示位置参数的个数。
    • 通过${*:1:3},${*:$#}来表示多个参数。

    实例

    #!/bin/bash
    func(){
            echo '$1='$1
            echo '$2='$2
            echo '$@='$@
            echo '$*='$*
            echo '$0='$0
            echo '${#*}'=${#*}
            echo '${#@}'=${#@}
            echo '"$*"'="$*"
            echo '"$@"'="$@"
            echo '${*:1:3}='${*:1:3}
            echo '${*:$#}='${*:$#}
            echo '${*:1:1}='${*:1:1}
    } ;
    func 1 2 3 4 5 6 7 8 9
    输出:
    $1=1
    $2=2
    $@=1 2 3 4 5 6 7 8 9
    $*=1 2 3 4 5 6 7 8 9
    $0=func.sh
    ${#*}=9
    ${#@}=9
    "$*"=1 2 3 4 5 6 7 8 9
    "$@"=1 2 3 4 5 6 7 8 9
    ${*:1:3}=1 2 3
    ${*:$#}=9
    ${*:1:1}=1
    
    
    $@ $* "$@" "$*"的区别
    #!/bin/bash
    count=0
    f(){
            echo '$@'=$@
            for i in $@; do echo ${i}; ((count++)); done;
            echo 'count='${count}
    }
    f 1 2 '3 "4 5" 6' 7 "8 9"
    
    输出:     
    $@=1 2 3 "4 5" 6 7 8 9
    1
    2
    3
    "4
    5"
    6
    7
    8
    9
    count=9
    ==========================================
    
    # "$@" 带双引号
    count=0
    f(){
            echo '"$@"'="$@"
            for i in "$@";do echo ${i}; ((count++)); done;
            echo 'count='${count}
    }
    f 1 2 '3 "4 5" 6' 7 "8 9"
    输出:
    "$@"=1 2 3 "4 5" 6 7 8 9
    1
    2
    3 "4 5" 6
    7
    8 9
    count=5
    ==========================================
    
    # $* 不带双引号
    count=0
    f(){
            echo '$*'=$*
            for i in $*;do echo ${i}; ((count++));done;
            echo 'count='${count}
    }
    f 1 2 '3 "4 5" 6' 7 "8 9"
    输出:
    $*=1 2 3 "4 5" 6 7 8 9
    1
    2
    3
    "4
    5"
    6
    7
    8
    9
    count=9
    ==========================================
    
    # "$*"带双引号
    count=0
    f(){
            echo '"$*"'="$*"
            for i in "$*";  do echo ${i}; ((count++)); done;
            echo 'count='${count}
    }
    f 1 2 '3 "4 5" 6' 7 "8 9"
    输出:
    "$*"=1 2 3 "4 5" 6 7 8 9
    1 2 3 "4 5" 6 7 8 9
    count=1
    
    

    记得使用"$@",不要直接使用$@

    使用外部文件执行函数时,通过外部函数传参

    1. 创建一个test.sh文件vim test.sh
    2. 在文件中写入如下函数:
    #!/bin/bash                                                     
    f()
    {
        name=$1
        echo "${name}"
    }
    f $1
    
    1. 执行文件
    $ bash test.sh abc(参数)
    abc
    

    执行方式

    • chmod u+x test.sh; ./test.sh 在当前shell中执行
    • bash test.sh 开启一个子shell执行
    • source test.sh 在当前shell中执行,同./test.sh

    DBUG代码

    • bash -x 读取代码中的每一句,并执行,可以方便的看到每次的执行过程。
    #!/bin/bash
    # bash_dbug函数
    $ cat bash_dbug.sh
    f(){
        for i in $(seq 1 5); do echo ${i}; done
    }
    f
    $ bash -x bash_dbug.sh  # dbug模式执行函数
    + f    # 先执行f函数
    ++ seq 1 5  # 执行seq
    + for i in $(seq 1 5) # 循环
    + echo 1  # 输出
    1
    + for i in $(seq 1 5) # 第二次循环
    + echo 2
    2
    + for i in $(seq 1 5) # 第三次循环...
    + echo 3
    3
    + for i in $(seq 1 5)
    + echo 4
    4
    + for i in $(seq 1 5)
    + echo 5
    5
    
    • set -x 开启dbug模式,可以放在脚本的开头使用
    #! /bin/bash
    # set_dbug的函数
    $ cat set_dbug.sh
      
    set -x  # 开启dbug
    f(){
        for i in $(seq 1 5); do echo ${i}; done
    }
    f
    
    $ ./set_dbug.sh   # 执行函数
    + f
    ++ seq 1 5
    + for i in $(seq 1 5)
    + echo 1
    1
    + for i in $(seq 1 5)
    + echo 2
    2
    + for i in $(seq 1 5)
    + echo 3
    3
    + for i in $(seq 1 5)
    + echo 4
    4
    + for i in $(seq 1 5)
    + echo 5
    5
    
    
    • set +x 关闭dbug模式
    • set 可以进行局部调试,在需要调试的代码之前加上“set -x”,需要调试的代码之后加上“set +x”即可

    相关文章

      网友评论

          本文标题:shell脚本

          本文链接:https://www.haomeiwen.com/subject/gfnnxctx.html