Shell(二)

作者: 冰菓_ | 来源:发表于2022-07-11 18:43 被阅读0次
    什么是Shell
    shell 是命令解释器,用于解释用户对操作系统的操作。
    
    shell有很多
        cat /etc/shells
    
    CentOS 7 默认使用的shell是bash
    
    UNIX的哲学:一条命令只做一件事情
    为了组合命令和多次执行命令,使用脚本文件来保存需要执行的命令
    赋予该文件执行权限(chmod u+rx filename)
    
    Shell脚本
    Sha-Bang
    命令
    “#”号开头的注释
    chmod u+x filename 可执行权限
    
    
    执行命令
    bash ./filename.sh   会生成一个子进程(不需要执行权限)
    ./filename.sh   会生成一个子进程,使用Sha-Bang (需要可执行权限)
    source ./filename.sh  在当前进程运行
    . filename.sh (点之后会产生一个子进程)
    
    比如一个脚本如下:
    #! /bin/bash
    cd /tmp
    
    在/home目录下运行
    bash ./filename.sh 运行,子进程运行cd,然后子进程结束,此时pwd,还是在 /home 而不是/tmp
    
    source ./filename.sh 或者 . filename.sh,这两个在/tmp,不产生子进程
    
    内建命令和外部命令的区别
    内建命令不需要创建子进程,内建命令比如source
    内建命令对当前shell生效
    
    管道和重定向

    Shell管道是Shell中最值得称赞的功能之一,它以非常简洁的形式实现了管道的进程间通信方式,我个人认为Shell处理文本数据的半壁江山都来自于竖线形式的管道。像其它编程语言,打开管道后还要区分哪个进程写管道、哪个进程读管道,为了安全,每个进程还要关闭不用的读端或写端,总之就是麻烦,而Shell的管道非常简洁,竖线左边的就是写管道的,竖线右边的就是读管道的。

    管道和信号一样,也是进程通信的方式之一
    匿名管道(管道符)是 Shell 变成经常用到的通信工具
    管道符是“|”,将前一个命令执行的结果传递给后面的命令
    ps | cat
    ps aux | grep 'sshd'
    echo 123 | psecho 123 |cat |cmd 可以连续使用
    

    注意一下输出重定向符

    一个进程默认会打开标准输入、标准输出、错误输出三个文件描述符
    输入重定向符号 “<”
    read var < /path/to/a/file
    read var1 < a.txt
    
    输出重定向符号  ">"  ">>"  "2>" "&>"
    
    echo 123 > /path/to/a/file   清空再输入
    echo 123 >> /path/to/a/file  追加
    2> 命令执行过程中有错误则重定向
    &> 命令执行过程中无论正确错误都重定向 
    
    输入和输出重定向组合使用
    cat > /path/to/a/file.sh << EOF
    i am $USER
    EOF
    
    运行以上三句话,会生成一个file.sh文件,文件里内容为i am $USER
    
    变量

    变量的赋值

    变量名的命名规则
    
    字母、数字、下划线
    不以数字开头
    
    变量的赋值
    
    为变量赋值的过程中,称为变量替换
        变量名=变量值
        a=123  (不允许出现空格,shell会认为前面不是变量名而是一个命令,比如reboot =1),会重启
    
    使用let为变量赋值
        let a=10+20 (尽量少用,效率很低)
    
    将命令赋值给变量
        l=ls  (用处不大)
    
    将命令结果赋值给变量,使用$() 或者 ''
        letc=$(ls -l /etc)
        letc='ls /root'
    
    变量值有空格等特殊字符可以包含在 "" 或 '' 中
        srring1='hello bash'
        string2="hello I'm name"
    

    变量引用和作用范围

    变量的引用
        ${变量名}称作对变量的引用
        echo ${变量名} 查看变量的值
        ${变量名} 在部分情况下可以省略为 $变量名
    
    变量的作用范围
        变量的默认作用范围
            当前的shell,父进程的变量对子进程无效,子进程的对父进程也无效。
            可以使用source
        变量的导出
            export ,子进程可以获得父进程的变量
            export demo_var1="hello subshell"
        变量的删除
            unset
            unset demo_var1
    

    系统环境变量,预定义变量,位置变量

    环境变量:每个Shell打开都可以获得到的变量
        set 和 env 命令
            env |  more 查看当前所有的环境变量
            echo ${HOME}  查看单个环境变量
        $PATH 当前命令的搜索路径
            所以要在$PATH 中新增路径,使用PATH=$PATH:新加路径(只对当前终端生效,对子shell生效)
        $PS1  当前提示终端
    
    预定义变量
            echo $?  $$ $0等
            $?   指上一条命令是否正确执行,echo $?,正确执行返回0,错误1
            $$   显示当前进程 PID
            $0   显示当前进程名称
    
    位置变量
        $1 $2 ... ${10},需要有{}
    
    比如有脚本 test.sh]如下:
    #! /bin/bash
    pos1=$1
    pos2=$2
    echo $pos1
    echo $pos2
    
    执行的时候,./test.sh -a -l
    会传参进去,可以对脚本进行简化
    echo $1
    echo $2
    
    考虑到echo $2 的时候 $2 有可能是空值。所以写成下面这样
    pos1=$1
    pos2=${2}_
    echo $pos1
    echo $pos2
    如果没有传参,则$2默认为_,规避读入的值是空值。但是这么写,如果传参进去,后面会多一个_,所以可以改成下面这样:
    pos2=${2-_}
    

    常见的环境变量

    HOSTNAME=control_node
    USER=root
    HOME=/root
    SHELL=/bin/bash
    HISTSIZE=1000
    SSH_TTY=/dev/pts/2
    PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
    MAIL=/var/spool/mail/root
    PWD=/root
    LANG=en_US.UTF-8
    

    常见的特殊变量

    $1,$2,...,$N:脚本的位置参数
    $0:shell或shell脚本的名称  
    $*:扩展为位置参数,"$*"会将所有位置参数一次性包围引起来,"$*"等价于"$1_$2_$3..."
    $@:扩展为位置参数,"$@"会将每个位置参数单独引起来,"$@"等价于"$1" "$2" "$3"...
    $#:位置参数的个数
    $$:当前Shell的进程PID,在某些子Shell(如小括号()开启的子Shell)下,会被继承。如果可以,建议使用$BASHPID替代$$
    $?:最近一个前台命令的退出状态码  
    $!:最近一个后台命令的进程PID
    $-:当前Shell环境的一些特殊设置,比如是否交互式
    $_:最近一个前台命令的最后一个参数(还有其它情况,该变量用的不多,所以不追究了)
    

    需要注意一下的👍

    关于$* "$*" $@ "$@"的区别,参见如下shell脚本测试:
    
    #!/bin/bash
    
    echo '$*----------':
    for i in $*;do echo "<$i>";done
    
    echo '"$*--------"':
    for i in "$*";do echo "<$i>";done
    
    echo '$@----------':
    for i in $@;do echo "<$i>";done
    
    echo '"$@--------"':
    for i in "$@";do echo "<$i>";done
    
    
    $ chmod +x position_parameters.sh
    $ ./position_parameters.sh a b 'c d'
    $*---------:
    <a>
    <b>
    <c>
    <d>
    "$*--------":
    <a b c d>
    $@----------:
    <a>
    <b>
    <c>
    <d>
    "$@--------":
    <a>
    <b>
    <c d>
    
    

    环境变量配置文件

    配置文件
    /etc/profile
    /etc/profile.d/
    ~/.bash_profile
    ~/.bashrc
    /etc/bashrc
    
    所以经常在 /etc/profile 中新增 export PATH=$PATH:/new/path
    su - 切换用户会加载4个文件
    su 切换用户只会加载~/.bashrc、/etc/bashrc
    
    数组
    定义数组
        IPTS=(10.0.0.1 10.0.0.2 10.0.0.3)
    
    显示数组的所有元素
        echo ${IPTS[@]}
    
    显示数组元素个数
        echo ${#IPTS[@]}
    
    显示数组的第一个元素
        echo ${IPTS[0]}
    
    转义和引用
    特殊字符:一个字符不仅有字面意义,还有元意(meta-meaning)
        # 注释
        ; 分号
        \ 转义符号
        " 和 ' 引号
    
    单个字符的转义
        \n \r \t 单个字母的转义
        \$ \"  \\ 单个非字母的转义
    
    引用
    "" 双引号,如果里面有变量,会进行解释
    '' 单引号不会进行解释
    ` 反引号
    
    运算符
    赋值运算符
    = 赋值运算符,用于算数赋值和字符串赋值
    使用 unset 取消为变量的赋值
    = 除了作为赋值运算符还可以作为测试操作符
    
    
    算数运算符
    
    基本运算符
    + - * / ** %
    
    使用expr进行计算
    expr 4 + 5 (要有空格,只能支持整数)
    num1=`expr 4 + 5`
    
    数字常量的使用方法,如果不用特殊方法,a=4+5,其实把 “4+5”字符串赋值给a
    let “变量名=变量值”
    变量值使用0开头为八进制,0x开头为十六进制
    
    双圆括号是let命令的简化
    ((a=4+5))
    ((a++))
    echo $((10+20))
    
    特殊符号大全
    引号
        ' 完全引用
        " 不完全引用
        ` 执行命令
    括号
        () (()) $() 圆括号
            单独使用圆括号会产生一个子shell(xyz=123)
            数组初始化 IPS=(ip1 ip2 ip3)
    
        [] [[]] 方括号
            单独使用方括号是测试(test)或数组元素功能
            两个方括号表示测试表达式
       
        <> 尖括号 重定向符号
        
        {} 花括号
            输出范围 echo{0..9},会输出0-9所有数字
            文件复制 cp -v /etc/passwd /etc/passwd.bak 
            等同于 cp -v /etc/passwd{,.bak}
    运算和逻辑符号
        +-*/% 算数运算符
        ><= 比较运算符
        && || !逻辑运算符
            (( 5 > 4 && 6> 5)),然后通过 echo $? 判断
        
    转义符号
        \
    
    其他符号
        # 注释符
        ;  命令分隔符
            case 语句的分隔符要转义 ;;
        :空指令
        .  和source命令相同
        ~  家目录
        ,  分隔目录
        * 通配符
        ? 条件测试或通配符
        $ 取值符号
        | 管道符
        & 后台运行
           空格
    
    测试和判断
    退出程序命令
        exit 判断上一条命令是否正常,0或者非0
        exit 1或者0 返回10 给shell ,返回值非0位不正常退出
        $? 判断当前shell前一个进程是否正常退出
    
    测试命令test
    
    test命令用于检查文件或者比较值
    test可以做以下测试
        文件测试 
        整数比较测试
        字符串测试
    
    test测试语句可以简化为[]符号
        test -f /etc/passwd2 判断文件是否存在并且是个普通文件,-e是文件或者目录,-d目录
        [ -d /etc/ ]
    []符号还有扩展写法 [[]] 支持 && || < >
    
    if
    if-then 语句的基本用法
        if [ 测试条件成立 ] 或 命令返回值是否为0
        then 执行相应命令
        fi 结束
    
    if-then-else 语句可以在条件不成立时也运行相应的命令
        if [ 测试条件成立 ]
        then 执行相应命令
        else 测试条件不成立,执行相应命令
        fi 结束
    
        if [ 测试条件成立 ]
        then 执行相应命令
        elif  [ 测试条件成立 ]
        then 执行相应命令
        else 测试条件不成立,执行相应命令
        fi 结束
    
    嵌套 if 的使用
    if 条件测试中可以再嵌套 if 条件测试
        if [ 测试条件成立 ]
        then 执行相应命令
            if [测试条件成立]
            then 执行相应命令
            fi
        fi
    
    case
    case 语句和 select 语句可以构成分支
    
    case "$变量" in
        "情况1" )
        命令...;;;
        "情况2" )
        命令...;;;
        * )
         命令...;;;
    esac
    
    for
    for 循环的语法
        for  参数 in 列表
        do 执行的命令
        done 封闭一个循环
    
    使用反引号或 $() 方式执行命令,命令的结果当做列表进行处理
    列表中包含多个变量,变量用空格分隔
        for i in {0..9}
        for filename in 'ls *.mp3'
    对文本处理,要使用文本查看命令取出文本内容
    默认逐行处理,如果文本出现空格会当做多行处理
    
    
    C 语言风格的 for 命令
    for((变量的初始化;循环判断条件;变量变化))
    do
      命令
    done
    
    while
    while test测试是否成立
    do
        命令
    done
    
    
    until 循环 与while 循环相反,循环测试为假时,执行循环,为真实循环停止
    
    break continue
    break 退出
    continue 结束本轮循环
    

    使用循环处理位置参数

    命令行参数可以使用 $1 $2 ... ${10}..$n 进行读取
    $0 代表脚本名称
    $* 和 $@ 代表所有位置参数
    $# 代表位置参数的数量
    
    有脚本test.sh如下:
    #! /bin/bash
    
    # help display help help
    
    for pos in $*
    do
            if [ "$pos" = "help"]; then
                echo $pos $pos
            fi
    done
    
    运行语句:
    bash test.sh a b c help
    
    
    
    while 脚本如下:
    
    #! /bin/bash
    
    while [ $# -ge 1]
    do
            if [ "$1" = "help" ]; then
                    echo $1 $1
            fi
            shift
    done
    
    shift能够参数左移
    运行语句:
    bash test.sh a b c help
    
    自定义函数
    函数用于“包含”重复使用的命令集合
    
    自定义函数
        function fname(){
        命令
        }
    
    函数的执行
        fname
    
    函数作用范围的变量
    local 变量名
    
    
    函数的参数
    $1 $2...$n
    
    checkpid() {
        local i
        for i in $* ; do
            [ -d "/proc/$i" ] && return 0
        done
        return 1
    }
    
    执行时
    checkpid 1    或者 checkpid 1 2
    echo  $?
    
    系统脚本
    系统自建了函数库,可以在脚本中引用
    /etc/init.d/functions
    
    自建函数库
    使用 source 函数脚本文件“导入”函数
    
    source /etc/init.d/functions
    echo_success
    
    脚本优先级任务
    可以使用 nice 和 renice 调整脚本优先级
    避免出现“不可控的”死循环
    死循环导致cpu占用过高
    死循环导致死机
    
    ulimit -a  可以查看当前终端的使用限制,root用户的话有些限制不会生效
    
    max user processes 用户的最大进程数
    
    任务计划
    at 18:31
    at > echo hello > /tmp/test.txt
    at> 
    
    然后ctrl +d 提交
    
    cron
        配置方式
            crontab -e
        查看现有的计划任务
            crontab -l
        配置格式
            分钟,小时,日期,月份,星期,执行的命令
            注意命令的路径问题
        日志(可以查看计划任务有没有被执行)
            /var/log/cron
        每个用户都有自己的计划任务目录
            /var/spool/cron/用户
    

    任务计划加锁

    如果计算机不能按照预期时间运行
        anacontab 延时计划任务
        flock 锁文件
    
    正则表达式和文本搜索
    元字符
    
    . 匹配除换行符外的任意单个字符
    * 匹配任意一个跟在它前面的字符
    [] 匹配方括号中的字符类中的任意一个
    ^ 匹配开头
    $ 匹配结尾
    \ 转义后面的特殊字符
    
    扩展元字符
    + 匹配前面的正则表达式至少出现一次
    ? 匹配前面的正则表达式出现零次或一次
    | 匹配它前面或后面的正则表达式
    
    
    grep 搜索
    grep test /root/test.txt
    
    cd /etc
    find passwd
        -regex 区分大小写
        -iregex 不区分大小写
    
    find /etc -name pass*
    find /etc -regex .*wd
    
    类型匹配
    find /etc -type f -regex .*wd
    时间匹配,最基础的时间戳包括:-atime/-mtime/-ctime
    find /etc/ -atime 8 -regex .*wd
    删除.txt文件
    find * txt -exec rm -v {} \;
    

    Linux find命令常用用法示例

    sed awk xargs
    sed 的基本工作方式
     -将文件以行为单位读取到内存(模式空间)
     -使用sed的每个脚本对该行进行操作
     -处理完成后输出该行
    
    sed 一般用于对文本内容做替换
    
        sed '/user1/s/user1/u1' /etc/passwd
        sed 's/old/new/' filename
        sed -e  's/old/new/' -e  's/old/new/' filename
        sed -i  's/old/new/' -i  's/old/new/' filename
    
        sed -r 's/(a*b)/\1 \1'  filename
        head -5 /etc/passwd | sed 's/...//'
        head -5 /etc/passwd | sed 's/s*sbin  //'
    
    sed指令扩展
         s/old/new标志位
        数字.第几次出现才替换
        g,每次出现都进行替换
        p,打印模式空间的内容
        sed -n 阻止默认输出
        w file 将模式空间的内容写入到文件
    
    
    sed 's/root/!!!/2' 
    sed 's/root/!!!/g' 
    sed -n 's/root/!!!/w  /tmp/a.txt'  
    
    sed '1,3s/root/!!!/2'  第一行和第三行进行替换
    sed '1,$s/root/!!!/2'  第一行到最后一行进行替换
    寻址可以匹配多条命令
    /regular/{ 's/old/new/' ; 's/old/new/'}
    可以将选项保存为文件,使用-f加载脚本文件
    sed -f sedscript filename
    
    awk 基本用法
    awk 一般用于对文本内容进行统计,按需要的格式进行输出
        cut 命令:cut -d:-f 1/etc/passwd
        awk 命令:awk -F: '/wd$/{print$1}' /etc/passwd
        
        awk '/^menu/{print $0}' /boot/grub2/grub.cfg 
        awk -F "'" '/^menu/{print $2}' /boot/grub2/grub.cfg
        awk -F "'" '/^menu/{print  x++, $2}' /boot/grub2/grub.cfg 
    
    awk 表达式
    
    系统变量
    FS和OFS字段分隔符,OFS表示输出的字段分隔符
    RS记录分隔符
    NR FNR行数
    NF字段数量,最后一个字段内容可以用$NF取出
    
     head -5 /etc/passwd | awk 'BEGIN{FS=":"}{print $1}'
     head -5 /etc/passwd | awk 'BEGIN{FS=":";OFS="---"}{print $1,$2}'
     head -5 /etc/passwd | awk '{print NR,$0}'
     awk '{print FNR,$0}'   /etc/passwd  /etc/passwd
     head -5 /etc/passwd | awk 'BEGIN{FS=":"}{print $NF}'
    
     awk '{if($2>80){print $1 ; print $2}}' kpi.txt
     head -1 kpi.txt | awk '{for(c=2;c<=NF;c++) print c}'
     head -1 kpi.txt | awk '{for(c=2;c<=NF;c++) print $c}'
    
    xargs是将标准输入转为命令行参数
    echo "a b c" | xargs mkdir
    大多数情况下xargs命令都是跟着管道一起使用的
    ,但是,也可以单独使用,在输入xargs按下回车键,
    命令行就会等待用户输入,作为标准输入,你可以输入
    任意内容然后按下ctrl + d 表示输入结束,这时echo命令就会把前面的结果打印出来
    
    -d指定分隔符
      echo -e "a\tb\tc" | xargs -d "\t" echo -n
    

    相关文章

      网友评论

        本文标题:Shell(二)

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