【问题标题】:change string in file between two strings with character X [duplicate]在带有字符X的两个字符串之间更改文件中的字符串[重复]
【发布时间】:2016-08-19 16:36:42
【问题描述】:

我想用相同数量的 X 替换标签之间的值。例如

1.

<Name> Jason </Name>
to
<Name> XXXXX </Name>

2。 (看不到空格)

 <Name>Jim</Name>
 to
 <Name>XXX</Name>

3.

<Name Jason /> 
to 
<Name XXXXX />`

4.

<Name Jas />
to
<Name XXX />

开始标签、值和结束标签都可以在不同的行

5.

<Name>Jim
</Name>
to
<Name>XXX
</Name>

6.

<Name>
     Jim
       </Name>
to
<Name>
     XXX
       </Name>

7.

  <Name
     Jim
       />
to
  <Name
     XXX
       />

8.

<Name> Jason </Name> <Name> Ignacio </Name>
to
<Name> XXXXX </Name> <Name> XXXXXX </Name>

9.

<Name> Jason Ignacio </Name>
to
<Name> XXXXX XXXXXXX </Name>
or
<Name> XXXXXXXXXXXXX </Name>

两个都很好

我试过了,但是没用

file=mylog.log
search_str="<Name>"
end_str="</Name>"
sed -i -E ':a; s/('"$search_str"'X*)[^X'"$end_str"']/\1X/; ta' "$file"

请告诉我如何在 bash 脚本中执行此操作....

更新:

我也试过这个,但对于 6 和 7 案例都不起作用。案例 1 到 5 有效。

sed -i -E '/<Name>/{:a; /<\/Name>/bb; n; ba; :b; s/(<Name>X*)[^X\<]/\1X/; tb; }' "$file"
sed -i -E '/<Name[[:space:]]/{:a; /\/>/bb; n; ba; :b; s/(<Name[[:space:]]X*)[^X\/]/\1X/; tb; }' "$file"

【问题讨论】:

  • 建议使用xml 感知工具(或)能够解析&lt;node&gt; 元素的工具。 sedawk 不是最好的方法
  • 是可以做到的,但是每个案例都会很痛苦。我不愿意承受产生答案所涉及的痛苦。案例 3 和 4 看起来是相同的。案例 1 和 2 可以使用正则表达式按照 s/\(&lt;Name&gt;[[:space:]]*X*\)\([^X[:space:]]\)\([[:space:]]*&lt;\/Name&gt;\)/\1X\2/ 和一些 sedhackery 进行迭代,直到没有替换(标签和测试/分支操作)。请注意,显示的机制不会处理&lt;Name&gt; Jason Bourne &lt;/Name&gt; — 您需要进行一些更改(很容易,因为它发生了)。玩得开心。
  • 顺便说一句,如果你可以有&lt;Name&gt; Jason Bourne &lt;/Name&gt;,输出应该是&lt;Name&gt; XXXXX XXXXXX &lt;/Name&gt;还是&lt;Name&gt; XXXXXXXXXXXX &lt;/Name&gt;
  • @fedorqui:我猜这些名字一般不只是 Jason、Jim、Jas。并且单个 XML 块可能有无数不同的名称——例如,我正在设想一个带有掩码名称的客户表的 XML 输出。所以我认为任何使用 shell 来查找值并进行替换的东西都会很痛苦——但无论如何,整个练习都会很痛苦。您确实需要一个 XML 解析器(Perl、Python 等)并以这种方式处理它。
  • @Inian 问题是,&lt;Name Jim /&gt; 不是有效的 XML。

标签: bash shell sed


【解决方案1】:

临时解决方案

这扩展了下面的“初始提供”并处理案例 1、2、5、6、8、9。它不处理有一个或多个完整的 &lt;Name&gt;…&lt;/Name&gt; 条目以及起始 &lt;Name&gt; 的情况在同一行没有匹配的&lt;/Name&gt;。坦率地说,我什至不知道如何开始处理这种情况。

未处理的案例 3、4、7 不是有效的 XML — 我也不相信它们是有效的 HTML(或 XHTML)。我相信它们可以通过与此处显示的完整&lt;Name&gt;…&lt;/Name&gt; 版本类似(但更简单)的机制来处理。我将把它作为练习留给读者(注意字符类中的&lt;——它需要变成/)。

script.sed

/<Name>/! b
/<Name>.*<\/Name>/{
: l1
s/\(<Name>[[:space:]]*\(X[X[[:space:]]*\)\{0,1\}\)[^X<[:space:]]\(.*[[:space:]]*<\/Name>\)/\1X\3/
t l1
b
}
/<Name>/,/<\/Name>/{
  # Handle up to 4 lines to the end-name tag
  /<\/Name>/! N
  /<\/Name>/! N
  /<\/Name>/! N
  /<\/Name>/! N
# s/^/ZZ/; s/$/AA/p
# s/^ZZ//; s/AA$//
  : l2
  s/\(<Name>[[:space:]]*\(X[X[[:space:]]*\)\{0,1\}\)[^X<[:space:]]\(.*[[:space:]]*<\/Name>\)/\1X\3/
  t l2
}

第一行“跳过”不包含&lt;Name&gt; 的行的处理(它们被打印并读取下一行)。接下来的 6 行是“初始报价”中的脚本,除了有一个 b 可以跳转到处理结束。

新部分是/&lt;Name&gt;/,/&lt;\/Name&gt;/ 代码。这会自行查找&lt;Name&gt;,并连接最多4 行,直到&lt;/Name&gt; 包含在模式空间中。两个注释行用于调试——它们让我看到什么被视为一个单元。除了使用标签 l2 代替 l1 之外,其余部分与初始产品中的完全相同 - sed 正则表达式已经适应换行符。

这是重型sed 脚本,而不是我想要使用或维护的。我会选择使用 XML 解析器的 Perl 解决方案(因为我比 Python 更了解 Perl),但 Python 也可以使用适当的 XML 解析器来完成这项工作。

data

稍微扩展的数据文件。

<Name> Jason </Name>
<Name>Jim</Name>
<Name> Jason Bourne </Name>
<Name> Elijah </Name> <Name> Dennis </Name>
<Name> Elijah Wood </Name> <Name> Dennis The Menace </Name>
<Name>Elijah Wood</Name> <Name>Dennis The Menace</Name>
<Name> Jason
        </Name>
<Name>
    Jim</Name>
<Name>
    Jim
        </Name>
<Name> Jason
Bourne </Name>
<Name> 
    Jason
        Bourne
            </Name>
<Name> Elijah </Name>
<Name>
Dennis
</Name>
<Name> Elijah
Wood </Name>
            <Name> Dennis
The Menace </Name>
<Name>Elijah
Wood</Name>
    <Name>Dennis The
Menace</Name>



<Name> Jason </Name>
to
<Name> XXXXX </Name>

2. (see no space)

 <Name>Jim</Name>
 to
 <Name>XXX</Name>

3.

<!--Name Jason /--> 
to 
<!--Name XXXXX /-->`

4.

<!--Name Jas /-->
to
<!--Name XXX /-->

starting tag, value and closing tag can all come in different line

5.

<Name>Jim
</Name>
to
<Name>XXX
</Name>

6.

<Name>
     Jim
       </Name>
to
<Name>
     XXX
       </Name>

7.

  <!--Name
     Jim
       /-->
to
  <!--Name
     XXX
       /-->

8.

<Name> Jason </Name> <Name> Ignacio </Name>
to
<Name> XXXXX </Name> <Name> XXXXXX </Name>

9.

<Name> Jason Ignacio </Name>
to
<Name> XXXXX XXXXXXX </Name>
or
<Name> XXXXXXXXXXXXX </Name>

没有声称data 文件包含最小的案例集;它是重复的。它包括来自问题的材料,除了像 &lt;Name Value /&gt; 这样的“非正统” XML 元素被转换为 XML cmets &lt;!--Name Value /--&gt;。映射实际上并不重要。开头部分与&lt;Name&gt; 不匹配(并且尾部与&lt;/Name&gt; 不匹配),因此它们无论如何都不会被处理。

输出

$ sed -f script.sed data
<Name> XXXXX </Name>
<Name>XXX</Name>
<Name> XXXXX XXXXXX </Name>
<Name> XXXXXX </Name> <Name> XXXXXX </Name>
<Name> XXXXXX XXXX </Name> <Name> XXXXXX XXX XXXXXX </Name>
<Name>XXXXXX XXXX</Name> <Name>XXXXXX XXX XXXXXX</Name>
<Name> XXXXX
        </Name>
<Name>
    XXX</Name>
<Name>
    XXX
        </Name>
<Name> XXXXX
XXXXXX </Name>
<Name> 
    XXXXX
        XXXXXX
            </Name>
<Name> XXXXXX </Name>
<Name>
XXXXXX
</Name>
<Name> XXXXXX
XXXX </Name>
            <Name> XXXXXX
XXX XXXXXX </Name>
<Name>XXXXXX
XXXX</Name>
    <Name>XXXXXX XXX
XXXXXX</Name>



<Name> XXXXX </Name>
to
<Name> XXXXX </Name>

2. (see no space)

 <Name>XXX</Name>
 to
 <Name>XXX</Name>

3.

<!--Name Jason /--> 
to 
<!--Name XXXXX /-->`

4.

<!--Name Jas /-->
to
<!--Name XXX /-->

starting tag, value and closing tag can all come in different line

5.

<Name>XXX
</Name>
to
<Name>XXX
</Name>

6.

<Name>
     XXX
       </Name>
to
<Name>
     XXX
       </Name>

7.

  <!--Name
     Jim
       /-->
to
  <!--Name
     XXX
       /-->

8.

<Name> XXXXX </Name> <Name> XXXXXXX </Name>
to
<Name> XXXXX </Name> <Name> XXXXXX </Name>

9.

<Name> XXXXX XXXXXXX </Name>
to
<Name> XXXXX XXXXXXX </Name>
or
<Name> XXXXXXXXXXXXX </Name>
$

首发

部分答案 - 但它说明了您面临的问题。处理问题中的案例 1 和 2,加上多词变化,您可以使用脚本:

script.sed

/<Name>.*<\/Name>/{
: l1
s/\(<Name>[[:space:]]*\(X[X[[:space:]]*\)\{0,1\}\)[^X<[:space:]]\(.*[[:space:]]*<\/Name>\)/\1X\3/
t l1
}

这是相当扭曲的,这是礼貌的。它查找&lt;Name&gt;,后跟零个或多个空格。后面可以跟\(X[X[[:space:]]*\)\{0,1\},这意味着X 出现0 次或1 次,后跟一系列X 或空格。所有这些都在替换中被捕获为\1。然后有一个不是X&lt; 或空格的字符,后跟零个或多个任意字符、零个或多个空格以及&lt;/Name&gt;。中间的单个字符被 X 替换。整个替换重复,直到通过标签 : l1 和条件分支 t l1 不再匹配。所有这些都只在&lt;Name&gt;&lt;/Name&gt; 的行上运行。

data

<Name> Jason </Name>
<Name>Jim</Name>
<Name> Jason Bourne </Name>
<Name> Elijah </Name> <Name> Dennis </Name>
<Name> Elijah Wood </Name> <Name> Dennis The Menace </Name>
<Name>Elijah Wood</Name> <Name>Dennis The Menace</Name>
<Name> Jason
</Name>
<Name>
Jim</Name>
<Name> Jason
Bourne </Name>
<Name> Elijah </Name> <Name> Dennis
</Name>
<Name> Elijah
Wood </Name> <Name> Dennis
The Menace </Name>
<Name>Elijah
Wood</Name> <Name>Dennis The
Menace</Name>

输出

$ sed -f script.sed data
<Name> XXXXX </Name>
<Name>XXX</Name>
<Name> XXXXX XXXXXX </Name>
<Name> XXXXXX </Name> <Name> XXXXXX </Name>
<Name> XXXXXX XXXX </Name> <Name> XXXXXX XXX XXXXXX </Name>
<Name>XXXXXX XXXX</Name> <Name>XXXXXX XXX XXXXXX</Name>
<Name> Jason
</Name>
<Name>
Jim</Name>
<Name> Jason
Bourne </Name>
<Name> XXXXXX </Name> <Name> Dennis
</Name>
<Name> Elijah
Wood </Name> <Name> Dennis
The Menace </Name>
<Name>Elijah
Wood</Name> <Name>Dennis The
Menace</Name>
$

请注意最后的替换部分。那条线会让人头疼。

我还没有弄清楚脚本将如何处理各种分割线的情况,除了它几乎肯定需要加入线,直到&lt;/Name&gt; 被捕获。然后它会进行与已经显示的内容密切相关的处理,但需要在匹配的材料中允许换行符。

【讨论】:

  • 谢谢...我会试试你的解决方案,但我真的需要多线解决方案,特别是案例 6。
【解决方案2】:

试试这个 python 脚本:

$ cat script.py
#!/usr/bin/python
import re
from bs4 import BeautifulSoup
soup = BeautifulSoup(open('allcases'), features="xml")
for tag in soup.findAll('Name'):
    for name in 'Jason Ignacio', 'Jason', 'Jim':
        tag.string =  re.sub(r'\b%s\b' % name, len(name)*'X', tag.string)
print(str(soup))

此代码与 python2 或 python3 兼容。

要使其正常工作,您可能需要安装 BeautifulSoup 模块。在类似 debian 的系统上:

apt-get install python-bs4

或者,对于python3:

apt-get install python3-bs4

示例

让我们考虑这个输入文件:

$ cat cases
<page>
<p>Jason</p>
<Name> Jason </Name>
<p>Jason</p>
 <Name>Jim</Name>
<p>Jim</p>
<Name>Jim
</Name>
<Name>
     Jim
       </Name>
<Name> Jason </Name> <Name> Ignacio </Name>
<Name> Jason Ignacio </Name>
</page>

让我们运行我们的脚本并观察输出:

$ python script.py
<?xml version="1.0" encoding="utf-8"?>
<page>
<p>Jason</p>
<Name> XXXXX </Name>
<p>Jason</p>
<Name>XXX</Name>
<p>Jim</p>
<Name>XXX
</Name>
<Name>
     XXX
       </Name>
<Name> XXXXX </Name> <Name> Ignacio </Name>
<Name> XXXXXXXXXXXXX </Name>
</page>

请注意,&lt;p&gt; 标签中的名称是单独的。该代码仅更改&lt;Name&gt; 标签中的名称。

此外,根据设计,JimJasonJason Ignacio 更改为 X,但其他名称保持不变。即使是伊格纳西奥,如果它出现时没有相邻的杰森,也会被单独留下。

【讨论】:

  • 谢谢@John1024。这正是我想要的结果.. 但是在我们的服务器上,没有 python.. 只有 shell/bash.. 你介意给我一个等效的 sed 命令吗……或者可能告诉我我的 sed 命令有什么问题?
  • 嘿 john @John1024 .. 你知道如何使用 sed 命令修复 case 6/7 吗?
  • @PuneetJain 您能否澄清一点:只想更改特定名称?或者,您要更改名称标签中出现的所有字母字符吗?
  • John @John1024 Name 标签内的任何内容。名称只是此处显示的 1 个单词。实际上我将使用一个变量,其值将来自名称数组,例如:(John,Jim,Carry,Marry,SSN,Dude)等。目前我正在使用 2 个 sed 命令: sed -i -E ':a; s/('"$search_str1"'X*)[^X\
  • 你好!!你在那? @John1024
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-06-02
  • 2017-07-07
  • 2020-05-28
  • 1970-01-01
  • 2018-07-28
  • 1970-01-01
  • 2011-03-23
相关资源
最近更新 更多