Bash:sed 或 awk 重写数字序列

Bash:sed 或 awk 重写数字序列

如何编写 a sed(或awk,或两者),它将重写以下内容:

echo 'v100 v201 v102 v300 v301 v500 v999 v301' | sed/awk ...

到这个输出:

v1 v2 v3 v4 v5 v6 v7 v5

即每个后续的vx都被重写以开始并且在序列中使用v1...vn相同的地方(即)应该应用相同的(如)。vv301vv5

旁注:示例输入序列显示了所有可能的情况(即重复、原始数据乱序、原始数字跳跃)。

您是可以回答这个问题的 sed 或 awk 专家吗?

答案1

使用awk

awk '{ for (i=1; i<=NF; ++i) $i = (seen[$i] ? seen[$i] : seen[$i] = "v" ++n) }; 1'

这将遍历每个输入行的所有字段并重新分配它。重新分配的值后面v跟着 counter 的下一个值n,除非之前已经见过该字段的值,在这种情况下,其新值将与之前给出的该字段的值相同。

最后1的 触发修改行的输出。

测试:

$ echo 'v100 v201 v102 v300 v301 v500 v999 v301' | awk '{ for (i=1; i<=NF; ++i) $i = (seen[$i] ? seen[$i] : seen[$i] = "v" ++n) }; 1'
v1 v2 v3 v4 v5 v6 v7 v5

awk仅当字段与正则表达式匹配时才修改字段的替代命令^v[0-9]+$

awk '{ for (i=1; i<=NF; ++i) if ($i ~ "^v[0-9]+$") $i = (seen[$i] ? seen[$i] : seen[$i] = "v" ++n) }; 1'

或者,为了可读性,格式化为多行:

awk '
{
    for (i=1; i<=NF; ++i)
        if ($i ~ "^v[0-9]+$")
            $i = (seen[$i] ? seen[$i] : seen[$i] = "v" ++n)
}; 1'

答案2

perl

$ echo 'v100 v201 v102 v300 v301 v500 v999 v301' |
   perl -pe 's{v\K\d+}{$seen{$&} //= ++$n}ge'
v1 v2 v3 v4 v5 v6 v7 v5
  • v\d+匹配v后跟一个或多个十进制数字。 after\K重置v匹配部分的开始,K保留其左侧的内容v,以便仅替换数字序列s
  • e标志导致替换被视为代码对其e进行评估以生产替代品。在该代码中,$&包含匹配的部分。
  • A // B是一种形式或者扩展为AifA被定义,B否则(与A || B它扩展为AifA解析为相反)真的值和B其他)。//=是相应的作业形式。 SoA //= B是 的缩写if (defined(A)) {A} else {A = B}

请注意,$seen哈希表的索引是细绳这些数字的值,依此类推,v2 v02 v002您将得到、和 ,它们是彼此不同的字符串。您可以替换为来标准化数字(010 被视为 10,而不是八进制 8),以便进入上面的示例。或者你可以保留前导s 并得到结果。v1 v2 v3202002$&0+$&v1 v1 v1s{v0*\K\d+}{$seen{$&} //= ++$n}ge0v1 v01 v001

为了避免替换例如v1中找到的内容rev1sion,您可以添加一些b边界匹配两边的正则表达式运算符 ( \bv\K\d+\b)。或者仅替换以空格分隔的单词(v1.2例如,单独保留),添加一些消极环视对于非白人S步伐:(?<!\S)v\K\d+(?!\S).

答案3

GNU 实现awk支持RS将其定义为正则表达式并将其匹配的内容记录在RT特殊变量中。因此,有了它,您可以执行以下操作:

$ echo 'v100 v201 v102 v300 v301 v500 v999 v301' |
   gawk -v RS='v[0-9]+' -v ORS= '
     RT {$0 = $0 "v" (RT in seen ? seen[RT] : seen[RT] = ++n)}1'
v1 v2 v3 v4 v5 v6 v7 v5

请注意,它会替换所有出现的v后跟数字的情况,甚至是在单词(如 inrev1.2或 )中找到的数字rev0lution。像我的 Perl 方法,如果数字可能是零填充的,您可能需要调整它。

答案4

仅限 GNU awk:

echo 'v100 v201 v102 v300 v301 v500 v999 v301' |
  awk -v RS='[[:space:]]' -F '' '
    $0 {printf "%s", $1 (A[$0]?A[$0]:A[$0]=++i) RT}'
v1 v2 v3 v4 v5 v6 v7 v5

相关内容