zoukankan      html  css  js  c++  java
  • shell脚本中sed的使用

    Shell脚本之sed篇
    原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 、作者信息和本声明。否则将追究法律责任。http://manual.blog.51cto.com/3300438/908002


    目录:
    一、概述
    二、sed基本语法格式
    三、简单正则表达式
    四、sed脚本指令(注释、替换、删除、追加、打印、下一行、读写文件、退出、多行模式)
    五、综合案例


    一、概述
    1.sed是一款流编辑工具,用来对文本进行过滤与替换工作,特别是当你想要对几十个配置文件做统计修改时,你会感受到sed的魅力!
      sed通过输入读取文件内容,但一次仅读取一行内容进行某些指令处理后输出,所以sed更适合于处理大数据文件。

    2.sed流程:
            * 通过文件或管道读取文件内容。
            * sed并不直接修改源文件,而是将读入的内容复制到缓冲区中,我们称之为模式空间(pattern space)。
            * 根据sed的指令对模式空间中的内容进行处理并输出结果,默认输出至标准输出即屏幕上。

                        输入
            ------------------------------------------------
                          |
                          |
                          v
                          v
                读取一行内容,并复制到模式空间    <------------ sed 指令
                          |
                          |
                          v
                          v
            ------------------------------------------------
                    输出经过处理后的内容

    *****************************************************************************************************

    二、sed基本语法结构

    sed  Options...  [script]   [inputfile...]

    sed  选项...       [脚本指令]      [输入文件]

    如果没有输入文件,则sed默认对标准输入进行处理(即键盘输入)。脚本指令是第一个不以“-”开始的参数。

    1.选项含义:
        --version            显示sed版本。

        --help               显示帮助文档。

        -n,--quiet,--silent    静默输出,默认情况下,sed程序在所有的脚本指令执行完毕后,将自动打印
                               模式空间中的内容,这些选项可以屏蔽自动打印。

        -e script              允许多个脚本指令被执行。

        -f script-file, 
        --file=script-file        从文件中读取脚本指令,对编写自动脚本程序来说很棒!

        -i,--in-place        直接修改源文件,经过脚本指令处理后的内容将被输出至源文件(源文件被修改)
                             慎用!

         -l N, --line-length=N    该选项指定l指令可以输出的行长度,l指令用于输出非打印字符。

         --posix            禁用GNU sed扩展功能。

         -r, --regexp-extended    
                            在脚本指令中使用扩展正则表达式

         -s, --separate        默认情况下,sed将把命令行指定的多个文件名作为一个长的连续的输入流。
                              而GNU sed则允许把他们当作单独的文件,
                              这样如正则表达式则不进行跨文件匹配。

         -u, --unbuffered      最低限度的缓存输入与输出。

    2.简单案例:
        以上仅是sed程序本身的选项功能说明,至于具体的脚本指令(即对文件内容做的操作)后面我们会详细描述,
        这里就简单介绍几个脚本指令操作作为sed程序的例子。
        a,append        追加
        i,insert        插入
        d,delete        删除
        s,substitution  替换

        案例说明:灰色背景的内容为待处理的源文件,红色字体的文字为sed脚本,蓝色字体的文字为处理后的结果输出。

        这里test.txt为样本文件:
    [jacob@localhost ~] #cat  test.txt
    DEVICE=eth0
    ONBOOT=yes
    BOOTPROTO=static
    IPADDR=192.168.0.1
    NETMASK=255.255.255.0

    GATEWAY=192.168.0.254



    [jacob@localhost ~] #sed  '2a TYPE=Ethernet'  test.txt    第二行后添加TYPE=Ethernet

    [jacob@localhost ~] #sed  '3i  TYPE=Ethernet'  test.txt   第三行前添加TYPE=Ethernet

    [jacob@localhost ~] #sed  's/yes/no/g'     test.txt       将样本文件中的所有yes替换为no

    [jacob@localhost ~] #sed  '3,4d'  test.txt                删除第3至4行的内容

         以上大多数操作指令,都依据行号定位操作对象(地址),如:2a即第二行后添加。
    但实际情况可能大多数情况你并不确定你要操作对象(地址)的行号,这时更多的我们会使用正则表达式确定操作对象(地址)。
    下面是使用正则表达式定位操作行的示例:

    [jacob@localhost ~] #sed  '/ONBOOT/a TYPE=Ethernet'  test.txt   

                         匹配到包含ONBOOT的行,并在其后添加TYPE=Ethernet

    [jacob@localhost ~] #sed  '/^GATEWAY/d'  test.txt

                         匹配以GATEWAY开始的行,并删除改行


        另外我们的操作指令可以,写入到脚本文件中并通过sed的-f选项读取。
     创建一个sed脚本,内容如下:

    [jacob@localhost ~] #cat   sed.sh
    /^$/d                                    这条指令的作用是:匹配到空白行后,删除改行。

    [jacob@localhost ~] #sed  -f  sed.sh   test.txt    对test.txt文件执行sed.sh指令

        另外,当你需要执行多个指令时,可以使用以下三种方法:
    1. [jacob@localhost ~] #sed  's/yes/no/;s/static/dhcp/'  test.txt            注:使用分号隔开指令。

    2. [jacob@localhost ~] #sed  -e  's/yes/no/'    -e  's/static/dhcp/'  test.txt    注:使用-e选项。

    3. [jacob@localhost ~] #sed '
        >s/yes/no/
        >s/static/dhcp/'  test.txt
                                        注:利用分行指令。
     然而在命令行上输入过长的指令是愚蠢的,这时就需要-f选项指定sed脚本文件,在脚本文件中可以包含多行指令,而且便于修改!

     *****************************************************************************************

    三、简单正则表达式
        从以上案例中我们不难发现,我们编写的脚本指令需要指定一个地址来决定操作范围,如果不指定则默认对文件的所有行操作。
        如:sed  'd'   test.txt        将删除test.txt的所有行,而'2d'则仅删除第二行。

    1.为sed指令确定操作地址:
        number        指定输入文件的唯一行号。

        first~step    以first开始,并指定操作步长为step,如1~2,指定第一行,第三行,第五行... 为操作地址。
                    [jacob@localhost ~] #sed  -n  '1~2p'  test.txt        打印文件的奇数行。
                    2~5,则可以指定第二行开始,每5行匹配一次操作地址。

        $            匹配文件的最后一行。

        /regexp/     //中间包含的是正则表达式,通过正则表达式匹配操作地址。
                     注://空的正则表达式,匹配最近一次正则表达式的匹配地址,会在后面使用看出效果。

        \cregexpc     匹配扩展正则表达式,c字符可以使用任意字符替代。

        addr1,addr2    匹配从操作地址1到操作地址2的所有行。
                       [jacob@localhost ~] #sed  '2,8d'  test.txt        删除2至8中间的所有行。
        
        addr1,+N        匹配地址1以及后面的N行内容。

    2.正则表达式概述(对你要找内容的一种描述)

        char            字符本身就匹配字符本身,如/abc/就是定位包含abc的行。

        *            匹配前面表达式出现了0或若干次,如/a*/可以帮你找到a,aa,aaa,... ...等等。
        
        \+            类似于*,但匹配前面表达式的1次或多次,这属于扩展正则表达式。

        \?            类似于*,但匹配前面表达式的0次或1次,这属于扩展正则表达式。
        
        \{i\}         类似于*,但匹配前面表达式的i次(i为整数),如:a\{3\}可以帮你找到aaa。

        \{i,j\}       匹配前面表达式的i到j次,如a\{1,2\}可以帮你找到a或aa或aaa。

        \{i,\}        匹配前面表达式至少i次。

        \( \)         将\( \)内的模式存储在保留空间。最多可以存储9个独立子模式,可
                      通过转义\1至\9重复保留空间的内容至此点。

        \n            转义\1至\9重复保留空间的内容至此点。
                      例:test.txt的内容为ssttss    
                      grep '\(ss\)tt\1' test.txt            \1表示将ss重复在tt后面
                      该grep命令等同于grep  ssttss  test.txt    在test.txt文件中找ssttss

        .            (点)匹配任意字符。

        ^            匹配行的开始,如^test    将匹配所有以test开始的行。

        $            匹配行的结尾,如test$    将匹配所有以test结尾的行。

        []           匹配括号中的任意单个字符,如a[nt]    将匹配an或at。

        [^]          匹配不包含在[]中的字符,如[^a-z]    将匹配除a-z以外的字符。

        \n           匹配换行符。

        \char        转义特殊字符,如\*,就是匹配字面意义上的星号。

    **************************************************************************************

    四、sed脚本指令
    常用的sed脚本指令有:
    *注释(#)
    *替换(s)
    *删除(d)
    *追加(a)
    *插入(i)
    *更改(c)
    *列印(l)
    *转换(y)
    *打印(p)
    *读写文件(r,w)
    *退出(q)
    *下一步(n)
    *Next(N)
    *Print(P)
    *Delete(D)
    *Hold(h,H)
    *Get(g,G)
    *branch,test

    sed脚本指令的基本格式:

       [address]command
       [地址]命令            备注:有些命令仅可以对一行操作,有些可以对多行操作。

    命令可以用大括号进行组合以使命令序列可以作用于同一个地址:
        address{
            command1
            command2
            command3
    }
    第一个命令可以和左大括号在同一行,但右大括号必须单独处于一行。
        注意事项:命令后添加空格会产生错误。

    1.注释(#):
        注释行是以#开始的行,如果#后面的字符为n,则屏蔽sed程序的自动输出功能,等同于命令选项-n。

    2.替换(s,Substitution):
        [address]s/pattern/replacement/flags
        address为操作地址,s为替换指令,/pattern/匹配要替换的内容,/replacement/为替换的内容。
        flags可以是:
            n   1至512之间的数字,表示对模式空中指定模式的第n次出现进行替换。
                如一行中有3个A,而只想替换第二个A。
            g   对模式空间的匹配进行全局更改。没有g则仅第一次匹配被替换,如一行中有3个A,则仅替换第一个A。
            p   打印模式空间的内容。
            w   file
                将模式空间的内容写到文件file中。

        replacement为字符串,用来替换与正则表达式匹配的内容。
        在replacement部分,只有下列字符有特殊含义:
            &    用正则表达式匹配的内容进行替换。
            \n   匹配第n个子串,这个子串之前在pattern中用\( \)指定。
            \    转义(转义替换部分包含的:&, \等)。

    -----------------------------------------------------------------------------------------------
        示例1所使用的样本文件为:
    [jacob@localhost ~] #cat  test.txt
    <html>
    <title>First Web</title>
    <body>Hello the World! <body>
    </html>


        示例1:将样本文件中的第二个<body>替换为</body>
        编写sed脚本为:
    [jacob@localhost ~] #cat  sed.sh
    /body/{
    s//\/body/2 
                  注:替换与行匹配相同的内容即body,替换为/body,但仅替换第二个body为/body。
    }
        执行sed程序的结果:
    [jacob@localhost ~] #sed  -f  sed.sh   test.txt
    <html>
    <title>First Web</title>
    <body>Hello the World!</body>
    </html>

    ---------------------------------------------------------------------------------------------

        示例2所使用的样本文件为:
    [jacob@localhost ~] #cat  test.txt
    <html>
    <title>First Web</title>
    <body>
    h1Helloh1
    h2Helloh2
    h3Helloh3
    </body>
    </html>


        示例2:给所有第一个的h1,h2等添加<>;第二个h1,h2添加</>
        编写sed脚本为:
    [jacob@localhost ~] #cat  sed.sh
    /h[0-9]/{          注:匹配h紧跟一个数字的行
    s//\<&\>/1
             注:替换与上一行中匹配内容相同的内容,即替换h[0-9],替换为<&>,其中&即前面要替换的内容。
    s//\<\/&\>/2       注:上一条指令仅替换第一个h1,h2...,本行指令用来替换第二个h1,h2...。
    }
        执行sed程序的结果:
    [jacob@localhost ~] #sed  -f  sed.sh   test.txt
    <h1>Hello</h1>
    <h2>Hello</h2>
    <h3>Hello</h3>

    ---------------------------------------------------------------------------------------------

    技巧:关于 's///' 命令的另一个妙处是 '/' 分隔符有许多替换选项。
        如果规则表达式或替换字符串中有许多斜杠,则可以通过在 's' 之后指定一个不同的字符来更改分隔符。
        示例:$ sed -e 's:/usr/local:/usr:g' mylist.txt        这里:为分隔符。

    3.删除(d ,delete):

        删除指令删除匹配的行,而且删除命令还会改变sed脚本中命令的执行顺序。因为:
        匹配的行一旦被删除,模式空间将变为“空”,自然不会再执行sed脚本后续的命令。
        删除命令将导致读取新的输入行(下一行),而sed脚本中的命令则从头开始执行。
        注意事项:删除将删除整个行,而不只是删除匹配的内容。(如要删除匹配的内容,可以使用替换)


        示例1所使用的样本文件为:

    [jacob@localhost ~] #cat  test.txt
    DEVICE=eth0
    ONBOOT=yes
    BOOTPROTO=static

    IPADDR=192.168.0.1
    NETMASK=255.255.255.0

    GATEWAY=192.168.0.254


        示例1:删除文件中的空白行
        编写sed脚本为:
    [jacob@localhost ~] #cat  sed.sh
    /.*/{
    /^$/d

    }
        执行sed程序的结果:
    [jacob@localhost ~] #sed  -f  sed.sh   test.txt
    DEVICE=eth0
    ONBOOT=yes
    BOOTPROTO=static
    IPADDR=192.168.0.1
    NETMASK=255.255.255.0
    GATEWAY=192.168.0.254


    4.追加(a):在匹配行后追加内容

        示例所使用的样本文件为:
    [jacob@localhost ~] #cat  test.txt
    DEVICE=eth0
    ONBOOT=yes
    BOOTPROTO=static
    NETMASK=255.255.255.0
    GATEWAY=192.168.0.254


        示例程序:sed  '/static/a IPADDR=192.168.0.1'  test.txt        注:在static行后添加一行。
        

    5.插入(i):在匹配行前插入内容
        
         示例程序:sed  '/NETMASK/i IPADDR=192.168.0.1'  test.txt        注:在NETMASK行前添加一行。


    6.更改(c):更改匹配行的内容

        示例程序:sed  '/ONBOOT/c ONBOOT=yes'  test.txt     注:包含ONBOOT的行(整行)替换为ONBOOT=yes。


    7.列印(l):显示模式空间中的内容,显示非打印字符,一般与-n一起使用,否则会输出两次。

        示例程序:sed  -n '1,2l'  test.txt            注:如果使用sed脚本文件,需要#n屏蔽自动输出
        结果如下:
        DEVICE=eth0$
        ONBOOT=yes$


    8.转换(y):按字符的转换(Transform)        [address]y/source-chars/dest-chars/

        示例所使用的样本文件为:
    [jacob@localhost ~] #cat  test.txt
    DEVICE=eth0
    ONBOOT=yes
    BOOTPROTO=static
    netmask=255.255.255.0
    GATEWAY=192.168.0.254


        编写sed脚本为:
    [jacob@localhost ~] #cat  sed.sh
    /.*/{
    /netmask/y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/
            将小写转换为大写
    }
        执行sed程序的结果:
    [jacob@localhost ~] #sed  -f  sed.sh   test.txt
    DEVICE=eth0
    ONBOOT=yes
    BOOTPROTO=static
    NETMASK=255.255.255.0
    GATEWAY=192.168.0.254


    9.打印(p):作用类似与l(列印),但不显示非显示字符,一般与-n配合使用,脚本文件中需要#n

        sed  -n '1,2p'  test.txt        仅显示第一、第二行的内容。

    10.读写文件(r,w):[line-address]r file, [address]w  file

        示例所使用的样本文件为:
    [jacob@localhost ~] #cat  name.txt
    Jacob
    Tom
    Jerry


    [jacob@localhost ~] #cat  mail.txt
    jacob@gmail.com
    tom@gmail.com
    jerry@gmail.com


        编写sed脚本为:
    [jacob@localhost ~] #cat  sed.sh
    /.*/{
    $r mail.txt

    }
        执行sed程序的结果:
    [jacob@localhost ~] #sed  -f  sed.sh   name.txt
    Jacob
    Tom
    Jerry
    jacob@gmail.com
    tom@gmail.com
    jerry@gmail.com


    11.退出(q):        匹配地址后退出sed脚本

        sed  '10q'  test.txt            打印文件前10行内容(到第10行后退出sed脚本指令)。



    12.下一步(n):    [address]n        输出模式空间中的内容,然后读取输入的下一行。
                next命令改变了正常的sed脚本指令的流程,默认只有到sed脚本的底部才会输出模式空间的内容。
                next命令导致输入的下一行取代模式空间中的当前行,sed脚本中的后续命令将应用于替换后的行,而不是当前行。
        
        示例所使用的样本文件为:
    [jacob@localhost ~] #cat  name.txt
    Jacob
    Tom
    Jerry


        编写sed脚本为:
    [jacob@localhost ~] #cat  sed.sh
    /.*/{
    n                            读取下一行后删除(删除偶数行)。
    /.*/d

    }

        执行sed程序的结果:
    [jacob@localhost ~] #sed  -f  sed.sh   name.txt
    Jacob
    Jerry


    13.Next(N):

        多行Next(N)命令通过读取新的输入行,并将它添加到模式空间的现有内容之后,来创建多行模式空间。
        模式空间的最初内容与新的输入行之间用换行符分隔。在模式空间中插入的换行符可以使用\n匹配。
        示例1:
    [jacob@localhost ~] #cat  test.txt
    111
    222
    222
    333


    [jacob@localhost ~] #cat sed.sh
    #n
    /222/{
    N
    l

    }

    [jacob@localhost ~] #sed  -f  sed.sh   test.txt    结果如下:
    222\n222    
        示例2:
    [jacob@localhost ~] #cat  test.txt
    111
    222
    222
    222
    333

    [jacob@localhost ~] #cat sed.sh
    #n
    /222/{
    N
    l

    }

    [jacob@localhost ~] #sed  -f  sed.sh   test.txt
    222\n222
    222\n333


    14.Print(P):多行打印(P)与打印(p)稍有不同,该命令仅输出多行模式空间中的第一部分,直到第一个插入的\n换行符为止。

        示例:

    #cat test.txt         #cat test.txt          #cat test.txt      #cat test.txt
    111                    111                    111                111
    222                    222                    222                222
    333                    333                    333                333
    444                    444                    444                444
    555                    555                    555                555
    666                    666                    666                666

    -----------------------------------------------------------------------------------------------------

    #cat sed.sh         #cat sed.sh         #cat sed.sh       #cat sed.sh

    /.*/{               /.*/{               /.*/{              /.*/{
    N                    N                    N                N
    }                    l                    P                p
                        }                    }                }

    ------------------------------------------------------------------------------------------------------

    结果:                结果:                结果:            结果:
    111                   111\n222$             111               111
    222                   111                   111               222
    333                   222                   222               111
    444                   333\n444$             333               222
    555                   333                   333               333
    666                   444                   444               444
                          555\n666$             555               333
                          555                   555               444
                          666                   666               555
                                                                  666
                                                                  555
                                                                  666

    -------------------------------------------------------------------------------------------------------


       流程说明:1.sed脚本文件使用了N,但后续没有任何指令,所以仅按sed自动输出,即输出源文件所有内容。
                2.sed脚本问使用了N,l表示显示模式空间的内容,即111\n222(N的作用为把下一行追加至行尾),
                   同时sed脚本的自动输出功能会把源文件内容显示出来即111,222。
                   依次类推,sed脚本继续读取第三行333,并使用N将444追加至行尾,使用l显示模式空间的内容,
                   sed自动输出再把源文件内容显示出来。
                3.sed脚本文件使用了N将下一行追加至行尾,现在模式空间中的内容为111\n222,而P指令的作用是
                   打印模式空间中的第一部分内容直到\n结尾,即仅打印111,这时sed的自动输出功能把源文件
                   内容打印出来111,222。依次类推读取第三行333,N将444追加至行尾,P打印\n前的内容,
                   同时sed脚本自动将源问内容输出。
                4.原理类似与第三个sed脚本,但p打印时\n看作是回车换行,所以打印出来的是111回车222。

    ----------------------------------------------------------------------------------------------------- 

    15.Delete(D):
        删除命令(d)删除模式空间中的内容并导致读入新的输入行,而sed脚本重新执行。
        删除命令(D)删除模式空间中直到第一个插入的换行符(\n)前的这部分内容,它不会读入新的输入行,并返回sed脚本的顶端,
        使得剩余指令继续应用于模式空间中剩余的内容。

    16.Hold(h,H),Eet(g,G):

        模式空间是存放当前输入行的缓冲区。还有一个称为保持空间(hold space)的缓冲区。
        模式空间的内容可以复制到保持空间,保持空间的内容同样可以复制到模式空间。
        有一组命令用于在两者之间移动数据。
    Hold(h|H)        将模式空间的内容复制或追加到保持空间
    Get(g|G)        将保持空间的内容复制或追加到模式空间
    Exchange(x)    交换保持空间与模式空间的内容

        示例:反转部分行
    [jacob@localhost ~] #cat  test.txt
    1
    2
    11
    22
    111
    222

    [jacob@localhost ~] #cat  sed.sh
    /1/{
    h
    d
    }
    /2/{
    G
    }


    结果:
    2
    1
    22
    11
    222
    111


    17.branch,test

    分支(b),测试(t)命令将sed脚本中的控制转移到包含特殊标签的行,如果没有指定的标签,则将控制转移到脚本结尾处。
    分支命令是无条件转移,测试命令用于有条件转移,测试只有当替换命令改变当前行成功时才会执行。

    标签是任意不多于7个字符的序列,标签本身占据一行并以冒号开始:
    :mylable
    冒号与标签之间不允许有空格

    当分支或测试命令中指定标签时,在命令与标签之间允许有空格:
    b mylabel            注:不要在标签后插入空格

    五、综合案例(案例摘自GNU sed官网)
    综合案例1:重命名文件名为小写
    [jacob@localhost ~] #cat  sed.sh
    #! /bin/sh
         # rename files to lower/upper case...
         #
         # usage:
         #    move-to-lower *
         #    move-to-upper *
         # or
         #    move-to-lower -R .
         #    move-to-upper -R .
         #
         help()
         {
                 cat << eof
         Usage: $0 [-n] [-r] [-h] files...
         
         -n      do nothing, only see what would be done
         -R      recursive (use find)
         -h      this message
         files   files to remap to lower case
         
         Examples:
                $0 -n *        (see if everything is ok, then...)
                $0 *
         
                $0 -R .
         
         eof
         }
         
         apply_cmd='sh'
         finder='echo "$@" | tr " " "\n"'
         files_only=
         
         while :
         do
             case "$1" in
                 -n) apply_cmd='cat' ;;
                 -R) finder='find "$@" -type f';;
                 -h) help ; exit 1 ;;
                 *) break ;;
             esac
             shift
         done
         
         if [ -z "$1" ]; then
                 echo Usage: $0 [-h] [-n] [-r] files...
                 exit 1
         fi
         
         LOWER='abcdefghijklmnopqrstuvwxyz'
         UPPER='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
         
         case `basename $0` in
                 *upper*) TO=$UPPER; FROM=$LOWER ;;
                 *)       FROM=$UPPER; TO=$LOWER ;;
         esac
         
         eval $finder | sed -n '
         
         # remove all trailing slashes
         s/\/*$//
         
         # add ./ if there is no path, only a filename
         /\//! s/^/.\//
         
         # save path+filename
         h
         
         # remove path
         s/.*\///
         
         # do conversion only on filename
         y/'$FROM'/'$TO'/
         
         # now line contains original path+file, while
         # hold space contains the new filename
         x
         
         # add converted file name to line, which now contains
         # path/file-name\nconverted-file-name
         G
         
         # check if converted file name is equal to original file name,
         # if it is, do not print nothing
         /^.*\/\(.*\)\n\1/b
         
         # now, transform path/fromfile\n, into
         # mv path/fromfile path/tofile and print it
         s/^\(.*\/\)\(.*\)\n\(.*\)$/mv "\1\2" "\1\3"/p
         
         ' | $apply_cmd




    综合案例2:获取bash环境变量
         #!/bin/sh     
         set | sed -n '
         :x     
         # if no occurrence of ‘=()’ print and load next line
         /=()/! { p; b; }
         / () $/! { p; b; }     
         # possible start of functions section
         # save the line in case this is a var like FOO="() "
         h
         
         # if the next line has a brace, we quit because
         # nothing comes after functions
         n
         /^{/ q
         
         # print the old line
         x; p
         
         # work on the new line now
         x; bx
         '
     

    本文出自 “丁丁历险” 博客,请务必保留此出处http://manual.blog.51cto.com/3300438/908002

  • 相关阅读:
    jQuery --checkbox全选和取消全选简洁高效的解决办法
    扩展jquery插件的方法
    git
    varchar,char,varchar2,mybatis查询无返回
    Docker拉取mysql并连接
    Map
    mysql数据库用ip访问
    docker的安装和docket拉取Oracle
    springboot
    linux 安装mysql
  • 原文地址:https://www.cnblogs.com/yunsicai/p/2871108.html
Copyright © 2011-2022 走看看