【问题标题】:Sed substitution possible with arithmetic involved?涉及算术的sed替换可能吗?
【发布时间】:2015-05-08 13:19:06
【问题描述】:

我需要修改的文件包含以下内容:

block: 16, size: 16, start: 8, length: 4

我想要该文件,以便 blocksize 的值除以 2,而 start 的值长度将乘以 2。

由于我必须对一大堆文件进行此类修改,我正在考虑使用 Sed 来为我做替换工作。但我不确定匹配和替换过程中是否允许计算。

【问题讨论】:

    标签: regex sed


    【解决方案1】:

    我总是尝试使用sed 解决带有 标记的每个问题。但是在这里很容易用awk 完成你想要做的事情。 (并且在这种情况下使用sed 太难了。)所以,这是我使用awk 的解决方案:

    $ echo "block: 16, size: 16, start: 8, length: 4" | awk '{
        printf "%s %d, %s %d, %s %d, %s %d\n", $1, $2/2, $3, $4/2, $5, $6*2, $7, $8*2
    }'
    block: 8, size: 8, start: 16, length: 8
    

    【讨论】:

    • 当条目的顺序不同时,这不会按预期工作,例如。 G。 "block: 16, start: 8, size: 8, length: 4"
    【解决方案2】:

    (执行此操作的正确工具是 awk,但为了获得 sed 练习的乐趣...)

    在 sed 中是可能的。毕竟,根据一些简单的规则,乘以 2 是最后一位数字的替换:

    • 0 --> 0
    • 1 --> 2
    • 2 --> 4
    • 3 --> 6
    • ...
    • 8 --> 16
    • 9 --> 18

    要注意进位,每个规则应该写两次。

    这个可以使用sed -f script 运行的 sed 脚本将输入行上的所有数字乘以 2:

    s/$/\n\n/
    :loop
    s/0\n1\n/\n\n1/;t loop
    s/0\n\n/\n\n0/;t loop
    s/1\n1\n/\n\n3/;t loop
    s/1\n\n/\n\n2/;t loop
    s/2\n1\n/\n\n5/;t loop
    s/2\n\n/\n\n4/;t loop
    s/3\n1\n/\n\n7/;t loop
    s/3\n\n/\n\n6/;t loop
    s/4\n1\n/\n\n9/;t loop
    s/4\n\n/\n\n8/;t loop
    s/5\n1\n/\n1\n1/;t loop
    s/5\n\n/\n1\n0/;t loop
    s/6\n1\n/\n1\n3/;t loop
    s/6\n\n/\n1\n2/;t loop
    s/7\n1\n/\n1\n5/;t loop
    s/7\n\n/\n1\n4/;t loop
    s/8\n1\n/\n1\n7/;t loop
    s/8\n\n/\n1\n6/;t loop
    s/9\n1\n/\n1\n9/;t loop
    s/9\n\n/\n1\n8/;t loop
    s/\n1\n/\n\n1/;t loop
    s/\(.\)\n\n/\n\n\1/;t loop
    s/^\n\n//
    

    将偶数除以 2,逻辑相同,但从左到右而不是从右到左:

    s/^/\n\n/
    :loop
    s/\n1\n0/5\n\n/;t loop
    s/\n\n0/0\n\n/;t loop
    s/\n1\n1/5\n1\n/;t loop
    s/\n\n1/\n1\n/;t loop
    s/\n1\n2/6\n\n/;t loop
    s/\n\n2/1\n\n/;t loop
    s/\n1\n3/6\n1\n/;t loop
    s/\n\n3/2\n1\n/;t loop
    s/\n1\n4/7\n\n/;t loop
    s/\n\n4/2\n\n/;t loop
    s/\n1\n5/7\n1\n/;t loop
    s/\n\n5/2\n1\n/;t loop
    s/\n1\n6/8\n\n/;t loop
    s/\n\n6/3\n\n/;t loop
    s/\n1\n7/8\n\n/;t loop
    s/\n\n7/3\n1\n/;t loop
    s/\n1\n8/9\n\n/;t loop
    s/\n\n8/4\n\n/;t loop
    s/\n1\n9/9\n1\n/;t loop
    s/\n\n9/4\n1\n/;t loop
    s/\n1\n/5\n\n/;t loop
    s/\n\n\(.\)/\1\n\n/;t loop
    s/\n\n$//
    

    结合这些,这个脚本可以完成工作:

    h
    s/, start.*//
    s/^/\n\n/
    t loopa
    :loopa
    s/\n1\n0/5\n\n/;t loopa
    s/\n\n0/0\n\n/;t loopa
    s/\n1\n1/5\n1\n/;t loopa
    s/\n\n1/\n1\n/;t loopa
    s/\n1\n2/6\n\n/;t loopa
    s/\n\n2/1\n\n/;t loopa
    s/\n1\n3/6\n1\n/;t loopa
    s/\n\n3/2\n1\n/;t loopa
    s/\n1\n4/7\n\n/;t loopa
    s/\n\n4/2\n\n/;t loopa
    s/\n1\n5/7\n1\n/;t loopa
    s/\n\n5/2\n1\n/;t loopa
    s/\n1\n6/8\n\n/;t loopa
    s/\n\n6/3\n\n/;t loopa
    s/\n1\n7/8\n\n/;t loopa
    s/\n\n7/3\n1\n/;t loopa
    s/\n1\n8/9\n\n/;t loopa
    s/\n\n8/4\n\n/;t loopa
    s/\n1\n9/9\n1\n/;t loopa
    s/\n\n9/4\n1\n/;t loopa
    s/\n1\n/5\n\n/;t loopa
    s/\n\n\(.\)/\1\n\n/;t loopa
    s/\n\n$//
    H
    g
    s/.*, start/, start/
    s/\n.*//
    s/$/\n\n/
    t loopb
    :loopb
    s/0\n1\n/\n\n1/;t loopb
    s/0\n\n/\n\n0/;t loopb
    s/1\n1\n/\n\n3/;t loopb
    s/1\n\n/\n\n2/;t loopb
    s/2\n1\n/\n\n5/;t loopb
    s/2\n\n/\n\n4/;t loopb
    s/3\n1\n/\n\n7/;t loopb
    s/3\n\n/\n\n6/;t loopb
    s/4\n1\n/\n\n9/;t loopb
    s/4\n\n/\n\n8/;t loopb
    s/5\n1\n/\n1\n1/;t loopb
    s/5\n\n/\n1\n0/;t loopb
    s/6\n1\n/\n1\n3/;t loopb
    s/6\n\n/\n1\n2/;t loopb
    s/7\n1\n/\n1\n5/;t loopb
    s/7\n\n/\n1\n4/;t loopb
    s/8\n1\n/\n1\n7/;t loopb
    s/8\n\n/\n1\n6/;t loopb
    s/9\n1\n/\n1\n9/;t loopb
    s/9\n\n/\n1\n8/;t loopb
    s/\n1\n/\n\n1/;t loopb
    s/\(.\)\n\n/\n\n\1/;t loopb
    s/^\n\n//
    H
    g
    s/[^\n]*\n//
    s/\n//
    

    (在 awk 思想中要容易得多。)

    注意:我曾经看到一个图灵机实现是 sed,所以我试着记住任何可以用编程语言完成的事情都可以在 sed 中完成。当然,这并不意味着 sed 在所有情况下都是好工具。

    【讨论】:

    • 我没有试图理解这一点,但根据您的评论,除了一位数字之外,这似乎会失败。您不能单独替换最后一个数字。
    • 我知道我不能单独替换最后一个数字。这就是为什么我有 2 个\n。对于乘法,右边是解,中间是进位。
    • 我在sed 中实现类似的算术循环时遇到了麻烦(为了挑战,而不是为了实际目的)。第一条t 指令似乎总是成功。 t 被定义为分支“如果自最近读取输入行或执行 t. 以来已经进行了任何替换”,在初始 s/$/\n\n/ 指令之后是正确的。因此,如果最后一位数字不是 0,则不会发生乘法运算。
    【解决方案3】:

    Perl 在这里很有用:

    perl -pe '
      s{(\D+)(\d+)(\D+)(\d+)(\D+)(\d+)(\D+)(\d+)}
       {$1 . $2/2 . $3 . $4/2 . $5 . $6*2 . $7 . $8*2}e
    ' file
    

    如果您想就地编辑文件,perl 有一个 -i 选项,例如 sed。

    【讨论】:

    • 你的意思是 sedperl 一样有-i 选项,而不是相反。您不会在 sed 的 POSIX 规范的所需选项列表中找到 -i 选项,顺便说一句,而只是偶尔和非正式地在某些供应商端口中找到。阅读:sed -i 不被普遍支持,尽管perl -i 是。
    【解决方案4】:

    不认为这是可能的。参见例如http://www.delorie.com/gnu/docs/sed/sed_15.html.

    但是,如果您只有一小组可能的块、大小、开始和长度值,那么硬编码所需的替换可能是最快的。下一个最简单的选项可能是使用awk,但这不能就地修改文件。

    【讨论】:

      猜你喜欢
      • 2018-07-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-04-07
      • 1970-01-01
      • 2020-05-16
      相关资源
      最近更新 更多