问题是我无法将完整的命令与 SED 放在同一行,我已经完成了它,但对于这些文件它不起作用:我的例子:
<file>Documents/time/text1</file> //2X slash + 2 words to remove !!
<file>Commun/text2</file> //1X slash to remove + 1 words to remove
<file>Current/text3</file> //1X slash to remove + 1 words to remove
为什么这段代码不能在线工作?
sed 's/Documents//g' | sed 's/time//g' | sed 's/Commun//g' | sed 's/Current//g' | sed 's/Current//g' | sed '/<file>/s|<file>/|<file>|' | sed '/<file>/s|<file>/|<file>|' tracklist.txt > newtracklist.txt
答案1
运行 OP 的当前脚本管道sed
会导致输入文件 ( tracklist.txt
) 的内容被打印到 stdout,然后管道挂起(即,没有其他输出,没有返回到命令提示符)。我是猜测这就是OP在陈述时所指的内容it does not work
... ??
主要问题:输入文件 ( tracklist.txt
) 需要作为第一个sed
脚本的参数提供,而不是作为最后一个sed
脚本的参数。
推荐:
# instead of this:
sed 's/Documents//g' | ... | sed '/<file>/s|<file>/|<file>|' tracklist.txt
^^^^^^^^^^^^^
# do this:
sed 's/Documents//g' tracklist.txt | ... | sed '/<file>/s|<file>/|<file>|'
^^^^^^^^^^^^^
运行 OPsed
管道的更新版本会生成:
<file>text1</file>
<file>text2</file>
<file>text3</file>
虽然有更好的工具用于解析 HTML/XML,但如果 OP 必须使用,sed
那么有多种方法可以以更有效的方式生成相同的结果。
一个想法需要一个sed
脚本:
sed -E 's|(<file>).*/([^/]+</file>)|\1\2|' tracklist.txt
在哪里:
-E
- 启用对扩展正则表达式的支持(<file>)
- (第一个捕获组)匹配字符串<file>
([^/]+</file>)
-(第二个捕获组)匹配所有后面不是/
字符串的字符</file>
.*/
- 两个捕获组之间的所有内容都以 a 结尾/
\1\2
- 替换字符串由附加在一起的两个捕获组组成- 笔记:这适用于 OP 提供的特定输入;如果输入的格式与 OP 示例输入中显示的格式不同,则可能需要调整
对于OP的样本输入,这会生成:
<file>text1</file>
<file>text2</file>
<file>text3</file>
答案2
给定您的输入 XML 文件
添加r
根节点:
<r>
<file>Documents/time/text1</file>
<file>Commun/text2</file>
<file>Current/text3</file>
</r>
代码:
xidel --xquery '
<r>{
for $x in //file
return <file>{tokenize($x, "/")[last()]}</file>
}</r>
' --output-format=xml --output-node-indent file.xml
产量:
<?xml version="1.0" encoding="UTF-8"?>
<r>
<file>text1</file>
<file>text2</file>
<file>text3</file>
</r>
说明:
在这里,我没有使用错误的工具:sed
,而是使用XPath
and XQuery
(前者是后者的子集)正确的XML
解析器。
xidel
是操作 HTML/XML 的瑞士军刀。
用法:
xidel ... file.xml > new_file.xml
如果你想编辑在飞行中:
xidel ... file.xml | sponge file.xml
sponge
从GNU more-utils
。
答案3
使用乐(以前称为 Perl_6)
~$ raku -MXML -e 'my $xml = open-xml( $*ARGFILES.Str );
for $xml.elements( :RECURSE(0), :TAG{"file"} ) -> $E {
my $old = $E.contents[0];
my $new = XML::Text.new( text => $old.text.match(/ <?after "/"> <-[/]>+ $/) );
$E.replace( $old, $new );
}; .say for $xml;' file.xml
或者:
% raku -MXML -e 'my $xml = open-xml( $*ARGFILES.Str );
for $xml.elements( :RECURSE(0), :TAG{"file"} ) -> $E {
my $old = $E.contents[0];
my $new = XML::Text.new( text => $old.text.path.basename );
$E.replace( $old, $new );
}; .say for $xml;' file.xml
Raku 是 Perl 家族中的一种编程语言,具有高级功能语法用于解析文本。除了 Raku/Rakudo 本身之外,社区成员还支持 Raku/Rakudo 生态系统中的模块。这些模块之一是(Raku-native)XML
模块。
与OP的其他问题类似,在带有XML
-module的Raku中,您可以(例如)将替换限制为1)。顶层和 2)。仅在<file>
TAG 内。这是通过将代码设置为在elements
限制条件下进行迭代来完成的:RECURSE(0), :TAG{"file"}
。仅供参考,TAG
如果需要,您可以迭代所有深度的所有 s:只需设置:RECURSE(Inf)
和删除:TAG
命名参数,这会将:TAG
限制设置为 False。
上面的第一个答案确定了适合替换的标签/级别。这样就确定了,每个元素的内部(即非TAG)contents[0]
都被赋值给变量$old
,它实际上是一个XML::Text
对象。$old
将对象提取.text
到字符串中,然后match
找到所需的对象。使用现已更正的键/值对XML::Text.new
创建 ( )新的 ( ) 对象。从这里开始,-module 的例程完成了工作:。$new
text => 'value'
XML
replace
replace( $old, $new )
上面的第二个答案是第一个答案的巧妙转折。因为 OP 想要编辑路径名,所以IO::Path
可以使用与 Raku 对象类相关的例程。 Raku 的.IO
例程将文本理解为有效的路径名,并且 Raku 的.basename
例程返回最终的文件名。这种方法有可能提高代码的可移植性,因为 Raku 具有在不同平台上使用正确 (/
或\
) 路径分隔符的机制。
输入示例(感谢@GillesQuénot!):
<r>
<file>Documents/time/text1</file>
<file>Commun/text2</file>
<file>Current/text3</file>
</r>
示例输出:
<?xml version="1.0"?><r>
<file>text1</file>
<file>text2</file>
<file>text3</file>
</r>
https://github.com/raku-community-modules/XML
https://docs.raku.org/type/IO/Path
https://rakudo.org/
https://raku.org