【问题标题】:Replace range of numbers with certain number用特定数字替换数字范围
【发布时间】:2015-07-11 17:37:38
【问题描述】:

我需要用某个数字替换一系列数字。我真的很努力地用 sed(如 sed "s/[33-64]/64/")或 awk 自己编写代码,但总是得到错误的结果。它倾向于替换单个数字而不是数字......我需要的是:替换 0-32 -> 32、33-64 -> 64、65-128 -> 128、129-255 -> 255。在这些之间数字是 IP,应该保持不变。我认为这个命令正在选择所有,但 IP:

sed '/[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}/! ...  '

所以我有一个这样的文件:

65.74.16.161
232
10.128.8.72
63
10.128.14.13
100
10.128.8.58
32
10.128.4.129
60
10.128.240.18
59

它应该是这样的:

65.74.16.161
255
10.128.8.72
64
10.128.14.13
128
10.128.8.58
32
10.128.4.129
64
10.128.240.18
64

【问题讨论】:

  • sed 是一个糟糕的方法。你必须写一些疯狂的表达,比如's/\<\([0-9]\|[12][0-9]\|3[012]\)\>/32/g'

标签: bash awk sed grep


【解决方案1】:

[33-64] 定义了一个字符类,是一种有趣的书写方式[3-6],并且确实只匹配单个字符——3、4、5 或 6 中的任何单个数字。如果你真的想这样做sed,而你关心的是 33 到 64 之间的值,那么你必须以不同的方式写出它——而且更冗长。

部分取决于您拥有的sed 版本。一个适用于经典 sed 的解决方案是:

sed -e 's/^[0-9]$/32/' \
    -e 's/^[12][0-9]$/32/' \
    -e 's/^3[012]$/32/' \
    -e 's/^3[3-9]$/64/' \
    -e 's/^[45][0-9]$/64/' \
    -e 's/^6[0-4]$/64/' \
    -e 's/^6[5-9]$/128/' \
    -e 's/^[7-9][0-9]$/128/' \
    -e 's/^1[01][0-9]$/128/' \
    -e 's/^12[0-8]$/128/' \
    -e 's/^129$/255/' \
    -e 's/^1[3-9][0-9]$/255/' \
    -e 's/^2[0-4][0-9]$/255/' \
    -e 's/^25[0-5]$/255/'

但是,如您所见,这非常痛苦。如果你有 GNU sed,你可以使用-r 选项来启用扩展正则表达式;如果您有 Mac OS X 或 BSD sed,您可以使用 -E 选项来启用扩展正则表达式。然后你可以将上面的代码简化为:

sed -E \
    -e 's/^([0-9]|[12][0-9]|3[012])$/32/' \
    -e 's/^(3[3-9]|[45][0-9]|6[0-4])$/64/' \
    -e 's/^(6[5-9]|[7-9][0-9]|1[01][0-9]|12[0-8])$/128/' \
    -e 's/^(129|1[3-9][0-9]|2[0-4][0-9]|25[0-5])$/255/'

但是,使用awk 可能会做得更好:

awk '/^[0-9][0-9]*$/ { if      ($1 <=  32) print  32
                       else if ($1 <=  64) print  64
                       else if ($1 <= 128) print 128
                       else if ($1 <= 255) print 255
                       else                print  $1
                       next
                     }
     { print }'

最后的 else 子句准确地打印出任何意外值,例如 256 或 999,或者实际上是 123456789。有些人会写 1 代替 { print }awk 脚本的一部分匹配并打印 IP 地址。

【讨论】:

  • AWK 的优秀作品。这立即表明了目的是什么。
  • 您好,感谢您的建议。我不知道是什么问题,但它都不能在我的机器上运行。我尝试了所有 3 个代码片段以及其他两个建议,但它没有改变输出,数字保持不变。我有 mac os x 10.10.3。某些选项或 bash 版本可能有问题吗?
  • 我在 Mac OS X 10.10.4 上进行测试,并复制/粘贴了材料和您的数据,并获得了预期的输出。并且需要进行几次调整才能使awk 正确,所以我知道我的测试序列在输出中发现了错误。我的代码假定没有空格;如果有前导或尾随空格(或者如果数据文件是带有 CRLF 行结尾的 DOS 格式文件),它不会改变任何内容。
  • 对不起,它又试了一次,效果很好。非常感谢!
【解决方案2】:

您可以将这个awk 与一些算术一起使用:

awk '$1 == ($1+0) && $1<=255{$1 = ($1>128)?255:($1>64?128:32 * int(($1+31)/32))} 1' file
65.74.16.161
255
10.128.8.72
64
10.128.14.13
128
10.128.8.58
32
10.128.4.129
64
10.128.240.18
64

$1 == ($1+0) 是一个检查以确定$1 是一个整数。

【讨论】:

  • 出于好奇,如果您的值是 69(而不是 64),您的代码会打印 96 而不是 128。事实上,对于 65 到 96 之间的任何值,它都会打印 96当规范说它应该打印 128 时。您也可以使用仅包含 12345.6789 的行来获得乐趣;它被转换为 255,这不一定是正确的答案(尽管有一个参数值不应该在数据文件中)。
【解决方案3】:

使用 awk:

awk -vFS=. ' NF == 1 { v=2^int((log($1)/log(2))+0.5); $1 = v>255?255:v; }1' input

给予:

65.74.16.161
255
10.128.8.72
64
10.128.14.13
128
10.128.8.58
32
10.128.4.129
64
10.128.240.18
64

【讨论】:

    猜你喜欢
    • 2021-01-02
    • 1970-01-01
    • 2023-04-05
    • 1970-01-01
    • 2022-10-15
    • 2018-08-26
    • 2014-07-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多