用于交换文本文件 (XML) 中包含换行符的字符串的脚本

用于交换文本文件 (XML) 中包含换行符的字符串的脚本

我正在使用几周前更新的 Arch。使用的软件包是

  • Arch 6.0.10-arch2-1
  • BASH 5.1.16(1)-发布
  • gnu sed 4.9
  • gnu grep 3.8
  • 开箱3.6.1

在我的 Openboxrc.xml文本文件中,有两行如下

<!-- <keybind key="Right"> -->

<keybind key="s-Up">

我希望使用脚本经常切换/交换上面两行和下面两行

<!-- <keybind key="s-Up"> -->

<keybind key="Right">

rc.xml这使我能够快速更改键绑定,而无需每次都繁琐地编辑键绑定设置文本文件 ( )。

到目前为止,我的脚本如下,但无法正常工作,尽管我知道它已经接近了。

我不太关心如何实现这种切换,但已经在这方面花费了大量时间,让下面的脚本正常工作会很好。

下面的表达式sed按其应有的方式进行文本交换工作。

如果满足条件,该if语句似乎会进行第一次交换,但如果不满足条件,sed则不会进行第二次交换,这就是问题所在。sed

var_a=""

#var_a=$(grep -zoP  "<\!\-\- <keybind key=\"Right\"> \-\->\n\n<keybind key=\"s-Up\">" /home/kes/Dropbox/lubuntu-rc.xml | sed ':a;N;$!ba;s|\n\n||g')

var_a=$(grep -zoP  "<\!\-\- <keybind key=\"Right\"> \-\->\n\n<keybind key=\"s-Up\">" /home/kes/Dropbox/lubuntu-rc.xml | tr -d '\n' )

# result of grep is
# <!-- <keybind key="Right"> --><keybind key="s-Up">

echo $var_a; sleep 0.5

#if [[ -z ! "$var_a" ];then
if [[ '$var_a'=='<!-- <keybind key="Right"> --><keybind key="s-Up">' ]]; then

   sed -ie ':a;N;$!ba;s|<!-- <keybind key="Right"> -->\n\n<keybind key="s-Up">|<!-- <keybind key="s-Up"> -->\n\n<keybind key="Right">|g' /home/kes/Dropbox/lubuntu-rc.xml

else

   sed -ie ':a;N;$!ba;s|<!-- <keybind key="s-Up"> -->\n\n<keybind key="Right">|<!-- <keybind key="Right"> -->\n\n<keybind key="s-Up">|g' /home/kes/Dropbox/lubuntu-rc.xml

fi

答案1

实际上,这个条件永远不会被满足,因为单引号'$var_a'会阻止你的变量被扩展,所以文字字符串$var_a永远不会等于其他文字字符串。

使用双引号,它应该可以工作,我想对您的脚本添加一些评论:

(1)您首先执行 a grep,然后根据从中获得的内容建立条件grep,最后使用两个不同的sed脚本。这可能会导致错误。事实上,你sed会独自完成整件事:

sed -ie ':a;N;$!ba
  s|<!-- <keybind key="Right"> -->\n\n<keybind key="s-Up">|<!-- <keybind key="s-Up"> -->\n\n<keybind key="Right">|g
  t
  s|<!-- <keybind key="s-Up"> -->\n\n<keybind key="Right">|<!-- <keybind key="Right"> -->\n\n<keybind key="s-Up">|g' \
  /home/kes/Dropbox/lubuntu-rc.xml

如果进行了第一次替换,则t跳转到脚本末尾,否则执行另一次替换。和grep已经if包含在内!

(2)收集所有行的方法:a;N;$!ba仅适用于 GNU sed,因为根据 POSIX 标准,冒号后面的所有内容都将是跳转标记, 也不例外;。对你来说没问题,因为你有 GNU sed,但你也可以-z选择在一次运行中处理孔文件而不需要零件。如果您喜欢便携,H;1h;$!d;x可以通过存放空间做同样的事情。

(3)我认为,g全局标志是多余的,因为这些行只存在一次,对吗?

(4)一遍又一遍地编写相同的内容可能会导致错误并且更难以阅读。为什么不通过标记\(\)并将其引用为 来重复使用一个部件\1

sed -iz 's|<!-- \(<keybind key="\)Right"> -->\n\n\1s-Up">|<!-- \1s-Up"> -->\n\n\1Right">|
  t
  s|<!-- \(<keybind key="\)s-Up"> -->\n\n\1Right">|<!-- \1Right"> -->\n\n\1s-Up">|' \
  /home/kes/Dropbox/lubuntu-rc.xml

(5)实际上,您不需要将整个文件作为一个处理(这甚至可能导致大文件溢出),您只需要使用该N;P;D方案将每连续三行一起处理(通常是两行在一起,添加1N以将其扩展为三行):

sed -i '1N;N
  s|<!-- \(<keybind key="\)Right"> -->\n\n\1s-Up">|<!-- \1s-Up"> -->\n\n\1Right">|
  t
  s|<!-- \(<keybind key="\)s-Up"> -->\n\n\1Right">|<!-- \1Right"> -->\n\n\1s-Up">|;
  P;D' /home/kes/Dropbox/lubuntu-rc.xml

人们可能可以进一步简化这一点,但这需要有关文件内容的附加信息。

答案2

假设 XML 文档格式良好且语法正确,您可以使用 XML 处理器xmlstarlet来切换值。

忽略文档中注释掉的 XML,以下内容通过首先将key每个节点的所有属性keybind从值更改Righttemporary(具有其他值的属性不会受到影响)来实现此目的。然后,它将相同的属性从s-Upto更改为Right,最后从temporaryto更改s-Up。该字符串temporary是任意字符串,否则不会用于节点key的属性keybind

Right这有效地交换了到 的值s-Up,反之亦然。

xmlstarlet edit \
    --var attr '//keybind/@key' \
    --update '$attr[.="Right"]'     --value temporary \
    --update '$attr[.="s-Up"]'      --value Right \
    --update '$attr[.="temporary"]' --value s-Up \
   file.xml

这会将文档变成这样

<?xml version="1.0"?>
<root>
  <keybind key="s-Up"/>
  <keybind key="s-Down"/>
  <keybind key="Right"/>
</root>

... 进入

<?xml version="1.0"?>
<root>
  <keybind key="Right"/>
  <keybind key="s-Down"/>
  <keybind key="s-Up"/>
</root>

如果您--inplace直接在命令行上的单词后面插入edit,则编辑将就地进行,修改原始文件。

相关内容