[toc]

SHELL编程

编程语言
机器语言 01010101
低级语言(汇编语言) 助记词(转义器 将助记词编译成机器语言)
高级语言 跨平台

        编译型(编译器)      将标准文档转成对应平台的汇编
            C / C++

        解析型(解析器)      将文档实时转义成平台指令
            Shell PHP Python JavaScript, GO

        编译解析型(编译器/虚拟机)
            Java C# PHP Python JS, 易, OC, swift

编程入口
    编辑文档, 文档的第一行开始,逐行解析

SHELL解析器     /bin/bash
    #!/bin/bash
    快捷键
        快捷键模式
            emacs       默认
                ctrl + a
                ctrl + e
                ctrl + d
                ...
            vi

            临时更换模式: set -o vi

        tab键补全

特殊符号

        *           通配符
        \           转义符, 去除后面字符的特殊意义
        ""          批量转义符, 去除引号引起来的内容大部分特殊字符的特殊意义
        ''          批量完成转义符, 去除引号引起来的内容所有特殊字符的特殊意义
        ``          引起来内容当作shell命令去执行并且将执行标准输出替换, 尽量使用$()
                    $()可以嵌套, 反引号不能嵌套
                    ``和$()都是开新进程去执行

        {}          展开,代表多个的意思
            字符串截取
            数组的增. 删. 改. 查
            变量的默认值
            正则表达式匹配
            awk
        ()          子程序
            $()
        &           将程序放入后台执行(多进程)
        !           命令历史特殊符号
        ~           当前登录用户的家目录
        /           根
        #           注释符
        -           选项(标准输入)
        $           组合使用, 访问变量
        |           管道
        < > >>..    重定向
        .           当前目录
        ..          上一级目录
        [  ]        判断表达式
        :           永真(if while)
        ;           一条命令的结束
        &&          逻辑与
            前面的是"真"的才是会执行后面的
        ||          逻辑或
            前面的是"假"的才是会执行后面的

    命令历史
        查看命令历史
            history
            history 10

        存储命令历史
            history -w /tmp/history.txt

        读取命令历史
            history -r /tmp/history.txt

        删除命令历史
            history -c

        执行命令执行
            执行最后一条命令历史
                !!

            执行109的命令
                !109

            执行root开头的命令
                !root

            获取最后一条命令历史的最后一个参数
                !$

        命令历史的环境变量
            HISTFILE        控制命令历史默认写入的文件(~/.bash_history)
                echo $HISTFILE  打印环境变量的值
            HISTFILESIZE    控制命令历史写入文件的最大条目
                echo $HISTFILESIZE  打印环境变量的值
            HISTSIZE        控制命令历史最大条目数
            HISTTIMEFORMAT  控制命令历史时间显示格式
                export HISTTIMEFORMAT="%F %T "

命令别名 alias

        简化命令的输入, 只影响当前终端

        创建命令别名
            格式: alias 别名='执行命令名字及参数'
            例:
                alias i='ifconfig enp0s25'
                alias p='ping -c 3 192.168.2.1'
                alias pb='ping -c 3 www.baidu.com'

        删除命令别名
            格式: unalias 别名

        查看命令别名
            alias


环境变量
​ 提升程序运行效率, 将系统环境相关的信息存储到变量里,减少与内核交互
​ 会影响子进程(将自己的环境变量复制给子进程)

​ 输出当前shell所有环境变量
​ env

        访问环境变量的值
            echo $环境变量名

        定义和修改环境变量的值(PPP0)
            export 环境变量名=值

        删除环境变量
            unset 环境变量名

    配置文件
        bash启动时会执行配置文件(shell脚本)
            ~/.bashrc

        当bash结束时会自动将命令历史写入~/.bash_history

学习编程语言的流程:

了解准备学习的语言概述
编程入口
变量的使用(数据类型)
操作符(操作变量) -> 语句
流程控制语句(分支 循环)
函数(代码的打包)
数组
字符串处理
I/O文件操作

变量

变量默认没有数据类型之分, 一切皆于字符串

变量 : 以一组文字或符号来替换一些比较复杂或者容易变动的数据

变量的用途

    简单的用途就是为了方便搜索
    常用于编程里,对经常使用的值使用变量,方便引用

变量的分类

    自定义变量                  不影响子进程

    环境变量                    影响子进程

自定义变量转环境变量

    export  自定义变量名

变量命名规则:
    严格区分大小写,同名称但大小写不同的变量名是不同的变量
    变量名可以是数字 字母特殊字符进行组合,
        但不能以数字和特殊字符开头, 最好别用下划线开头更不能以纯数字为变量名
    定义变量时两边不能有空格,有空格的字符串要用引号引起来
    单引号和双引号的区别,单引号会转义所有特殊字符
    尽可能描述变量存储的值(见名知意)

定义变量
    变量名=值
        =两边不能有空格
        例:
            a=123
            a="hello world"
            a=34.6778
        特殊的变量定义方式:
            var=`ls -l`     把ls -l的标准输出结果赋给var变量
            a=$(ls -l)      将命令执行标准输出赋值给a变量

更新变量值
    变量名=新值
        num1=2333
        num2=$(($num1 + 1000))  结果是3333
        num2=$[num1 + 1000]
        let num2=($num1 + 1000)   结果是3333

        str1="hello world "
        str2=$str1"hello shell"  hello world hello shell

    变量名+=追加值


获取变量值(PPP1)
    $变量名
        或
    ${变量名}

删除变量
    unset 变量名

${}的特殊访问
    解决变量与其它字符串连接混乱问题

    按位置进行截取(PPP2)
        ${变量名:起始值:截取个数}
        ${变量名:起始值:-从后面定位结束值}
        ${a:7}          从第8个开始到结束
        ${var:0-7:3}    从倒数第7个开始, 截取3个
        ${var:0-7}      从倒数第7个开始, 截取到最后

    按关键词进行截取(PPP3)(PPP4)
        ${变量名#*关键词}       截取第一个关键词后面内容
        ${变量名##*关键词}      截取最后一个关键词后面内容
        ${变量名%关键词*}       截取最后一个关键词前面内容
        ${变量名%%关键词*}      截取第一个关键词前面内容

    获取字符串长度
        ${#变量名}

    替换(PPP5)
        ${变量名/查找关键词/替换关键词}     替换第一个关键词
        ${变量名//查找关键词/替换关键词}    替换全部关键词

    变量默认值(PPP6)
        echo ${变量名-默认值}
            此变量名值为空或不存在,默认值才会使用
        num=${1-$0}

source或.   内部命令, 使用本进程执行脚本, (默认情况下执行程序都开新进程)

内置变量
    $$          代表本脚本的PID(进程标识符) (PPPz)
        终端的pid是这个终端运行的脚本的PPID,
        这个脚本中运行的sleep命令会开启子进程去运行,
        脚本退出之后,里面sleep进程还是会继续运行, 只是变成了孤儿进程
    $0          代表本脚本名
    $1          代表脚本的第一个参数
    $2          代表脚本的第二个参数
    $3          代表脚本的第三个参数
    ...
    ${10}       代表脚本的第十个参数
    ...
    $n          代表脚本的第n个参数

    $*          代表脚本的所有参数
    [email protected]          代表脚本的所有参数
        假设在脚本运行时写了三个参数 1、2、3,
        则 " * " 等价于 "1 2 3"(传递了一个参数),
        而 "@" 等价于 "1" "2" "3"(传递了三个参数).

    $?          代表上一条命令执行返回码
                (0代表正确,非0代表错误)(非0退出就是上条命令有问题)
                赋值没有返回码
                    命令结果赋值的时候, 会把命令返回码的值作为$?的值

输入/输出语句(PPP7)

echo/printf

echo(见练习echo)
    -n 不换行输出
    -e 启用反斜杠转义解释

    \c 最后不加上换行符号


printf "%d%s\n" 34 "hello"
    "%10d"      按10个空位右对齐, 整个字符占10个位置, 不足补空格
    "%-10d"     按10个空位左对齐, 整个字符占10个位置, 不足补空格
    "%010d"     按10 个空位右对齐, 前面补0
    "%10.2f"    按10个空位右对齐, 保留两位小数点

read
    -p      指定输入提示信息
    -s      关闭回显
    -n      指定输入最大字符个数
    -t      指定超时时间(秒)
    如果在read命令行中不指定变量的话,read命令会将它接收到的所有参数放到特殊环境变量REPLY中。

    read var                    等待用户给var变量赋值

    read -p "input your name:" var  提示信息等待用户给var变量赋值

    read -p "input your name:" -t 10 var  提示信息等待用户给var变量赋值,10秒不输入的话直接跳过

    read -s -p "input your password:" passwd    提示信息等待用户给passwd变量赋值并且隐藏显示

一行多条命令执行

;        以分号分隔多条命令顺序执行
&&      前面命令执行成功才会执行后面命令
||      前面命令执行失败才会执行后面命令

代码规范(可读性)

加空行      使代码具有段落感
    解析器和第一行代码之间要加空行
    假如有连续定义多行变量, 在这之后要加空行
    if/while/for/until/case 之间加空行
    函数之间加空行
    按代码功能分段加空行

缩进对齐    使代码具有层次感
    缩进量为4个空格, 遇到if/while/for/until/case语句,里面内容进行缩进对齐
    函数内部要缩进

加空格      使代码更清晰
    双目操作符两边加空格(有左值右值的操作符)

命名规则    尽量能描述存储数据或功能
    首字母小写驼峰法    gameStatus      inputNum
    首字母大写驼峰法    GameStatus      InputNum
    下划线连接          game_status     input_num

    强烈不建议使用中文和拼音

折行        一行不要超过80个字符(包含空白字符)
    使用 \ 进行连接行, \后不能跟任何字符


流程控制语句

分支(PPP8)
    格式:
        if 命令; then
            命令成功要执行的代码块
        fi

        if 命令; then
            命令成功要执行的代码块
        else
            命令失败要执行的代码块
        fi

        if 命令1; then
            命令1成功要执行的代码块
        elif 命令2; then
            命令2成功要执行的代码块
        elif 命令3; then
            命令3成功要执行的代码块
        ...
        else
            命令失败要执行的代码块
        fi

    test命令(判断 条件表达式)
        test "$a" = "$b"
            或
        [ "$a" = "$b" ]
            或
        (("$a" == "$b"))
        [[ "$a" == "$b" ]]

        得到条件表达式所有帮助
            man test

        整型数据比较: -le -lt -ge -gt -ne -eq
            -eq  等于,如:if [ "$a" -eq "$b" ]
                equal
            -ne  不等于,如:if [ "$a" -ne "$b" ]
                no equal
            -gt  大于,如:if [ "$a" -gt "$b" ]
                greater than
            -ge  大于等于,如:if [ "$a" -ge "$b" ]
                greater equal
            -lt   小于,如:if [ "$a" -lt "$b" ]
                less than
            -le  小于等于,如:if [ "$a" -le "$b" ]
                less equal
            <   小于(需要双括号),如:(("$a" < "$b"))
            <=  小于等于(需要双括号),如:(("$a" <= "$b"))
            >   大于(需要双括号),如:(("$a" > "$b"))
            >=  大于等于(需要双括号),如:(("$a" >= "$b"))

        字符串比较: = != ==  (演练)
            = 等于,如:if [ "$a" = "$b" ]
            == 等于,如:if [ "$a" == "$b" ],与=等价
            != 不等于, 如: if [ "$a" != "$b" ]



        逻辑比较: -a -o !
            !   非,后面要跟空格
            -o  或, 条件只要达成1个就是真(or)
            -a  且,条件必须全部达成才是真(and)


一些特殊的参数:

             -a     文件存在。
             -e     文件存在(与-a相同)。
             -n     字符串长度不是零。
             -z     字符串长度为零。

             -r     文件存在并且可由当前进程读取。
             -w     文件存在并且可由当前进程写入。
             -x     文件存在并且可由当前进程执行。

             -d     文件存在并且是一个目录。
             -f     文件存在并且是一个常规文件。
             -s     文件存在且大小大于零。
             -b     文件存在,是一个块特殊文件。
             -c     文件存在,是一个字符特殊文件。
             -G     文件存在,并具有与此进程相同的组ID。
             -O     文件存在,并由该进程的用户ID拥有。
             -l     文件存在,是一个符号链接。
             -p     文件存在,是一个先入先出(FIFO)的特殊文件或命名管道。
             -S     文件存在,是一个套接字。

            if test -d "/tmp"; then
                echo "判断/tmp是否存在且是个目录"
            fi

            if [ -d "/tmp"]; then
                echo "判断/tmp是否存在且是个目录"
            fi



        对变量最好使用双引号引起来, 保护起来, 以免报语法错误


case(处理菜单使用, 多路匹配)(PPP9)

        格式:
            简略版:
            case "$num" in
                A|a) echo "A";;
                B|b) echo "B";;
                C|c) echo "C";;
                *) echo "Other";;
            esac

            完整版:
            case "$num" in
                A|a)
                    echo "A"
                    ;;
                B|b)
                    echo "B"
                    ;;
                C|c)
                    echo "C"
                    ;;
                *)
                    echo "Other"
                    ;;
            esac

循环(PPP11 PPP10)

    循环控制语句
        break           退出循环
        continue        跳过本次循环
    while(演练目录)
        格式:
            while 命令(条件表达式); do
                循环代码块
            done

            命令成功或条件表达式为真才会执行循环体

    until
        格式:
            until 命令(条件表达式); do
                循环代码块
            done

            命令失败或条件表达式为假才会执行循环体

    for(用for循环去做PPP5)
        格式:
            for 迭代变量 in 迭代对象 ; do
                循环代码块
            done

            迭代对象是以空格为分隔符序列

            for i in 1 2 3 4 5 6 7 8 9 10; do
                echo $i
            done

            for i in 1 2.334 "hello" "1.txt" 5 678.45 7 8 9 10; do
                echo $i
            done

            for ((i = 0; i < 5; i++)); do
                echo $i
            done
    seq
        产生一个序列, 可进行迭代
        count=18
        for i in $(seq 0 $count); do
            echo $i
        done


函数 打包代码(PPP23 PPP24)

函数定义格式:

[function] 函数名 () {
    函数代码块
}

函数调用格式:

函数名 [参数]...

函数就是命令,与命令用法一样

exit和return后面接的必须是0-255的数字

小于0会报错, 且最终的值是一个1-255的随机数字, 大多数是2
大于255, 最终的值是一个1-255的随机数字, 大多数是1
返回的不是数字, 会报错, 且返回的状态码是1-255之间的一个随机数字
函数中出现了return, 执行到这一行的时候就会结束,这个函数下面的代码不会执行了
在脚本中, 碰到exit会直接退出脚本, 下面的代码不会再执行

注意点:
    1. shell是面向过程的语言, 从上到下依次执行(类似C语言)
    2. 定义的变量要在使用之前,不然值是空的
    3. 定义函数要在使用之前, 不然会报错
    4. 函数外和函数内通信可以以传参的形式, 改变共同使用的变量
    5. 函数内和函数外通信可以使用的标准输出的形式(echo/printf)和return/exit
    6. 函数名要和变量名的起名规则是一样的, 做到见名知意
    7. 函数不调用不执行
    8. 假如函数名和linux的命令起冲突的时候, 会先执行自定义的函数


数组

与变量一样是存储数据
批量操作同类型的数据
SHELL只支持一维数组
数组不能直接作为函数参数进行传递

定义

    1. 索引数组
        declare -a 数组名
        数组名=(值1 值2 值3...)
    2. 键值对数组
        declare -A 数组名
        数组名=(["name1"]=soul ["name2"]=shell)
        数组名[键]=值

    3. 索引键值对混合数组(强烈不建议使用)
        只是shell的语法允许这样做,但是正常使用的时候不要这样用
        数组名[键]=值
        数组名=(值1 值2 值3...)

访问(演练)
    访问指定元素
        echo ${数组名[下标]}
            下标是正整数, 从0开始描述数组的第几个元素
            下标是负整数, 倒数第几个, 从1开始
            下标是-0就相当于是0
            下标是浮点数会报错
        echo ${数组名[键名]}

    访问所有元素
        echo ${数组名[*]}
        echo ${数组名[@]}

    访问数组长度
        ${#数组名[*]}
        ${#数组名[@]}

    访问数组所有键名
        ${!数组名[*]}
        ${!数组名[@]}
        索引数组的键名类似于{0..n}

更新
    更新数组的指定元素
        数组名[下标]=新值

        数组名+=(追加值1 追加值2...)
        给数组追加一个变量
        a=(1 2 3 4)
        num=5
        a[${#a[*]}]=$num

删除(PPP12)
    删除数组指定元素
        unset 数组名[下标]

        只是清空对应下标元素的值, 并不会改变数组其它元素的位置
        a=(1 2 3 4 5)
        len1=${#a[*]}   <-- 5
        unset a[3]      <-- 删除下标为3的元素
        len2=${#a[*]}   <-- 4
        for i in "${a[*]}"; do
            echo "$i"               <-- 这种算法不能把a[3]的实际值打出来
                                        这个时候打印的第4个数字只是第4个数字
                                        而不是a[3]的值
        done

        x=0
        for i in "${a[*]}"; do
            echo "下标为$x""的值是: ${a[$x]}"   <-- 这个时候打印的第4个数字才是a[3]的值
            let x+=1
        done



    删除整个数组
        unset 数组名


declare

定义只读变量(常量)
    declare -r 只读变量名=值

定义索引数组
    declare -a 数组名

定义键值对数组

    declare -A 数组名

定义自动转化大写的变量
    declare -u 变量名
        给此变量名赋值,会自动转成大写

定义自动转化小写的变量
    declare -l 变量名
        给此变量名赋值,会自动转成小写

定义环境变量
    declare -x 环境变量名


变量作用域

$* 和 [email protected]的区别: 它们的区别在于使用双引号, $*代表一个元素, [email protected]代表多个元素

函数直接可以访问和修改函数外的变量
shell里的变量都是全局变量, 在函数里定义的变量在函数调用后也可以直接访问

在函数内可以使用local关键词定义局部变量 (只能在函数中使用)


查找命令

which 查找$PATH变量指定的目录里的命令

    which ifconfig              查出ifconfig命令的路径

whereis 查找命令,显示命令更多的信息

    whereis ifconfig            命令路径,帮助文档等

locate 文件查找命令

    速度快,通过系统自带的一个数据库去查找

        /var/lib/mlocate/mlocate.db

    locate hello                查找带hello的文件,如果hello是刚刚新建的就找不到,因为数据库还没有没保存现在的信息

    updatedb                    手动更新查找数据库,然后再查找就会很快找到刚刚新建的hello文件


find

文件查找命令,功能最强大,速度慢,因为会扫描整个查找的范围



    find 范围 参数 关键字

    find /etc -name grub.conf   查找/etc目录下的grub.conf文件

    find / -name "*.conf"       查找/下所有.conf文件

    find / -iname grub.conf    查找/目录下的grub.conf文件,忽略大小写

    find / -maxdepth 2 -name grub.conf     可以使用-maxdepath参数来控制查找的层次,就是说只查当前目录和子目录,最多查2级目录
    find / -mindepth 2 -name grub.conf     最少查二级目录

    find /etc -type d           查找/etc/下所有的目录

    find /etc -type f           查找/etc/下的所有普通文件

    find /etc -type l -name "*.conf"      查找/etc/下软链接文件是.conf结尾的文件

    find /etc -type s           查找/etc/下所有socket文件

    find /etc -type c           查找/etc/下的所有字符设备文件

    find /etc -type p           查找/etc/下所有管道文件

    find /etc -user root        查找/etc/所属用户是root的文件

    find /etc -group root       查找/etc/所属用户组是root的文件

    find /etc -uid 500          查找/etc/下uid是500的文件,和-user类似

    find /etc -gid 500          查找/etc/下gid是500的文件,和-group类似

    find /etc -nouser           查找没有所属用户的文件

    find /etc -nogroup          查找没有所属用户组的文件

    find /etc -perm 777 -type d    查找/etc/下权限为777的目录

    find . -perm  111           查找权限是111的文件

    find . -size +10M           查找当前目录下大于10M的文件,单位可以有K,M,G,b等

    find / -size -2M            查找根目录下少于2M的文件
    find / -size +10M -size -100M   查找跟目录下面大于10M且小于100M的文件

    find / -mtime 1             查找根目录下1小时以前修改的所有文件

    find / -mtime +2            查找根目录下2个多小时以前修改的所有文件

    find / -mtime -3            查找根目录下最近3小时内修改的所有文件

    find / -atime 1             查找根目录下1天以前访问或读过的所有文件

    find / -atime -1            查找根目录下最近1天内读过或访问的文件

    find / -ctime -3            查找根目录下最近3天内状态发生改变的文件

    find / -cmin -3             查找根目录下最近3分钟内状态发生改变的文件

    find / -empty               查找根目录下所有空白文件或者空目录

    find / -false               查找根目录下总是错误的文件

    find / -false -exec ls -l {} \;   查找根目录下总是错误的文件并且用ls -l查看

    exec
        把前面命令的输出作为后面命令的参数
        对find出来的文件进行进一步的操作
        用ls -hl格式化输出/soul下面属于soul的大于10M的普通文件
        find / -size +10M -user soul -type f 2> /dev/null -exec ls -hl "{}" \;

    PPP13


文本处理工具

wc 统计命令

wc -c /etc/passwd                   统计/etc/passwd文件里有多少个字符

wc -w /etc/passwd                   统计/etc/passwd文件里有多少个单词

wc -l /etc/passwd                   统计/etc/passwd文件里有多少行

sort 排序命令

sort -f /etc/passwd                 忽略大小写排序

sort -b /etc/passwd                 忽略最前面的空格符部分

sort -n /etc/passwd                 按数字大小排序

sort -u /etc/passwd                 去除重复行显示

sort -r /etc/passwd                 反向排序

sort -n -k5 /etc/passwd             指定第五列进行按数字大小排序

sort -t: -n -k5 /etc/passwd         以:为分隔符指定第五列按数字大小排序

cut 字段截取

cut -d: -f1 /etc/passwd             以:为分隔符, 只显示第一列数据

cut -d: -f1,2,5 /etc/passwd         只显示第一列,第二列,第五列的数据

cut -d: -f1,2 /etc/passwd           以:为分隔符只显示第一列,第二列的数据

uniq 去除文件中相邻的重复行

uniq -u /etc/passwd                 只显示没有被重复过的行
                                        重复行不显示

uniq -d /etc/passwd                 只显示被重复过的行
                                        重复的行中间只显示一行

uniq -i /etc/passwd                 忽略大小写去除文件中相邻的重复行
                                        重复的行中间只显示一行

uniq -c /etc/passwd                 统计相邻重复行数

diff 比较文件差异

diff -B /etc/passwd passwd          忽略空行造成的不同

tr 替换字符

cat /etc/passwd | tr a b            查看/etc/passwd文件并把里面的a替换成b

tr a-z A-Z < /etc/passwd            把/etc/passwd文件里的小写全转成大写
不会修改源文件,输出到标准输出

grep 文本查找(查找到结果打印对应行)

格式: grep 参数 关键词 [文件路径]...

    当有参数r的时候, 没有写文件路径的话就是在当前文件
参数
    -r      递归查找文件
    -n      输出行号
    -i      忽略大小写
    -l      只输出路径
    -w      完全匹配
    -q      查找的结果不输出
    -A      输出结果后面的行(包括查找结果行)
    -B      输出结果前面的行(包括查找结果行)
    -v      输出没有关键词的行

在passwd文件中查找root输出对应行内容
    grep root /etc/passwd

在passwd文件中查找root输出对应行内容并且带行号
    grep -n root /etc/passwd

在passwd文件中查找nfs输出结果后3行(包含查找结果行)
    grep -A 3 nfs /etc/passwd

在passwd文件中查找nfs输出结果前3行
    grep -B 3 nfs /etc/passwd

在passwd文件中查找nfs输出结果前3行后2行
    grep -A 2 -B 3 nfs /etc/passwd

在多个文件中查找root关键词并且带颜色输出结果(文件名:行号:行内容)
    grep -rn root ./

在一个目录下所有文件查找root关键词
    grep -r root ./

在一个目录下的所有文件查找root关键词,只输出有结果的文件路径
    grep -rl root ./

查找没有root关键词的行
    grep -v root /etc/passwd

查找多个关键词(逻辑或的关系)
    grep --color=auto -e 'bin' -e 'nologin' /etc/passwd

查找单词(关键词)
    grep -w root /etc/passwd

忽略大小写查找
    grep -i root /etc/passwd

关闭输出查找(用于shell判断使用)
    grep -q root /etc/passwd

使用扩展正则表达式(egrep)
    grep -E 'a+' /etc/passwd
        或
    egrep 'a+' /etc/passwd

basename $(pwd) 取得当前目录名

pwd | awk -F '/' '{print $NF}'

dirname $(pwd) 取得当前路径,不包含当前目录名

pwd | sed -nr 's/(.*)(\/)(.*)$/\1\2/p'

tac 倒转文件里的内容

rev 倒转文件里的单词顺序


正则表达式 (字符串匹配 模糊匹配)

名字叫正规表示法

.           代表任意一个字符
    r...    代表r开头的任意四个字符

\<root      代表root开头的单词
root\>      代表root结尾的单词
\<root\>    代表完全匹配root单词
\b          <-- 单词边界
\B          <-- 非单词边界

^           代表行开头
    ^root   代表以root开头的行
$           代表行结尾
    root$   代表以root结尾的行

*           匹配前一个字符0次或n次
    a*      匹配0个a到多个a
    .*      匹配0个到n个任意字符
+           匹配前一个字符1次或n次(至少要有一个)
    a+      匹配至少一个a
?           匹配前一个字符0次或1次
    a?      匹配一个a或没有a

        默认情况下,数量表示符只作用于前面的【一个】字符,如果需要作用于
            前面的多个字符,可以使用(...)把前面的字符括起来
        ab+         匹配ab, abb, abbb, abbbb...
        (ab)+       匹配ab, abab, ababab, abababab...
{}          匹配前一个字符指定次数
    a{1}    匹配一个a
    a{0,1}  与a?功能相同
    a{0,}   与a*功能相同
    a{1,}   与a+功能相同
    a{1,3}  匹配一个a 或 二个a 或三个a
        这些数字可以是任意的正整数

[]   字符类       匹配一个字符
    [abc]           匹配这个字符必须是a或b或c
    [^abc]          匹配这个字符不是abc的其它任意字符
    [a-z]           匹配26个小写字母
    [0-9]           匹配0到9的字符
    [a-zA-Z0-9_]    匹配数字 大小写字母和下划线
    [a-zA-Z]+       匹配单词
    [^a-zA-Z]+      匹配单词分隔符

    预定义字符类
        [[:alnum:]]     字母和数字的组合,相当于[a-zA-Z0-9]
        [[:digit:]]     十进制数字 [0-9]
        [[:alpha:]]     字母组合 [a-zA-Z]
        [[:blank:]]     空格和制表符
        [[:punct:]]     特殊字符集合
        ...

\           转义
    \.      取消.的特殊意义
    \n      代表换行

    \d      任意一个十进制数字 [0-9]
    \D      任意一个不是数字的字符 [^0-9]
    \s      任意一个空白字符 [ \f\n\r\t]
    \w      任意一个单词字符 [a-zA-Z0-9_]
    \W      [^a-zA-Z0-9_]

|           代表或
    a|b     匹配a或b

()          子表达式
    (root)|(kyo)    匹配root或kyo

默认情况下,数量表示符是最大匹配,好的正则表达式引擎支持用问号 ? 来启用最小匹配
.*b   匹配 aaabababa      <-- 最大匹配
           ^^^^^^^^
.*?b  匹配 aaabababa      <-- 最小匹配
           ^^^^

引用表示法
--------------------------------------------------------------
从左边开始数左小括号(openning brace),数字从1开始,被第一对括号匹配的字符
    可以用\1 来引用,第二对可以用\2 来引用,以此类推。
echo abcabcabcaabb | grep -E '(a(bc)){2}\1' --color
    abcabcabcaabb
echo abcabcabcaabb | grep -E '(a(bc)){2}a\2' --color
    abcabcabcaabb
echo "hello world, hello world, hello beautiful world"      \
    | grep -E --color '((hello) (world)), \1, \2 .* \3'

    hello world, hello world, hello beautiful world

PPP14


sed 流编辑器

格式: sed 参数 "表达式" [操作文件]...

基本格式包含以下部分:
1. 指定范围,不明确指定的话,默认是所有的行,可以是行号,或者正则表达式
2. 指定动作,常用的有d, s, p, i, a
    d: delete, 删除
    s: substitute, 替换
    p: print, 打印
    i: insert, 在前面插入
    a: append, 在后面添加

选项:
    -e          表达式
    -n          取消非匹配行的文件输出
    -i          操作原文件(慎用)
    -r          支持更多正则表达式

    ()      域, 匹配子表达式,用于替换引用
        第一个小括号对应\1来引用
        第二个小括号对应\2来引用
        ...
        以此类推

        有-r参数, 小括号不需要转义, 没有-r参数需要转义 \(...\)

输出    p(PPP15)

    输出第3行内容
        sed -n '3p' /etc/passwd

    输出第3行和第7行内容,中间为;表示单独的操作
        sed -n '3p;7p' /etc/passwd

    输出第3行到第5行和第7行内容,中间为逗号,表示范围
        sed -n '3,5p;7p' /etc/passwd

    输出有root关键词的行
        sed -n '/root/p' /etc/passwd

    输出以root开头的行
        sed -n '/^root/p' /etc/passwd

    输出有数字的行
        sed -n '/[0-9]/p' /etc/passwd

    打印非空格开头的行
        sed -n '/^[^[:blank:]]/p' passwd
            或
        sed -n '/^[^\ ]/p' passwd

增加 i a (PPP16)
    在第2行的上面, 也就是第2行加上一行内容
        sed -i '2i sfasdfdsf' passwd

    在第2行的下面, 也就是在第3行加一行内容
        sed -i '2a sfasdfdsf' passwd

    i和a的区别:
        i是指定几行就在几行加,
        a是指定几行就在几行的下一行加

删除    d

    删除第3行
        sed '3d' mypasswd

    删除第10行到第30行
        cat -n /etc/passwd | sed '10, 30d'

    删除第3行, 影响原文件
        sed -i '3d' mypasswd

    删除第二行到第三行
        cat -n passwd | sed -e '2,3d'

    删除第一行和第五行
        cat -n passwd | sed -e '1d;5d'

    删除空行
        cat passwd | sed -e '/^$/d'

    删除空格开始的行
        cat /etc/passwd |sed -e '/^[[:blank:]]/d' --删除以空格开头的
        cat /etc/passwd |sed -e '/^ /d'    --同上
        cat /etc/passwd |sed -e '/^\ /d'    --同上

    删除/etc/passwd的空行和注释
        cat passwd |sed -e '/^#/d;/^$/d'
    ...

    删除一个字符串中指定位置的字符(PPP17)
    删除第3至第5的字符
    echo 123456789ABCDEF | sed -r 's/^(..)...(.*)$/\1\2/'
    删除第3个字符
    echo 123456789ABCDEF | sed -r 's/^(.{2}).(.*)$/\1\2/'
    删除从3个字符开始的10个字符
    echo 123456789ABCDEF | sed -r 's/^(.{2}).{10}(.*)$/\1\2/'

替换 s(PPP18)(PPP19)

    将每行的第一个root替换成soul
        sed -e 's/root/soul/' mypasswd

    将每行的第二个root替换成soul
        sed -e 's/root/soul/2' mypasswd

    将每行的所有root替换成soul
        sed -e 's/root/soul/g' mypasswd

    将每行的开头的空格删掉
        cat -n /etc/passwd | sed -e 's/^[ ]*//g'

    将1到10行的所有root替换成soul
        sed -e '1,10s/root/soul/g' mypasswd

    将10到最后行的所有root替换成soul
        sed -e '10,$s/root/soul/g' mypasswd

    将10到最后行的所有小写字母替换K
        sed -e '10,$s/[a-z]/K/g' mypasswd

    删除每行中第一个字符
        sed -e "s/^.//" mypasswd

    将每行中第一个字符替换成A
        sed -e "s/^./A/" mypasswd

    删除每行中第二个字符
        sed -r -e "s/^(.)./\1/" mypasswd

    删除每行中第一个有效字符(非空白字符)
        sed -e "s/^([[:blank:]]*)./\1/" mypasswd
            或者
        sed -r -e 's/^([ ]*)./\1/' mypasswd

    删除每行最后一个字符
        sed -e 's/.$//' mypasswd


    从左边开始数左小括号(openning brace),数字从1开始,被第一对括号匹配的字符
        可以用\1 来引用,第二对可以用\2 来引用,以此类推。


    删除每行倒数第二个字符
        sed -r -e 's/.(.)$/\1/' mypasswd
            把倒数第一个和倒数二个换成了倒数第一个

    删除每行的第二个数字
        sed -r -e 's/[0-9]+//2' passwd

    练习:
        2018-9-24
        2018-12-1
        2018-1-4
        2018-10-1

        把上面的如期格式替换成2018-10-01的形式



    删除每行第二个单词([a-zA-Z]+)
        sed -re 's/^([a-Z]+)([^a-Z])([a-Z]+)/\1\2/' 1_passwd.txt
            或者
        sed -re 's/[a-Z]+//2' 1_passwd.txt

    删除每行第3个非单词
        sed -re 's/[^a-Z]+//3' 1_passwd.txt

    PPP20
    PPP21



查找有线网卡设备名
    ifconfig | sed -rn 's/^(e[0-9a-zA-Z]+).*$/\1/p'
截取IP地址(一行命令)
    输出结果格式: 192.168.0.183/255.255.255.0
    #!/bin/bash

    # 再脚本运行的进程里,把语言环境改成英文,就不会受到语言环境的影响
    export LANG=en_US.UTF-8
    export LANGUAGE=en_US:en

    # 截取出正在上网的网卡名称
    link=$(route -n  | sed -n '/UG/p' | sed -re 's/(.*)(\b[a-Z0-9]+$)/\2/')
            或者
    link=$(route -n | sed -n '/UG/p' | awk '{print $NF}')
    # 截取出ip和子网掩码, 用/分开
    ip=$(ifconfig $link | sed -n '/Mask/p' | sed -r 's/^([^0-9]+)([0-9.]+)(.*)([^0-9.]+)([0-9.]+$)/\2\/\5/')
    echo $ip


Awk(PPP22)

Awk是一门编程语言,有很多版本,我们用的是GNU的gawk

    以:为分隔符打印第三列

        awk -F ":" '{print $3}' /etc/passwd



    以:为分隔符统计/sbin/nologin有多少个

        awk 'BEGIN{FS=":" ;count=0}{if($7 = "/sbin/nologin")count+=1}END{print count}' /etc/passwd

            BEGIN      在读记录之前执行
            END        在读完记录后再执行
            $0         代表所有记录
            $1-n       代表一个记录的第n个字段

    使用awk脚本

        #!/bin/awk

        BEGIN{
            FS=:
            count=0         定义变量
        }

        {
            if($7 == "/sbin/nologin")
            {
                count += 1
                print $0
            }
        }

        END{
            print "count="count
        }

        awk -f 1.awk /etc/passwd        运行上面的脚本

        FNR         当前处理到第几个文件
        NF          当前多少个字段
        NR          多少行
        OFS         输入指定字段分隔符
        ORS         输出记录分隔符
        FS          指定分隔符



    tips:
        awk没有指定分隔符的话, 就是默认以"空格"为分隔符

    打印所有行, 以空格为分隔符

        awk '{ print $0 }' /etc/passwd

    打印第一列,以:为分隔符

        awk -F ":" '{print $1}' /etc/passwd

    打印每行的最后一列

        awk -F : '{print $NF}' /etc/passwd

    打印第一列和第三列, 并且加上welcome

        awk -F ":" '{print $1 "\t" $3 "welcome"}' /etc/passwd

    打印第八行

        awk -F : '{if (NR == 8){print $0}}' /etc/passwd
        awk -F : 'NR == 8 {print $0}' /etc/passwd

    打印行总数

        awk -F : 'END{print NR}' /etc/passwd

    打印每行的字段数

        awk -F : '{print NF}' /etc/passwd

    打印最后一行的最后一列

        awk -F : 'END{print $NF}' /etc/passwd

    打印字段数大于4的行

        awk -F : 'NF > 4 {print $0}' /etc/passwd

    打印文件里所有字段总数

        awk -F : 'BEGIN{c = 0}{c=c+NF}END{print c}' /etc/passwd

    打印uid在30~40范围内的用户名

        awk -F : '$3 >= 30 && $3 <= 40{print $1}' /etc/passwd

    打印5到56行

        awk -F : 'NR >= 5 && NR <= 56 {print $0}' /etc/passwd

    打印偶数行

        awk '{if (NR % 2 == 0)print $0}' /etc/passwd

    打印偶基数行

        awk '{if (NR % 2 == 1)print $0}' /etc/passwd

    打印每行的第一单词

        awk 'BEGIN{FS="[^A-Za-z]+"}{print $1}' /etc/passwd

    打印每行的第一个和第三个单词

        awk 'BEGIN{FS="[^a-zA-Z]+"}{print $1,$3}' /etc/passwd

    打印字段数大于5个的行总数

        awk -F ":" 'BEGIN {count=0 }(NF > 5){count+=1} END{print count}' /etc/passwd


Last modification:November 18th, 2019 at 02:08 pm
如果觉得我的文章对你有用,请随意赞赏