Unix / Linux - 使用 SED 的正则表达式

  • 简述

    本章将详细讨论 Unix 中使用 SED 的正则表达式。
    正则表达式是一个字符串,可用于描述多个字符序列。正则表达式被几个不同的 Unix 命令使用,包括ed, sed, awk, grep,并且在更有限的程度上, vi.
    这里 SED 代表 sed它。这个面向流的编辑器专为执行脚本而创建。因此,您输入的所有输入都会通过并进入 STDOUT,并且不会更改输入文件。
  • 调用 sed

    在我们开始之前,让我们确保我们有一个本地副本 /etc/passwd 要使用的文本文件 sed.
    如前所述,可以通过如下方式通过管道向其发送数据来调用 sed -
    
    $ cat /etc/passwd | sed
    Usage: sed [OPTION]... {script-other-script} [input-file]...
      -n, --quiet, --silent
                     suppress automatic printing of pattern space
      -e script, --expression = script
    ...............................
    
    cat 命令转储的内容 /etc/passwdsed通过管道进入 sed 的模式空间。模式空间是 sed 用于其操作的内部工作缓冲区。
  • sed 通用语法

    以下是 sed 的一般语法 -
    
    /pattern/action
    
    这里, pattern 是一个正则表达式,并且 action是下表中给出的命令之一。如果pattern 省略, action 正如我们在上面看到的那样,对每一行执行。
    模式周围的斜线字符 (/) 是必需的,因为它们用作分隔符。
    序号 范围和描述
    1
    p
    打印行
    2
    d
    删除行
    3
    s/pattern1/pattern2/
    用模式 2 替换第一次出现的模式 1
  • 用sed删除所有行

    我们现在将了解如何使用 sed 删除所有行。再次调用sed;但 sed 现在应该使用editing command delete line,由单个字母表示 d
    
    $ cat /etc/passwd | sed 'd'
    $
    
    可以指示 sed 从文件中读取数据,而不是通过管道向其发送文件来调用 sed,如下例所示。
    以下命令与前面的示例完全相同,没有 cat 命令 -
    
    $ sed -e 'd' /etc/passwd
    $
    
  • sed 地址

    sed 也支持地址。地址要么是文件中的特定位置,要么是应应用特定编辑命令的范围。当 sed 没有遇到地址时,它会在文件的每一行上执行操作。
    以下命令为您一直使用的 sed 命令添加了一个基本地址 -
    
    $ cat /etc/passwd | sed '1d' |more
    daemon:x:1:1:daemon:/usr/sbin:/bin/sh
    bin:x:2:2:bin:/bin:/bin/sh
    sys:x:3:3:sys:/dev:/bin/sh
    sync:x:4:65534:sync:/bin:/bin/sync
    games:x:5:60:games:/usr/games:/bin/sh
    man:x:6:12:man:/var/cache/man:/bin/sh
    mail:x:8:8:mail:/var/mail:/bin/sh
    news:x:9:9:news:/var/spool/news:/bin/sh
    backup:x:34:34:backup:/var/backups:/bin/sh
    $
    
    注意数字 1 加在 delete edit命令。这指示 sed 在文件的第一行执行编辑命令。在这个例子中,sed 将删除第一行/etc/password 并打印文件的其余部分。
  • sed 地址范围

    我们现在将了解如何使用 the sed address ranges. 那么如果你想从一个文件中删除多于一行怎么办?您可以使用 sed 指定地址范围,如下所示 -
    
    $ cat /etc/passwd | sed '1, 5d' |more
    games:x:5:60:games:/usr/games:/bin/sh
    man:x:6:12:man:/var/cache/man:/bin/sh
    mail:x:8:8:mail:/var/mail:/bin/sh
    news:x:9:9:news:/var/spool/news:/bin/sh
    backup:x:34:34:backup:/var/backups:/bin/sh
    $
    
    上述命令将应用于从 1 到 5 的所有行。这将删除前五行。
    尝试以下地址范围 -
    序号 范围和描述
    1
    '4,10d'
    从第4个直到10个被删除
    2
    '10,4d'
    只删除了第10行,因为 sed 不能反向工作
    3
    '4,&plus5d'
    这匹配文件中的第 4 行,删除该行,继续删除接下来的五行,然后停止删除并打印其余的行
    4
    '2,5!d'
    这将删除一切,除了从2行直到第5行
    5
    '1~3d'
    这将删除第一行,跳过接下来的三行,然后删除第四行。Sed 继续应用此模式,直到文件结束。
    6
    '2~2d'
    这告诉 sed 删除第二行,跳过下一行,删除下一​​行,并重复直到到达文件末尾
    7
    '4,10p'
    打印第4行到第10行
    8
    '4,d'
    这会产生语法错误
    9
    ',10d'
    这也会产生语法错误
    Note − 在使用 p 行动,你应该使用 -n避免重复行打印的选项。检查以下两个命令之间的差异 -
    
    $ cat /etc/passwd | sed -n '1,3p'
    Check the above command without -n as follows −
    $ cat /etc/passwd | sed '1,3p'
    
  • 替换命令

    替换命令,表示为 s, 将用您指定的任何其他字符串替换您指定的任何字符串。
    要将一个字符串替换为另一个字符串,sed 需要具有有关第一个字符串结束位置和替换字符串开始位置的信息。为此,我们继续使用正斜杠 (/) 特点。
    以下命令替换字符串的一行中的第一次出现 root 用字符串 amrood.
    
    $ cat /etc/passwd | sed 's/root/amrood/'
    amrood:x:0:0:root user:/root:/bin/sh
    daemon:x:1:1:daemon:/usr/sbin:/bin/sh
    ..........................
    
    需要注意的是 sed 只替换一行中的第一次出现,这一点非常重要。如果字符串根在一行中出现不止一次,则只会替换第一个匹配项。
    对于执行全局替换的 sed,添加字母 g 到命令的末尾,如下所示 -
    
    $ cat /etc/passwd | sed 's/root/amrood/g'
    amrood:x:0:0:amrood user:/amrood:/bin/sh
    daemon:x:1:1:daemon:/usr/sbin:/bin/sh
    bin:x:2:2:bin:/bin:/bin/sh
    sys:x:3:3:sys:/dev:/bin/sh
    ...........................
    
  • 替换标志

    除了 g 标志,您可以一次指定多个。
    序号 标志和描述
    1
    g
    替换所有匹配项,而不仅仅是第一个匹配项
    2
    NUMBER
    仅替换第 NUMBER个匹配项
    3
    p
    如果进行了替换,则打印模式空间
    4
    w FILENAME
    如果进行了替换,则将结果写入 FILENAME
    5
    I or i
    以不区分大小写的方式匹配
    6
    M or m
    除了特殊正则表达式字符 ^ 和 $ 的正常行为外,该标志还会导致 ^ 匹配换行符之后的空字符串,而 $ 匹配换行符之前的空字符串
  • 使用替代字符串分隔符

    假设您必须对包含正斜杠字符的字符串进行替换。在这种情况下,您可以通过在后面提供指定的字符来指定不同的分隔符s.
    
    $ cat /etc/passwd | sed 's:/root:/amrood:g'
    amrood:x:0:0:amrood user:/amrood:/bin/sh
    daemon:x:1:1:daemon:/usr/sbin:/bin/sh
    
    在上面的例子中,我们使用了 : 作为 delimiter 而不是斜线 / 因为我们试图搜索 /root 而不是简单的根。
  • 用空白空间替换

    使用空替换字符串从 /etc/passwd 完全归档 -
    
    $ cat /etc/passwd | sed 's/root//g'
    :x:0:0::/:/bin/sh
    daemon:x:1:1:daemon:/usr/sbin:/bin/sh
    
  • 地址替换

    如果你想替换字符串 sh 用字符串 quiet 仅在第 10 行,您可以指定如下 -
    
    $ cat /etc/passwd | sed '10s/sh/quiet/g'
    root:x:0:0:root user:/root:/bin/sh
    daemon:x:1:1:daemon:/usr/sbin:/bin/sh
    bin:x:2:2:bin:/bin:/bin/sh
    sys:x:3:3:sys:/dev:/bin/sh
    sync:x:4:65534:sync:/bin:/bin/sync
    games:x:5:60:games:/usr/games:/bin/sh
    man:x:6:12:man:/var/cache/man:/bin/sh
    mail:x:8:8:mail:/var/mail:/bin/sh
    news:x:9:9:news:/var/spool/news:/bin/sh
    backup:x:34:34:backup:/var/backups:/bin/quiet
    
    同样,要进行地址范围替换,您可以执行以下操作 -
    
    $ cat /etc/passwd | sed '1,5s/sh/quiet/g'
    root:x:0:0:root user:/root:/bin/quiet
    daemon:x:1:1:daemon:/usr/sbin:/bin/quiet
    bin:x:2:2:bin:/bin:/bin/quiet
    sys:x:3:3:sys:/dev:/bin/quiet
    sync:x:4:65534:sync:/bin:/bin/sync
    games:x:5:60:games:/usr/games:/bin/sh
    man:x:6:12:man:/var/cache/man:/bin/sh
    mail:x:8:8:mail:/var/mail:/bin/sh
    news:x:9:9:news:/var/spool/news:/bin/sh
    backup:x:34:34:backup:/var/backups:/bin/sh
    
    从输出中可以看出,前五行包含字符串 sh 变成 quiet,但其余的行保持不变。
  • 匹配命令

    你会使用 p 选项与 -n 打印所有匹配行的选项如下 -
    
    $ cat testing | sed -n '/root/p'
    root:x:0:0:root user:/root:/bin/sh
    [root@ip-72-167-112-17 amrood]# vi testing
    root:x:0:0:root user:/root:/bin/sh
    daemon:x:1:1:daemon:/usr/sbin:/bin/sh
    bin:x:2:2:bin:/bin:/bin/sh
    sys:x:3:3:sys:/dev:/bin/sh
    sync:x:4:65534:sync:/bin:/bin/sync
    games:x:5:60:games:/usr/games:/bin/sh
    man:x:6:12:man:/var/cache/man:/bin/sh
    mail:x:8:8:mail:/var/mail:/bin/sh
    news:x:9:9:news:/var/spool/news:/bin/sh
    backup:x:34:34:backup:/var/backups:/bin/sh
    
  • 使用正则表达式

    在匹配模式时,您可以使用提供更多灵活性的正则表达式。
    检查以下示例,该示例匹配所有以daemon开头的行,然后删除它们 -
    
    $ cat testing | sed '/^daemon/d'
    root:x:0:0:root user:/root:/bin/sh
    bin:x:2:2:bin:/bin:/bin/sh
    sys:x:3:3:sys:/dev:/bin/sh
    sync:x:4:65534:sync:/bin:/bin/sync
    games:x:5:60:games:/usr/games:/bin/sh
    man:x:6:12:man:/var/cache/man:/bin/sh
    mail:x:8:8:mail:/var/mail:/bin/sh
    news:x:9:9:news:/var/spool/news:/bin/sh
    backup:x:34:34:backup:/var/backups:/bin/sh
    
    以下是删除所有以 sh
    
    $ cat testing | sed '/sh$/d'
    sync:x:4:65534:sync:/bin:/bin/sync
    
    下表列出了在正则表达式中非常有用的四个特殊字符。
    序号 字符和描述
    1
    ^
    匹配行的开头
    2
    $
    匹配行尾
    3
    .
    匹配任何单个字符
    4
    *
    匹配前一个字符的零次或多次出现
    5
    [chars]
    匹配 chars 中给出的任何一个字符,其中 chars 是一个字符序列。您可以使用 - 字符来指示字符范围。
  • 匹配字符

    再看几个表达式来演示使用 metacharacters. 例如,以下模式 -
    序号 表达和描述
    1
    /a.c/
    匹配包含字符串的行,例如 a&plusc, a-c, abc, match, 和 a3c
    2
    /a*c/
    匹配相同的字符串以及字符串,例如 ace, yacc, 和 arctic
    3
    /[tT]he/
    匹配字符串 Thethe
    4
    /^$/
    匹配空行
    5
    /^.*$/
    匹配整行,无论它是什么
    6
    / */
    匹配一个或多个空格
    7
    /^$/
    火柴 blank 线
    下表显示了一些常用的字符集 -
    序号 设置和说明
    1
    [a-z]
    匹配单个小写字母
    2
    [A-Z]
    匹配单个大写字母
    3
    [a-zA-Z]
    匹配单个字母
    4
    [0-9]
    匹配单个数字
    5
    [a-zA-Z0-9]
    匹配单个字母或数字
  • 字符类关键字

    一些特殊的关键字通常可用于 regexps,尤其是使用 regexps. 这些对于 sed 正则表达式非常有用,因为它们可以简化事情并增强可读性。
    例如,字符 a through z 和字符 A through Z, 构成这样一类具有关键字的字符 [[:alpha:]]
    使用字母字符类关键字,此命令仅打印 /etc/syslog.conf 以字母开头的文件 -
    
    $ cat /etc/syslog.conf | sed -n '/^[[:alpha:]]/p'
    authpriv.*                         /var/log/secure
    mail.*                             -/var/log/maillog
    cron.*                             /var/log/cron
    uucp,news.crit                     /var/log/spooler
    local7.*                           /var/log/boot.log
    
    下表是 GNU sed 中可用字符类关键字的完整列表。
    序号 字符类和描述
    1
    [[:alnum:]]
    字母数字 [az AZ 0-9]
    2
    [[:alpha:]]
    字母 [az AZ]
    3
    [[:blank:]]
    空白字符(空格或制表符)
    4
    [[:cntrl:]]
    控制字符
    5
    [[:digit:]]
    数字 [0-9]
    6
    [[:graph:]]
    任何可见字符(不包括空格)
    7
    [[:lower:]]
    小写字母 [az]
    8
    [[:print:]]
    可打印字符(非控制字符)
    9
    [[:punct:]]
    标点符号
    10
    [[:space:]]
    空白
    11
    [[:upper:]]
    大写字母 [AZ]
    12
    [[:xdigit:]]
    十六进制数字 [0-9 af AF]
  • 与号引用

    sed metacharacter &表示匹配模式的内容。例如,假设您有一个名为phone.txt 充满电话号码,例如以下 -
    
    5555551212
    5555551213
    5555551214
    6665551215
    6665551216
    7775551217
    
    你想让 area code(前三位数字)用括号括起来以便于阅读。为此,您可以使用与号替换字符 -
    
    $ sed -e 's/^[[:digit:]][[:digit:]][[:digit:]]/(&)/g' phone.txt
    (555)5551212
    (555)5551213
    (555)5551214
    (666)5551215
    (666)5551216
    (777)5551217
    
    在模式部分,您匹配前 3 位数字,然后使用 & 您正在用周围的数字替换这 3 位数字 parentheses.
  • 使用多个 sed 命令

    您可以在单个 sed 命令中使用多个 sed 命令,如下所示 -
    
    $ sed -e 'command1' -e 'command2' ... -e 'commandN' files
    
    这里 command1 通过 commandN是前面讨论过的类型的 sed 命令。这些命令应用于 files 给出的文件列表中的每一行。
    使用相同的机制,我们可以编写上面的电话号码示例如下 -
    
    $ sed -e 's/^[[:digit:]]\{3\}/(&)/g'  \ 
       -e 's/)[[:digit:]]\{3\}/&-/g' phone.txt 
    (555)555-1212 
    (555)555-1213 
    (555)555-1214 
    (666)555-1215 
    (666)555-1216 
    (777)555-1217
    
    Note - 在上面的例子中,而不是重复字符类关键字 [[:digit:]] 三次,我们将其替换为 \{3\},这意味着前面的正则表达式匹配了 3 次。我们也用过\ 给出换行符,并且必须在运行命令之前将其删除。
  • 返回参考

    ampersand metacharacter很有用,但更有用的是在正则表达式中定义特定区域的能力。这些特殊区域可用作替换字符串中的参考。通过定义正则表达式的特定部分,您可以使用特殊的引用字符来引用这些部分。
    去做 back references,您必须先定义一个区域,然后再引用该区域。要定义区域,请插入backslashed parentheses围绕每个感兴趣的区域。然后用反斜杠包围的第一个区域被引用\1,第二个区域由 \2, 等等。
    假设 phone.txt 有以下文字 -
    
    (555)555-1212
    (555)555-1213
    (555)555-1214
    (666)555-1215
    (666)555-1216
    (777)555-1217
    
    尝试以下命令 -
    
    $ cat phone.txt | sed 's/\(.*)\)\(.*-\)\(.*$\)/Area \ 
       code: \1 Second: \2 Third: \3/' 
    Area code: (555) Second: 555- Third: 1212 
    Area code: (555) Second: 555- Third: 1213 
    Area code: (555) Second: 555- Third: 1214 
    Area code: (666) Second: 555- Third: 1215 
    Area code: (666) Second: 555- Third: 1216 
    Area code: (777) Second: 555- Third: 1217
    
    注意 - 在上面的例子中,括号内的每个正则表达式将被反向引用 \1, \2等等。我们用过\在这里换行。这应该在运行命令之前删除。