【Linux】shell编程

版权声明:本文为博主原创文章,未经博主允许不得转载。Copyright (c) 2018, code farmer from sust. All rights reserved. https://blog.csdn.net/sustzc/article/details/82788290

    crontab -l           #查看当前计划任务
    crontab -e           #自动打开vim,相当于创建计划任务
    crontab -r           #删除计划任务
    crontab -u 用户名    #指定用户
    service cron restart #重启cron服务
    
        *表示取值范围内的数字
        /表示每
        -表示从某个数字到某个数字
        ,表示分开几个离散的数字
    
        # 分 时 日 月 星
          20 21 * * 1-5 /path/test.sh >> /path/test.log 2>&1
                                                    标准出错重定向到标准输出
          * * * * * date >> ~/date.log

    特点
        1.解释非编译型
        2.弱类型(不做类型区分,可以发生隐式类型转换)
        3.执行模式:交互式/批处理式
        
    C语言是静态弱类型语言  (静态:编译过程中变量类型不能被修改)
    C++是静态强类型语言
    shell脚本是动态弱类型语言
    Python是动态强类型语言
    
    eg: first.sh
        #!/bin/bash
        echo "hello world!"
    
    #!是一个约定的标记,它告诉系统这个脚本需要什么解释器来执行,即使用哪一种Shell。
通常,(#!)的名称,叫做”Shebang”或者”Sha-bang”。echo命令用于向窗口输出文本。
    #后面加注释
    
    执行方式
        1.  #chmod +x first.sh   
            #./first.sh
        2.    #/bin/bash first.sh
            或者  #bash first.sh
            这样执行的话就不需要在第一行指定解释器信息.       /usr/bin/sh -> bash
            
    eg:
        pwd
        cd ..
        pwd
            sh first.sh  虽然会打印出pwd的执行结果,但是cd ..到上个目录并没有被执行
                sh创建bash父进程的子进程,在子进程切换目录后并没有把执行结果带回父进程。
            source first.sh  回到上级目录   . first.sh同理
                source直接使用父进程bash来操作,因此可以切换目录。
            
    执行本质原理
    
    第一种执行方式: chmod +x first.sh。Shell会fork一个子进程并调用exec执行./first.sh这个程序,exec系统调用应该把子进程
的代码段替换成./first.sh程序的代码段,并从它的_start开始执行。然而first.sh是个文本文件,根本没有代码段和_start函数!
怎么办呢? 其实exec还有另外一种机制,如果要执行的是一个文本文件,并且第一行用Shebang指定了解释器,则用解释器程序的代码段
替换当前进程,并且从解释器的_start开始执行,而这个文本文件被当作命令行参数传给解释器。
    交互Shell(bash) fork/exec一个子Shell(sh)用于执行脚本,父进程bash等待子进程sh终止。sh读取脚本中的cd ..命令,
调用相应的函数执行内建命令,改变当前工作目录为上一级目录。sh读取脚本中的ls命令,fork/exec这个程序,
列出当前工作目录下的文件,sh等待ls终止。ls终止后,sh继续执行,读到脚本文件末尾,sh终止。sh终止后,bash继续执行,打印提示符等待用户输入。

    变量(类似于宏,字符串替换)
        定义变量时不加空格。
        注意:
            rm -rf /$mydir  一定要注意查看变量mydir里面存放了什么,否则就被认为是空串导致删除根目录。
            变量名外面的花括号是可选的,加不加都行,加花括号是为了帮助解释器识别变量的边界。 推荐给所有变量加上花括号。
        字符串拼接时就必须加上花括号。
            eg: echo ${mystr1}hello     # 防止变量名产生歧义,加上{}
                echo "$mystr1 $mystr2"  # 打印对应的值
                echo '$mystr1 $mystr2'  # 单引号打印$mystr1 $mystr2
                    单引号不会对变量进行求值,双引号会对变量的内容进行求值。
        只读变量
            使用readonly命令可以让变量变为只读变量,只读变量的值不能被改变。
        删除变量
            unset命令不能删除只读变量。
        变量的分类
            本地变量: 局部变量在脚本或命令中定义,仅在当前shell实例中有效,其他shell启动的程序不能访问局部变量。
            环境变量: 所有的程序,包括shell启动的程序,都能访问环境变量,有些程序需要环境变量来保证其正
        常运行。必要的时候shell脚本也可以定义环境变量。
            shell变量: shell变量是由shell程序设置的特殊变量。shell变量中有一部分是环境变量,有一部分是局部
        变量,这些变量保证了shell的正常运行。
        
        获取字符串长度  #变量名
            mystr="hehe"
            echo ${#mystr}
        提取子字符串
            mystr="abcd1234"
            echo ${mystr:1:4}   # 从第二(相当于数组下标1)个字符开始提取4个字符bcd1
        查找字符串
            mystr="bit is a great company"
            index=`echo $mystr | awk '{i=index($0, "great"); print i;}'`   # $0表示第一个参数  从1开始计数 返回10就是g出现的下标
        通配符
            * : 匹配0个或多个任意字符
            ? : 匹配一个任意字符
            [若干字符] : 匹配方括号中任意一个字符的一次出现(或的关系)
            
            在参数还没传给程序之前已经展开了, 比如上述ls file[1-5]命令,如果当前目录下有file1~file5,
        则传给ls命令的参数实际上是这5个文件名,而不是一个匹配字符串。
        
        命令替换和算术替换
            由反引号``括起来的也是一条命令,shell先执行该命令,然后将输出结果立刻替换到当前命令行中。
            命令代换也可以用$() 表示: DATE=$(date +%Y:%m:%d)
            (()) 中的shell变量取值将转换成整数,常用于算术计算 +-*/% ++ --和()运算,并且只能做整数运算。
                ((val=1+1))  # 使用双圆括号时,操作数之间可以加空格
                echo $val
                
                let val=1+1  # 使用let命令时,操作数之间不允许出现空格
                echo $val
        转义字符
            touch \$\ \$  #   创建$ $文件
            另外,还有一个字符虽然不具有特殊含义,但是要用它做文件名也很麻烦,就是-号。如果要创建一个文件名以-号开
        头的文件,这样是不行的: 即使加上\转义也还是报错: 因为各种UNIX命令都把-号开头的命令行参数当作命令的选
        项,而不会当作文件名。如果非要处理以-号开头的文件名,可以有两种办法:
            1. touch -- -file
            2. touch -- ./-file
            \还有一种用法,在\后敲回车表示续行,Shell并不会立刻执行命令,而是把光标移到下一行,给出一个续行提示符>,等待
        用户继续输入,最后把所有的续行接到一起。
            :表示退出码
        单引号和双引号的区别
            双引号用于保持引号内所有字符的字面值(回车也不例外),但以下情况除外:
                $加变量名可以取变量的值
                反引号仍表示命令替换
                $表示$的字面值
                `表示`的字面值(反引号)
                \"表示"的字面值
                \表示\的字面值 除以上情况之外,在其它字符前面的\无特殊含义,只表示字面值。
                
    条件测试
        常见的测试命令包含test或[ ,通过检查该类命令的退出码,决定条件测试是否成立。
        eg:
            read myint  # 标准输入读取myint
            test $myint -eq 100  # 这里test后面的参数是命令行参数,需要用空格隔开
                [ $myint -eq 100 ] # 注意这里[]之间的空格
            echo $? # 打印上个进程的退出码
        shell认为退出码为0表示测试条件成立,非0表示测试条件不成立。
        
        整数测试
            -eq(等于equal) -ne(不等于not equal) -lt(小于less than)
            -gt(大于greater than) -le(小于等于less equal)  -ge(大于等于greater equal)
        字符串测试
            ==(相等,注意用空格间隔) !=(不相等) -z(判断空字符串) -n(非空)
                [ $mystr=='hehe' ] #这里相当于[]内部的是个整体,不再是多个命令行参数了,因此一直为真。
                    如果mystr输入的是回车符,那么会报语法错误,就变成了[ == 'hehe' ],实际上,读取变量相当于宏替换。
                    修改为:  [ 'X'$mystr == 'Xhehe' ]  相当于添加一个字符,不管输入是不是回车,都可以。
                        sh -x second.sh   #可以定位到错误,并且把每行执行的命令内容以及替换后的内容显示出来。
                [ -z $mystr ]
                [ -n $mystr ]        
        文件测试(实际上也可以判断该文件是否存在)
            -d -f -b -c
                c字符文件 /dev/stdout -> 最终指向的文件就是字符文件(只能一个一个地读取)
                    [ -c /dev/pts/3 ]
                b块存储文件(磁盘)  可以随机读取
                    [ -b /dev/sda ]
                d目录文件
                f普通文件
        多条件测试
            逻辑非!  注意要加空格,否则就是字符串拼接
                [ ! $myint == 10 ]
            逻辑与-a  (and)
                [ $myint1 -eq 1 -a $myint2 -eq 2 ]
            逻辑或-o  (or)
                [ $myint1 -eq 1 -o $myint2 -eq 2 ]
    条件分支
        if/then/elif/else/fi
            read myint
            if [ $myint -eq 100 ]
            then
                echo "success"
            fi
        如果两条命令写在同一行则需要用;号隔开,比如后面加上then,一行只写一条命令就不需要写;号了。
            存在if字眼的地方下面都要加then。
            read myint
            if [ $myint -eq 100 ]
            then
                echo "equal 100"
            elif [ $myint -lt 100 ]
            then
                echo "less than 100"
            else
                echo "greater than 100"
            fi
        grep
            -E使用扩展正则匹配
            -q使用安静模式匹配 (不打印查找结果,只反映找没找到)
            grep查找结果是有返回值的,匹配到就返回0,反之返回非0。
            
            : 是一个特殊的命令,称为空命令,不做任何事,但Exit Status总是真。此外,
        也可以执行/bin/true或/bin/false得到真或假的Exit Status。
            
        || 和 &&
            &&相当于"if...then...",而||相当于"if not...then..."。&&和||用于连接两个命令,而上面讲的-a和-o仅用于在测试表
        达式中连接两个测试条件,要注意它们的区别。
            make clean && make -j 8    # -j表示开启多线程模式,开辟8个线程   如果清空成功,则执行多线程
            
        case/esac
            Shell脚本的case可以匹配字符串,末尾必须以;;结束(类似于break)。
                myval="start"
                case $myval in
                    "start" )
                        echo "start..."
                        ;;
                    "stop" )
                        echo "stop..."
                        ;;
                    * )
                        echo "default"
                        ;;
                esac
            $1 是一个特殊变量,在执行脚本时自动取值为第一个命令行参数。$0是可执行文件的名字。
                [Ss]tart | "-s" )   #多个选项并且单个选项中某些字符可选
            nohup ./http_server 0 9000 & #启动守护进程
            
            server_pid=`pidof http_server`  #获取pid
            kill -9 $server_pid
        
        循环
            for循环
                for i in {1..100} {a..z}  #如果不加空格打印出来的是两个序列的排列组合
                do
                    touch file.$i  
                done
            ---------------------------------------    
                for ((i=0; i<=10; i++))
                do
                    echo "hello $i"   # echo加上-n选项不换行  
                done
            while循环
                while循环,一定要注意负责进行索引的自增。
                    i=0
                    while [ $i -lt 5 ]
                    do
                        echo "$i"
                        ((i++))
                        #let i++
                    done
            until循环
                与while循环判断条件相反
            死循环
                for ((;;))
                do
                    echo "hha"
                done
            ------------------------
                while :           # while true  与  until false 都等价于左边的while :
                do
                    echo "hah"
                done
            不允许出现空语句。
            
            命令行循环
                i=0; while [ $i -le 10 ]; do echo "hello bit $i"; let i++; done
            
                计算1-100的和
                    i=1
                    sum=0
                    str=''
                    for ((i = 1; i <= 100; ++i))
                    do
                        if [ -z $str ]
                        then
                            str=$i
                        else
                            str=$str"+"$i
                        fi
                        let sum+=i
                    done
                    echo $str"="$sum
                    
                1-100的奇数和
                    i=1
                    sum=0
                    str=''
                    while [ $i -le 100 ]
                    do
                        let sum+=i
                        if [ -z $str]
                        then
                            str=$i
                        else
                            str=$str"+"$i
                        fi
                        let i+=2
                    done
                    echo $str"="$sum
                    
        位置参数和特殊变量
            $0相当于argv[0],也就是命令行参数
            $?表示上一条命令的退出状态
            $#相当于argc-1
            $@表示参数列表$1 $2 ...例如可以用在for循环中的in后面
            $$当前shell的进程id
        即使越界,也不会崩溃。
        
        shift
            位置参数可以用shift命令左移,不带参数的shift命令相当于shift 1,再比如shift 3表示原来的$4变成$1。
            $0一直不变,就是可执行文件。    
            shift 3相当于把左边三个命令行参数挤出去。
                echo "\$0 -> $0"
                echo "\$1 -> $1"
                echo "\$2 -> $2"
                echo "\$3 -> $3"
            ---------------------------------
                shift 1
            ---------------------------------
                echo "\$0 -> $0"
                echo "\$1 -> $1"
                echo "\$2 -> $2"
                echo "\$3 -> $3"
            
            命令行输入 ./third.sh a b c
            
                $0 -> ./third.sh
                $1 -> a
                $2 -> b
                $3 -> c
                $0 -> ./third.sh
                $1 -> b
                $2 -> c
                $3 ->
        
        遍历命令行参数
            for i in $@      # $@表示参数之间用空格隔开
            do
                echo "$i"
            done
        ----------------------------------
            while [ $# -ne 0 ]
            do
                echo $1
                shift 1
            done
    
    函数
        函数定义中没有返回值也没有参数列表。函数就相当于一个命令,也可以看做是一个迷你版的脚本。
        Shell脚本中的函数必须先定义后调用,一般把函数定义都写在脚本的前面,把函数调用和其它命令写在脚本的最后(类
    似C语言中的main函数,这才是整个脚本实际开始执行命令的地方)。
        shell函数没有参数列表并不表示不能传参数,事实上,函数就像是迷你脚本,调用函数时可以传任意个参数,在函数内同样
    是用$1、$2等变量来提取参数。
        传参相当于命令行传参,不用加括号,没有形参的概念,函数名相当于可执行程序。
        缺点:
        试验表明,$0 并不会作为函数参数,从事传参任务。
        函数中的位置参数相当于函数的局部变量,改变这些变量并不会影响函数外面的$1 、$2 等变量。
        函数调用或者返回时,将shell函数当成命令。只要是命令,那么函数调用成功与否,可以通过$?来判定。一般函
    数中可以用return命令返回,如果return后面跟一个数字则表示函数的ExitStatus。
        function myfun()
        {
            return 11
        }
        echo $?

    eg:
        function myfun()
        {
            echo "hehe"
            echo "$1"
            echo "$2"
            echo "$3"
        }
        myfun aa bb cc
        
            注意函数体的左花括号{和后面的命令之间必须有空格或换行,如果将最后一条命令和右花括号}写在同一行,命令末尾必须有;号。
            如果没有参数的话只需要函数名就可以调用函数了,有参数的话就传入位置参数。
        function myfunc()
        {
            echo "hehe"
        }

        val=`myfunc`    # ret=$(myfunc)
        echo $val
            函数返回字符串  (使用``来接收)
    shell脚本的调试
        -n检查脚本的语法错误
        -v一边执行脚本,一边将执行的脚本命令打印到标准错误输出
        -x提供跟踪执行信息,将执行的每一条指令和结果依次打印出来
        set -x和set +x分别表示开启和禁用-x参数,也可以只对脚本的某一段进行跟踪调试(-x +x)。   使用bash -x third.sh
            当然也可以在脚本开始she-bang处加上-x
        
    数组
        Bash Shell只支持一维数组(不支持多维数组),初始化时不需要定义数组大小(与 PHP类似),并且没有限定数组的大小。
    与大部分编程语言类似,数组元素的下标由0开始。获取数组中的元素要利用下标,下标可以是整数或算术表达式,其值应大于或等于0。
        Shell数组用括号来表示,元素用"空格"符号分割开,可以不使用连续的下标,而且下标的范围没有限制。
            array_name=(value1 value2 value3 ... valuen)
            读取数组指定元素
                ${array_name[index]}
                
            eg:
                array=(1 10 3.14 'a')
                echo ${array[0]}
                echo ${array[1]}
                echo ${array[2]}
                echo ${array[3]}
                
                echo ${#array[@]} #数组长度 数组元素可以不连续
                echo ${array[@]} #数组元素列表
    shell与文件
        输出重定向 >  (覆盖式的重定向)
        追加重定向 >>
            for ((i=1; i<=10; ++i))
            do
                echo $i >> test.txt
            done
            -------------------------
            for ((i=1; i<=10; ++i))
            do
                echo $i
            done > test.txt   # 相当于只输出一次
            -------------------------
        输入重定向 <
            while read line      # 循环读取一行
            do
                echo $line
            done < test.txt
            
        字符串变换
            while read line
            do
                echo "hello"$line
            done < test.txt > test2.txt    # 先输入重定向后输出重定向
        find / -name vim 2>test.txt   # 将出错信息重定向到test.txt,其中2>是粘在一起的
        find / -name vim 2>>test.txt  # 追加输出错误信息
            make的出错信息也可以重定向
            
        Here Document
            用来将输入重定向到一个交互式 Shell 脚本或程序,它的作用是将两个 delimiter 之间的内容(document) 作为输入传递给command
            语法:
                command << delimiter
                    document
                delimiter
            结尾的delimiter 一定要顶格写,前面不能有任何字符,后面也不能有任何字符,包括空格和 tab 缩进。
            开始的delimiter前后的空格会被忽略。

        

           

猜你喜欢

转载自blog.csdn.net/sustzc/article/details/82788290