Bash:sed 或 awk 重写数字序列

Roe*_*aar 4 bash sed awk

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

echo 'v100 v201 v102 v300 v301 v500 v999 v301' | sed/awk ...
Run Code Online (Sandbox Code Playgroud)

到这个输出:

v1 v2 v3 v4 v5 v6 v7 v5
Run Code Online (Sandbox Code Playgroud)

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

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

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

Sté*_*las 5

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
Run Code Online (Sandbox Code Playgroud)
  • v\d+匹配v后跟一个或多个十进制数字。after重置匹配部分的开始,保留其左侧\K的内容,以便仅替换数字序列。vKvs
  • e标志导致替换被视为被评估以产生替换的代码。e在该代码中,$&包含匹配的部分。
  • A // B是OR的一种形式,它扩展为AifA已定义,B否则(与此相反A || B,它扩展为AifA解析为值,B否则)。//=是相应的作业形式。SoA //= B是 的缩写if (defined(A)) {A} else {A = B}

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

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


Kus*_*nda 5

使用awk

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

这将遍历每个输入行的所有字段并重新分配它。重新分配的值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
Run Code Online (Sandbox Code Playgroud)

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'
Run Code Online (Sandbox Code Playgroud)

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

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