替换多个模式,但不是用相同的字符串
Replace multiple patterns, but not with the same string
是否可以在同一命令中将乘法模式更改为不同的值?
假设我有
A B C D ABC
并且我想将每个 A 更改为 1,每个 B 更改为 2,每个 C 更改为 3
所以输出将是
1 2 3 D 123
因为我有 3 个模式要更改,所以我想避免单独替换它们。
我以为会有类似
sed -r s/'(A|B|C)'/(1|2|3)/
当然这只是将 A 或 B 或 C 替换为 (1|2|3)。
我应该提一下,我的真实模式比那更复杂......
谢谢!
在 Perl 中很简单:
perl -pe '%h = (A => 1, B => 2, C => 3); s/(A|B|C)/$h{}/g'
如果您使用更复杂的模式,请将更具体的模式放在备用列表中更通用的模式之前。按长度排序可能就足够了:
perl -pe 'BEGIN { %h = (A => 1, AA => 2, AAA => 3);
$re = join "|", sort { length $b <=> length $a } keys %h; }
s/($re)/$h{}/g'
要添加字或行边界,只需将模式更改为
/\b($re)\b/
# or
/^($re)$/
# resp.
在 sed
中很容易:
sed 's/WORD1/NEW_WORD1/g;s/WORD2/NEW_WORD2/g;s/WORD3/NEW_WORD3/g'
您可以在同一行中用 ;
分隔多个命令
更新
可能这太简单了。 指出上述命令可能会导致不需要的结果,因为第二次替换甚至可能触及第一次替换的结果(依此类推)。
如果您关心这一点,可以使用 t
命令避免这种副作用。 t
命令分支到脚本的末尾,但前提是确实发生了替换:
sed 's/WORD1/NEW_WORD1/g;t;s/WORD2/NEW_WORD2/g;t;s/WORD3/NEW_WORD3/g'
如果您的 "words" 不包含 RE 元字符(. * ? 等),这将起作用:
$ cat file
there is the problem when the foo is closed
$ cat tst.awk
BEGIN {
split("the a foo bar",tmp)
for (i=1;i in tmp;i+=2) {
old = (i>1 ? old "|" : "\<(") tmp[i]
map[tmp[i]] = tmp[i+1]
}
old = old ")\>"
}
{
head = ""
tail = [=10=]
while ( match(tail,old) ) {
head = head substr(tail,1,RSTART-1) map[substr(tail,RSTART,RLENGTH)]
tail = substr(tail,RSTART+RLENGTH)
}
print head tail
}
$ awk -f tst.awk file
there is a problem when a bar is closed
上面显然将 "the" 映射到 "a" 并将 "foo" 映射到 "bar" 并使用 GNU awk 作为单词边界。
如果您的 "words" 确实包含 RE 元字符等,那么您需要使用 index()
的基于字符串的解决方案,而不是使用 match()
的基于 RE 的解决方案(请注意 sed
仅支持 RE,不支持字符串)。
替换为javascript
中的回调函数
类似于 choroba
的 perl 解决方案
var i = 'abcd'
var r = {ab: "cd", cd: "ab"}
var o = i.replace(/ab|cd/g, (...args) => r[args[0]])
o == 'cdab'
可以使用像 /(ab)|(cd)/g
这样的捕获组进行优化
并检查 args[i]
的 undefined
值
是否可以在同一命令中将乘法模式更改为不同的值? 假设我有
A B C D ABC
并且我想将每个 A 更改为 1,每个 B 更改为 2,每个 C 更改为 3
所以输出将是
1 2 3 D 123
因为我有 3 个模式要更改,所以我想避免单独替换它们。 我以为会有类似
sed -r s/'(A|B|C)'/(1|2|3)/
当然这只是将 A 或 B 或 C 替换为 (1|2|3)。 我应该提一下,我的真实模式比那更复杂......
谢谢!
在 Perl 中很简单:
perl -pe '%h = (A => 1, B => 2, C => 3); s/(A|B|C)/$h{}/g'
如果您使用更复杂的模式,请将更具体的模式放在备用列表中更通用的模式之前。按长度排序可能就足够了:
perl -pe 'BEGIN { %h = (A => 1, AA => 2, AAA => 3);
$re = join "|", sort { length $b <=> length $a } keys %h; }
s/($re)/$h{}/g'
要添加字或行边界,只需将模式更改为
/\b($re)\b/
# or
/^($re)$/
# resp.
在 sed
中很容易:
sed 's/WORD1/NEW_WORD1/g;s/WORD2/NEW_WORD2/g;s/WORD3/NEW_WORD3/g'
您可以在同一行中用 ;
更新
可能这太简单了。
如果您关心这一点,可以使用 t
命令避免这种副作用。 t
命令分支到脚本的末尾,但前提是确实发生了替换:
sed 's/WORD1/NEW_WORD1/g;t;s/WORD2/NEW_WORD2/g;t;s/WORD3/NEW_WORD3/g'
如果您的 "words" 不包含 RE 元字符(. * ? 等),这将起作用:
$ cat file
there is the problem when the foo is closed
$ cat tst.awk
BEGIN {
split("the a foo bar",tmp)
for (i=1;i in tmp;i+=2) {
old = (i>1 ? old "|" : "\<(") tmp[i]
map[tmp[i]] = tmp[i+1]
}
old = old ")\>"
}
{
head = ""
tail = [=10=]
while ( match(tail,old) ) {
head = head substr(tail,1,RSTART-1) map[substr(tail,RSTART,RLENGTH)]
tail = substr(tail,RSTART+RLENGTH)
}
print head tail
}
$ awk -f tst.awk file
there is a problem when a bar is closed
上面显然将 "the" 映射到 "a" 并将 "foo" 映射到 "bar" 并使用 GNU awk 作为单词边界。
如果您的 "words" 确实包含 RE 元字符等,那么您需要使用 index()
的基于字符串的解决方案,而不是使用 match()
的基于 RE 的解决方案(请注意 sed
仅支持 RE,不支持字符串)。
替换为javascript
中的回调函数类似于 choroba
var i = 'abcd'
var r = {ab: "cd", cd: "ab"}
var o = i.replace(/ab|cd/g, (...args) => r[args[0]])
o == 'cdab'
可以使用像 /(ab)|(cd)/g
这样的捕获组进行优化
并检查 args[i]
的 undefined
值