这应该是直截了当的,但我无法弄清楚。如果我想使用 sed 将 A 或 B 替换为 C,代码可能是:
$ echo AAXXAAYYBB | sed 's/[AB]/C/g'
CCXXCCYYCC
这会导致所有 A 和 B 都转换为 C。
我想做的是将“A”替换为两个(或可能更多)变量之一:
输入:
AAXXAAYYBB
代码:
sed 's/A/[BC]/g'
输出(其中 B 或 C 的替换是随机的):
BCXXCBYYBB
但这段代码只会将 A 更改为...
$ echo AAXXAAYYBB | sed 's/A/[BC]/g'
[BC][BC]XX[BC][BC]YYBB
如果可能的话,我试图在这里避免使用 PERL。有谁知道如何解决这个问题?
答案1
可以更换第一的字符串与以下内容的匹配:
${str/A/...}
并且,可以通过以下方式生成随机(不是加密安全数字)值:
r=(B C)
${r[RANDOM%2]}
每次变量r
都会扩展。
一个完全相同但实现速度更快的操作是使用 AND 来提取值的最后一位:${r[RANDOM&1]}
所以:
#!/bin/bash
str=AAXXAAYYBB
r=(B C)
while [ "${str%"${str#*A}"}" ]; do # while there is an A to change
str=${str/A/"${r[RANDOM&1]}"}
done
echo "str=$str"
每次调用时都会生成一个随机结果。
波斯克斯利
#!/bin/sh
str=AAXXAAYYZZAAA
while [ "${str%"${str#*A}"}" ]; do # while there is an A.
r=$(od -An -tu1 -N 1 /dev/urandom) # get one random byte
r=$((r&1)) # Is it even or odd?
if [ "$r" -eq 0 ]; then s=B; else s=C;fi # Select B or C
str="${str%%A*}${s}${str#*A}" # Change the string.
done
echo "str=$str"
也许随机字节可以用更简单(但更神秘)的方式读取,大多数时候使用更快的内置 printf:
r=$(printf '%d\n' "'$(head -c1 /dev/urandom)")
答案2
不是 Sed,但避免 Perl:
$ echo AAXXAAYYBB | gawk '
BEGIN{srand()}
{
n = patsplit($0,a,/A/,s);
for(i=1;i<=n;i++) printf("%s%s", rand() < 0.5 ? "B" : "C", s[i]);
print ""
}
'
CBXXCCYYBB
答案3
一旦我们诉诸脚本,就有很多方法给这只特定的猫剥皮,但这里是我整理的一些东西 - 它可能不太漂亮(并且依赖于 bash shell!),但它可能会帮助你:
#!/bin/bash
TEXT="AAXXAAYYBB"
echo "Start: $TEXT"
# So long as there are un-converted 'A' in the input string...
while [[ "$TEXT" =~ A ]]
do
# .. convert one 'A' to a random choice of either 'B' or 'C'
TEXT=$(echo $TEXT | sed -e "s/A/$(((RANDOM%2>0))&&echo B || echo C)/")
# lets show how we are progressing...
echo "Progress: $TEXT"
done
# No more 'A' in the input string, we are done:
echo "End: $TEXT"
输出示例:
第一次运行:
Start: AAXXAAYYBB
Progress: BAXXAAYYBB
Progress: BBXXAAYYBB
Progress: BBXXBAYYBB
Progress: BBXXBCYYBB
End: BBXXBCYYBB
第二次运行:
Start: AAXXAAYYBB
Progress: CAXXAAYYBB
Progress: CBXXAAYYBB
Progress: CBXXCAYYBB
Progress: CBXXCBYYBB
End: CBXXCBYYBB
答案4
Perl 喜欢正则表达式
如果您选择使用 perl,您会发现该e
标志对于替换很有用。这e
会将替换评估为代码。
例如:
s/A/c("BC")/eg
其中 c 是从字符串中提取随机字符的子例程。
使用 A->[BC] 进行硬编码:
sub c {
if(my $s = shift) {
my $index = int(rand(length($s)));
return substr($s, $index, 1);
}
}
while(<<>>){
print s/A/C("BC")/eg
}
或者压缩成一篇不太漂亮的单行诗。 (为了清楚起见,实际上是两行):
perl -E 'sub c {if(my $l = shift) {substr($l, int(rand(length($l))), 1);}}' -plE 's/A/c(BCBBB)/eg'
展开为 random_replace:
#!/usr/bin/env perl
use strict;
use warnings;
die "usage: random_replace regex string_of_replacement_chars\n" unless @ARGV == 2;
my $search = shift;
my $replace = shift;
sub c {
if (my $s = shift) {
my $index = int(rand(length($s)));
substr($s, $index, 1);
}
}
while(<<>>)
{
s/$search/c($replace)/eg;
print;
}
% echo "AAAAAA" | ./random_replace A BC
CCCCBC
% echo "AAAAAA" | ./random_replace A BC
BBBBBC
作为额外的好处,搜索可以是正则表达式。假设您想用 C 或 D 替换 A 或 B:
% echo "AAABBB" | ./random_replace '[A-B]' CD
CCCDCD
% echo "AAABBB" | ./random_replace '[A-B]' CD
DDCCDD