美文网首页
shell脚本和shell十三问

shell脚本和shell十三问

作者: 大吉岭猹 | 来源:发表于2019-05-01 16:19 被阅读0次

    1. 视频笔记

    1.1. 基本命令

    • 创建脚本:vi my.sh
    • 运行脚本:bash my.sh

    1.2. 变量

    • PATH、PS1等

    1.3. 参数

    • $0 脚本名
    • $1/2/3... 跟着的第1/2/3...个参数

    1.4. 通配符

    • *表示任意
    • [12]表示方括号里随意选

    1.5. 标准头文件

    • 专业人士一般会加
    • 说明用法,所以如果给别人看的话需要写

    1.6. 输入输出与捕获

    • ls blabla尖括号的作用为捕获
    • 重定向

    1.7. 小技巧

    • grep -vf (反选、文件中选)
    • 软链接文件夹无法绕过权限,因此需要软链接文件夹里的所有文件。ls 目标路径(往往要用*)|while read id; do ln -s $id $(basename $id);done

    2. literal和meta

    • 常用meta

      • IFS,CR


        meta.png
    • 屏蔽meta:quoting

      • hard quote:''(单引号),凡在 hard quote 中的所有 meta 均被关闭;
      • soft quote:""(双引号),凡在 soft quote 中大部分 meta 都会被关闭,但某些会保留 (如 $);
      • escape: \ (反斜杠),只有在紧接在 escape(跳脱字符) 之后的单一 meta 才被关闭;
    • enter键产生的字符可能是

      • CR
      • IFS
      • NL(New Line)
      • FF(Form Feed)
      • NULL
      • ...
    • 双引号与单引号的区别中awk相关的部分暂略。

    3. 改变meta

    • IFS=;
    • 几个例子
    $ A="  abc"
    $ echo $A
    abc
    $ echo "$A" #note1
       abc
    $ old_IFS=$IFS
    $ IFS=; #将IFS设置为null
    $ echo $A
       abc
    $ IFS=$old_IFS
    $ echo $A
    abc
    
    $ a=";;;test"
    $ IFS=";"
    $ echo $a
       test
    $ a="   test"
    $ echo $a
       test
    $ IFS=" "
    $ echo $a
    test
    
    $ old_IFS=$IFS
    $ read A
    ;a;b;c
    $ echo $A
    ;a;b;c
    $ IFS=";"  #Note2
    $ echo $A
    a b c
    

    4. read获取文件的问题

    • 如果行的起始部分有 IFS 之类的字符,将被忽略;
    • echo $i的解析过程中,首先将 $i 替换为字符串, 然后对 echo 字符串中字符串分词,然后命令重组,输出结果; 在分词,与命令重组时,可能导致多个相邻的 IFS 转化为一个;
    • 解决方案:
    old_IFS=$IFS
    IFS=; #将IFS设置为null
    cat file | while read i
    do
      echo "$i"
    done
    IFS=old_IFS #恢复IFS的原始值
    

    或使用od,hexdump

    5. 变量

    5.1. 设定变量的规则

    • 变量名首字符不能是数字
    • 区分大小写

    5.2. 变量替换

    • 成为命令
    $ A=ls
    $ B=la
    $ C=/tmp
    $ $A -$B $C
    
    • 变量扩充
    A=B:C:D
    A=$A:E
    
    注意要有分隔符:,要不然就`A=${A}E`
    

    5.3. export

    $ A=B
    $ B=C
    $ export $A #注意被export的是B这个变量
    

    5.4. 取消变量

    • unset A
    • 同样要注意变量替换
    • 注意unset和null的区别(用var=${name=value}能否成功赋值的区别)

    6. 进程

    • 我们所执行的任何程序,都是父进程 (parent process) 产生的一个子进程 (child process), 子进程在结束后,将返回到父进程去。此现象在 Linux 中被称为fork。
    • 环境变量只能从父进程到子进程单项传递,因此在子进程中改变环境变量(如工作环境$pwd)不会影响父进程中的环境变量。
    • 当我们执行一个 shell script 时,其实是先产生一个 sub-shell 的子进程, 然后 sub-shell 再去产生命令行的子进程。
    • 所以要用source来执行脚本,如$ source ./my_script.sh,source就是让脚本在当前shell内执行而不是产生一个sub-shell来执行。
    • exec也是让脚本在同一个进程上执行,但是原有进程被结束了。暂略

    7. ()与{}

    • ()将command group置于sub-shell(子shell) 中去执行,也称 nested sub-shell。
    • {}则是在同一个shell内完成,也称non-named command group。
    • 可以定义function
    function function_name {
        command1
        command2
        command3
        .....
    }
    
    function_name () {
        command1
        command2
        command3
        ......
    }
    
    • 通过source命令, 我们可以自行定义许许多多好用的 function,再集中写在特定文件中, 然后,在其他的 script 中用source将它们载入,并反复执行。

    8. $()

    • “捕获”,和` `很大程度上等价
    • 命令替换command1 $(commmand2 $(command3))
    • 界定变量名称
    • 剪切字符串
      • 定义一个变量file=/dir1/dir2/dir3/my.file.txt
      • 非贪婪左删除${file#*/} #其值为:dir1/dir2/dir3/my.file.txt;${file#*.} #其值为:file.txt
      • 贪婪左删除${file##*/} #其值为:my.file.txt
      • 非贪婪右删除${file%/*} #其值为:/dir1/dir2/dir3
      • 贪婪右删除${file%%/*} #其值为:其值为空。
    • 取字符串:${s:pos:length}, 取字符串 s 从 pos 位置开始(含)的长度为 length 的子串
    • 字符串变量值的替换
      • 首次替换:${s/src_pattern/dst_pattern} 将字符串s中的第一个src_pattern替换为dst_pattern
      • 全部替换:${s//src_pattern/dst_pattern} 将字符串s中的所有出现的src_pattern替换为dst_pattern
    • 针对变量的不同状态(未设定、空值、非空值)进行赋值
      • 一般地,不带:,null值不受影响;带:,null值也受影响(:+除外)
      • ${file-my.file.txt} #如果file没有设定,则使用my.file.txt作为返回值,空值及非空值时,不作处理,返回${file};
      • ${file:-my.file.txt} #如果file没有设定或者file为空值,使用my.file.txt作为其返回值,为非空值时,不作处理,返回${file};
      • ${file+my.file.txt} #如果file已设定(为空值或非空值), 则使用my.file.txt作为其返回值,未设定时,不作处理;
      • ${file:+my.file.txt} #如果${file}为非空值, 则使用my.file.txt作为其返回值,未设定或者为空值时,不作处理。
      • ${file=my.file.txt} #如果file未设定,则将file赋值为my.file.txt,同时将file作为其返回值,file已设定(为空值或非空值),则返回${file}
      • ${file:=my.file.txt} #如果file未设定或者为空值,则my.file.txt作为其返回值,同时,将file赋值为my.file.txt,为非空值时不作处理。
      • ${file?my.file.txt} #如果file未设定,则将my.file.txt输出至STDERR, 若已设定(空值与非空值时),不作处理。
      • ${file:?my.file.txt} #若果file未设定或者为空值,则将my.file.txt输出至STDERR,否则, 非空值时,不作任何处理。
    • 计算shell字符串变量的长度:${#var}
    • bash数组的处理方法(暂略)

    9. $(())

    • 用来做整数运算
    $ a=5; b=7; c=2;
    $ echo $(( a + b * c ))
    19
    $ echo $(( (a + b)/c ))
    6
    $ echo $(( (a * b) % c ))
    1
    

    10. positional parameter

    • $0,$1,$2...
    • ${10}
    • $#看参数数量,因此用[$#=0]可以判断脚本有没有读入参数

    11. @与*

    my_fun() {
        echo "$#"
    }
    echo 'the number of parameter in "$@" is ' $(my_fun "$@")
    echo 'the number of parameter in "$*" is ' $(my_fun "$*")
    
    • "$@" 则可得到 "p1" "p2 p3" "p4" 这三个不同字段 (word);
    • "$*" 则可得到 "p1 p2 p3 p4" 这一整个单一的字段。

    12. RV

    • shell 下跑的每一个 command 或 function, 在结束的时候都会传回父进程一个值,称为 return value。
    • 变量$?中储存了最新的返回值
    • 进程的退出状态有两种
      • 0值为真
      • 非0值为假
    • &&与||
      • command1 && command2 # command2 只有在 command1 的 RV 为 0(true) 的条件下执行。
      • command1 || command2 # command2 只有在 command1 的 RV 为非 0(false) 的条件下执行。

    13. test

    • test expression 或 [ expression ]
    • 支持的测试对象
      • string:字符串,也就是纯文字
      • integer:整数
      • file:文件
    • 以 A=123 这个变量为例:
      • [ "$A" = 123 ] #是字符串测试,测试 $A 是不是 1、2、3 这三个字符。
      • [ "$A" -eq 123 ] #是整数测试,以测试 $A 是否等于 123。
      • [-e "$A" ] #文件测试,测试 123 这份文件是否存在。
    • test也允许多重复合测试:
      • expression1 -a expression2 当两个 expression 都为 true,返回 0,否则,返回非 0;
      • expression1 -o expression2 当两个 expression 均为 false 时,返回非 0,否则,返回 0;
      • [ -d "$file" -a -x "$file" ]表示当 $file 是一个目录,且同时具有 x 权限时,test 才会为 true。
    • 在 command line 中使用test时,请别忘记命令行的 “重组” 特性, 也就是在碰到 meta 时,会先处理 meta,在重新组建命令行。若在test中碰到变量替换,用soft quote是最保险的。

    14. >与<

    • File Descriptor(fd)

      • 0:standard Input (STDIN)
      • 1: standard output (STDOUT)
      • 2: standard Error output (STDERR)
    • I/O redirection

      • < 输入重定向
      • > 输出重定向
        • 1>
        • 2>
        • 需要将两个信息输进同一个文件时:1>&2或2>&或&>
      • /dev/null
      • >>不覆盖,而是追加
      • set -o noclobber限制覆盖,改为+复原
      • cat < file > file清空原有文件(输出优先于输入),若无该文件,创建新的空文件,其实>file就可以了
    • 又要管道又要重定向时,用tee代替>:cmd1 | cmd2 | tee file | cmd3

    • file I/O 是最常见的效能杀手,要尽量降低频度。

    15. if与case

    if cmd1
    then
        cmd2
        cmd3
    else
        cmd4
        cmd5
    fi
    

    在if的判断式中,else部分可以不用,但then是必需的,若then后不想跑任何command,可用:这个null command代替。当然,then或else后面,也可以再使用更进一层的条件判断式, 这在shell script的设计上很常见。 若有多项条件需要"依序"进行判断的话, 那我们则可使用elif这样的keyword:

    if cmd1; then
        cmd2;
    elif cmd3; then
        cmd4
    else
        cmd5
    fi
    

    意思是说:若cmd1为true,然则执行cmd2; 否则在测试cmd3,若为true则执行cmd4; 倘若cmd1与cmd3均不成立,那就执行cmd5。

    /etc/init.d/*中的那堆script中的case用法中的一例

    case "$1" in
        start)
            start
            ;;
        stop)
            stop
            ;;
        status)
            rhstatus
            ;;
        restart|reload)
            restart
            ;;
        condrestart)
            [ -f /var/lock/subsys/syslog ] && restart || :
            ;;
    
        *)
            echo $"Usage: $0 {start|stop|status|restart|condrestart}"
            exit 1
    esac
    

    16. 循环

    16.1. for loop

    for var in one two three four five
    do
        echo -----------------
        echo '$var is '$var
        echo
    done
    
    • 利用$(),for的范围可以有很多种
      • `ls data/*.txt`
      • `cat file`

    16.2. while

    num=1
    while [ "$num" -le 10 ]; do
        echo "num is $num"
        num=$(($num + 1))
    done
    

    while loop的原理与for loop稍有不同: 它不是逐次处理清单中的变量值, 而是取决于while 后面的命令行的return value

    16.3. until

    num=1
    until [ ! "$num" -le 10 ]; do
        echo "num is $num"
        num=$(($num + 1))
    done
    

    16.4. break & continue

    • 常用于复合式循环
    • break是结束loop(return是结束function,exit是结束script/shell)
    • continue:在continue在done之间的句子略过而返回到循环的顶端
    • break和continue后面可以跟一个数值,表示中断/进入从里向外的第n个循环

    17. wildcard与RE暂略

    最后,向大家隆重推荐生信技能树的一系列干货!

    1. 生信技能树全球公益巡讲:https://mp.weixin.qq.com/s/E9ykuIbc-2Ja9HOY0bn_6g
    2. B站公益74小时生信工程师教学视频合辑https://mp.weixin.qq.com/s/IyFK7l_WBAiUgqQi8O7Hxw
    3. 招学徒https://mp.weixin.qq.com/s/KgbilzXnFjbKKunuw7NVfw

    相关文章

      网友评论

          本文标题:shell脚本和shell十三问

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