【问题标题】:Substitute all characters between two strings by char 'X' using sed使用 sed 将两个字符串之间的所有字符替换为 char 'X'
【发布时间】:2017-06-16 02:08:35
【问题描述】:

在 Bash 脚本中,我试图在文件中将两个给定字符串之间的字符替换为“X”。我有一堆字符串对,我希望在它们之间用“X”替换字符。
在下面的代码中,对中的 first 字符串在 cpi_list 数组中声明。对中的 second 字符串始终是 %26&ENDOFLINE

这就是我正在做的。

# list of "first" or "start" string
declare -a cpi_list=('%26Name%3d' '%26Pwd%3d')  

# This is the "end" string
myAnd=\%26
newfile="inputlog.txt"

for item in "${cpi_list[@]}";
do
    sed -i -e :a -e "s/\($item[X]*\)[^X]\(.*"$myAnd"\)/\1X\2/;ta" $newfile;
done

输入

CPI.%26Name%3dJASON%26Pwd%3dBOTTLE%26Name%3dCOTT
CPI.%26Name%3dVoorhees&machete

我想成功

CPI.%26Name%3dXXXXX%26Pwd%3dXXXXXX%26Name%3dXXXX
CPI.%26Name%3dXXXXXXXX&machete

PS:最后一项也需要将%26Name%3dCOTT 更改为%26Name%3dXXXX,即使没有结束%26 因为我正在寻找%26 作为终点或END OF THE LINE

但不知何故,它不起作用。

【问题讨论】:

标签: string bash shell awk sed


【解决方案1】:

这将适用于任何 UNIX 安装中从任何 shell 调用的任何 awk:

$ cat tst.awk
BEGIN {
    begs = "%26Name%3d|%26Pwd%3d"
    ends = "%26|&"
}
{
    head = ""
    tail = $0
    while( match(tail, begs) ) {
        tgtStart = RSTART + RLENGTH
        tgt = substr(tail,tgtStart)
        if ( match(tgt, ends) ) {
            tgt = substr(tgt,1,RSTART-1)
        }

        gsub(/./,"X",tgt)
        head = head substr(tail,1,tgtStart-1) tgt
        tail = substr(tail,tgtStart+length(tgt))
    }
    $0 = head tail

    print
}

$ cat file
CPI.%26Name%3dJASON%26Pwd%3dBOTTLE%26Name%3dCOTT
CPI.%26Name%3dVoorhees&machete

$ awk -f tst.awk file
CPI.%26Name%3dXXXXX%26Pwd%3dXXXXXX%26Name%3dXXXX
CPI.%26Name%3dXXXXXXXX&machete

就像使用 sed 替换一样,beg 和 end 字符串中的任何正则表达式元字符都需要转义,或者我们必须使用带有 index()s 而不是 match() 的循环,所以我们会进行字符串匹配而不是正则表达式匹配。

【讨论】:

  • 我将关闭这篇文章并将您的答案标记为最终答案。但是我想到的一件事是,这可以在文件中完成吗?而不是“打印”并将输出写回原始文件?只是问问。
  • 使用 GNU awk,您可以添加 -i infile 参数,但是当您可以对包括 awk 'script' file > tmp && mv tmp file 在内的任何命令执行 cmd file > tmp && mv tmp file 时,为什么还要麻烦。虽然没有“代替打印” - 无论哪种方式,您仍然需要打印。
【解决方案2】:

您可以避免 %26 这样做:

a='CPI.%26Name%3dJASON%26Pwd%3dBOTTLE%26Name%3dCOTT'
echo "$a" |sed -E ':a;s/(%3dX*)([^%X]|%[013-9a-f][0-9a-f]|%2[0-5789a-f])/\1X/g;ta;'

请注意,每个编码字符 %xx 计为一个 X。

【讨论】:

  • 仅限 GNU sed。 POSIX 需要改变。
  • @dawg:我想是这样的:sed -e 's/%26/\&/g;' -e :a -e 's/\(%3d[^&]*\)[^&X]/\1X/g;ta;s/&/%26/g;'(我只使用 GNU sed)
  • 这行不通,因为模式空间 a 在本地范围内限定为每个 -e
  • 这行不通,因为我有 inputlog.txt 文件,每行可以在不同的地方有很多 '&'... 所以我不能用 & 替换 %26,反之亦然。
  • @PuneetJain:选择其他角色。
【解决方案3】:

它不是漂亮,但你可以使用perl:

$ s1="CPI.%26Name%3dJASON%26Pwd%3dBOTTLE%26Name%3dCOTT"
$ echo "$s1" | perl -lne 'if (/(?:^.*%26Name%3d)(.*)(?:%26Pwd%3d)(?:.*%26Name%3d)(.*)((?:%26Pwd%3d)|(?:$))/) { 
        $i1=$-[1];
        $l1=$+[1]-$-[1];
        $i2=$-[2];
        $l2=$+[2]-$-[2];
        substr($_, $i1, $l1, "X"x$l1);
        substr($_, $i2, $l2, "X"x$l2);
        print;
        }'
CPI.%26Name%3dXXXXX%26Pwd%3dBOTTLE%26Name%3dXXXX

如示例中的两对。一行中的 N 对将稍作修改。

【讨论】:

  • 不幸的是,我必须在 bash 脚本中执行此操作。不是 perl。
  • 你为什么“必须”在bash这样做?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-07-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-11-15
相关资源
最近更新 更多