如何编写 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
相同的地方(即)应该应用相同的(如)。v
v301
v
v5
旁注:示例输入序列显示了所有可能的情况(即重复、原始数据乱序、原始数字跳跃)。
您是可以回答这个问题的 sed 或 awk 专家吗?
和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
的内容,以便仅替换数字序列。v
K
v
s
e
标志导致替换被视为被评估以产生替换的代码。e
在该代码中,$&
包含匹配的部分。A // B
是OR的一种形式,它扩展为A
ifA
已定义,B
否则(与此相反A || B
,它扩展为A
ifA
解析为真值,B
否则)。//=
是相应的作业形式。SoA //= B
是 的缩写if (defined(A)) {A} else {A = B}
。请注意,$seen
哈希表是根据这些数字的字符串值进行索引的,因此v2 v02 v002
,您将得到v1 v2 v3
、2
和02
,002
它们是彼此不同的字符串。您可以替换$&
为0+$&
来标准化数字(010 被视为 10,而不是八进制 8),以便进入v1 v1 v1
上面的示例。或者你可以s{v0*\K\d+}{$seen{$&} //= ++$n}ge
保留前导0
s 并得到v1 v01 v001
结果。
为了避免替换v1
在rev1sion
例如中找到的内容,您可以在匹配项 ( ) 的两侧添加一些单词b
边界\bv\K\d+\b
正则表达式运算符。或者仅替换以空格分隔的单词(v1.2
例如,单独保留),为非白色步伐添加一些负面的环视: 。S
(?<!\S)v\K\d+(?!\S)
使用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)