![用于交换文本文件 (XML) 中包含换行符的字符串的脚本](https://linux22.com/image/214201/%E7%94%A8%E4%BA%8E%E4%BA%A4%E6%8D%A2%E6%96%87%E6%9C%AC%E6%96%87%E4%BB%B6%20(XML)%20%E4%B8%AD%E5%8C%85%E5%90%AB%E6%8D%A2%E8%A1%8C%E7%AC%A6%E7%9A%84%E5%AD%97%E7%AC%A6%E4%B8%B2%E7%9A%84%E8%84%9A%E6%9C%AC.png)
我正在使用几周前更新的 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
从值更改Right
为temporary
(具有其他值的属性不会受到影响)来实现此目的。然后,它将相同的属性从s-Up
to更改为Right
,最后从temporary
to更改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
,则编辑将就地进行,修改原始文件。