我希望能够在两种已知模式之间替换字符串。问题是,我想用一个长度相同的字符串替换它,只有' x'。
我们说我有一个包含以下内容的文件:
Hello.StringToBeReplaced.SecondString
Hello.ShortString.SecondString
我希望输出如下:
Hello.xxxxxxxxxxxxxxxxxx.SecondString
Hello.xxxxxxxxxxx.SecondString
答案 0 :(得分:4)
sed
循环您可以使用sed
,但所需的思维并不完全明显:
sed ':a;s/^\(Hello\.x*\)[^x]\(.*\.SecondString\)/\1x\2/;t a'
这是针对GNU sed
; BSD(Mac OS X)sed
和其他版本可能比较繁琐,需要:
sed -e ':a' -e 's/^\(Hello\.x*\)[^x]\(.*\.SecondString\)/\1x\2/' -e 't a'
两者的逻辑相同:
a
x
(捕获1),然后是非x
,以及任意其他数据加上第二个字符串(捕获2),并将其替换为捕获1的内容,x
和捕获2的内容。s///
命令发生了变化,请返回标签a
。当两个标记字符串之间没有非x
时,它会停止替换。
对正则表达式的两次调整允许代码在一行上识别模式的两个副本。丢失将匹配锚定到该行开头的^
,并将.*
更改为[^.]*
(以便正则表达式不那么贪婪):
$ echo Hello.StringToBeReplaced.SecondString Hello.StringToBeReplaced.SecondString |
> sed ':a;s/\(Hello\.x*\)[^x]\([^.]*\.SecondString\)/\1x\2/;t a'
Hello.xxxxxxxxxxxxxxxxxx.SecondString Hello.xxxxxxxxxxxxxxxxxx.SecondString
$
hek2mgl建议sed
使用保留空间的替代方法。这可以使用以下方式实现:
$ echo Hello.StringToBeReplaced.SecondString |
> sed 's/^\(Hello\.\)\([^.]\{1,\}\)\(\.SecondString\)/\1@\3@@\2/
> h
> s/.*@@//
> s/./x/g
> G
> s/\(x*\)\n\([^@]*\)@\([^@]*\)@@.*/\2\1\3/
> '
Hello.xxxxxxxxxxxxxxxxxx.SecondString
$
这个脚本不像循环版本那样健壮,但是当每一行与前中尾模式匹配时,写入正常。它首先将线分为三个部分:第一个标记,要修复的位和第二个标记。它重组了这两个标记,以便@
分隔两个标记,然后是@@
,并且要修改这个位。 h
将结果复制到保留空间。删除所有内容,包括@@
;用x
替换要被修改的位中的每个字符,然后在模式空间中的x
之后复制保留空间中的材料,并用换行符分隔它们。最后,识别并捕获x
,主要标记和尾部标记,忽略换行符,@
和@@
加上尾随材料,然后重新组合为导联标记,x
&和39,以及尾部标记。
为了使其健壮,您可以识别该模式,然后将{
和}
中显示的命令分组以对它们进行分组,以便它们仅在识别出模式时执行:
sed '/^\(Hello\.\)\([^.]\{1,\}\)\(\.SecondString\)/{
s/^\(Hello\.\)\([^.]\{1,\}\)\(\.SecondString\)/\1@\3@@\2/
h
s/.*@@//
s/./x/g
G
s/\(x*\)\n\([^@]*\)@\([^@]*\)@@.*/\2\1\3/
}'
根据您的需求进行调整......
[我试过你的一个解决方案,它工作正常。] 但是,当我尝试更换“你好”时通过我真实的字符串(这是 '
1.2.840.
')和我的第二个字符串(只是一个点'.
'),停止了一切 工作。我想所有这些点都会混淆sed
命令。 我试图实现的是改变这个'1.2.840.10008.
'至 '1.2.840.xxxxx.
'这个模式在我的文件中多次出现,变量号 在'
1.2.840.
'之间要替换的字符和下一个点'.
'
有时候让你的问题足够接近真实场景很重要 - 这可能就是这样。 Dot是一个元字符
sed
正则表达式(以及正则表达式的大多数其他方言 - shell globbing是明显的例外)。如果'位被破坏'总是数字,然后我们可以收紧正则表达式,但实际上(当我查看前面的代码时),紧缩确实不会对限制措施造成太大影响。
使用正则表达式的任何解决方案都是一种平衡行为,必须提供方便性和缩写,以防止可靠性和精确性。
修改代码加数据
cat <<EOF |
transform this '1.2.840.10008.' to '1.2.840.xxxxx.'
OK, and hence 1.2.840.21. and 1.2.840.20992. should lose the 21 and 20992.
EOF
sed ':a;s/\(1\.2\.840\.x*\)[^x.]\([^.]*\.\)/\1x\2/;t a'
示例输出:
transform this '1.2.840.xxxxx.' to '1.2.840.xxxxx.'
OK, and hence 1.2.840.xx. and 1.2.840.xxxxx. should lose the 21 and 20992.
脚本中的更改为:
sed ':a;s/\(1\.2\.840\.x*\)[^x.]\([^.]*\.\)/\1x\2/;t a'
1\.2\.840\.
作为开始模式。x
或.
&#39;。\.
作为尾部模式。如果您确定只想匹配数字,则可以将[^x.]
替换为[0-9]
,在这种情况下,您不必担心空格,如下所述。< / p>
您可能决定不希望空格匹配,以便随意评论如下:
The net prefix is 1.2.840. And there are other prefixes too.
不会以:
结束The net prefix is 1.2.840.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.
在这种情况下,您可能需要使用:
sed ':a;s/\(1\.2\.840\.x*\)[^x. ]\([^ .]*\.\)/\1x\2/;t a'
所以这些变化会持续下去,直到你得到足够精确的东西来做你想做的事情,而不做任何你不想要的当前数据集。编写防弹正则表达式需要精确指定您想要匹配的内容,并且可能非常困难。
答案 1 :(得分:2)
我选择perl:
perl -pe 's/(?<=Hello\.)(.*?)(?=\.SecondString)/ "x" x length($1) /e' file
答案 2 :(得分:1)
此awk
应该:
awk -F. '{for (i=1;i<=length($2);i++) a=a"x";$2=a;a=""}1' OFS="." file
Hello.xxxxxxxxxxxxxxxxxx.SecondString
Hello.xxxxxxxxxxx.SecondString
答案 3 :(得分:1)
尽管perl
,sed
和awk
解决方案可能是更好的选择,但Bash解决方案并不困难(只是更长)。 Bash也具有良好的逐字符处理能力:
#!/bin/bash
rep=0 # replace flag
skip=0 # delay reset flag
while read -r line; do # read each line
for ((i=0; i<${#line}; i++)); do # for each character in the line
# if '.' and replace on, turn off and set skip
[ ${line:i:1} == '.' -a $rep -eq 1 ] && { rep=0; skip=1; }
# print char or "x" depending on replace flag
[ $rep -eq 0 ] && printf "%c" ${line:i:1} || printf "x"
# if '.' and replace off
if [ ${line:i:1} == '.' -a $rep -eq 0 ]; then
# if skip, turn skip off, else set replace on
[ $skip -eq 1 ] && skip=0 || rep=1
fi
done
printf "\n"
done
exit 0
<强>输入强>
$ cat dat/replacefile.txt
Hello.StringToBeReplaced.SecondString
Hello.ShortString.SecondString
<强>输出强>
$ bash replacedot.sh < dat/replacefile.txt
Hello.xxxxxxxxxxxxxxxxxx.SecondString
Hello.xxxxxxxxxxx.SecondString
答案 4 :(得分:1)
为了您的理智,请使用awk:
$ awk 'BEGIN{FS=OFS="."} {gsub(/./,"x",$2)} 1' file
Hello.xxxxxxxxxxxxxxxxxx.SecondString
Hello.xxxxxxxxxxx.SecondString