【问题标题】:Get a list of delimited filenames from a text file从文本文件中获取分隔文件名列表
【发布时间】:2026-01-28 11:50:01
【问题描述】:

我对 Bash 真的很陌生,所以这对你们大多数人来说可能听起来很傻。 我正在尝试从文本文件中获取一些文件名的列表。尝试使用 sed 和 awk 执行此操作,但以我有限的知识无法使其工作。

这是一个示例文件内容:

<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 13.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 14948)  -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
 width="471.677px" height="126.604px" viewBox="0 0 471.677 126.604" enable-background="new 0 0 471.677 126.604"
 xml:space="preserve">
<rect x="0.01" y="1.27" fill="none" width="471.667" height="125.333"/>
<text transform="matrix(1 0 0 1 0.0098 8.3701)"><tspan x="0" y="0" font-family="'MyriadPro-Regular'" font-size="10">/Volumes/Secondary500/Temp/Untitled-2_Layer 1 copy 2.pdf</tspan><tspan x="0" y="12" font-family="'MyriadPro-Regular'" font-size="10">/Volumes/Secondary500/Temp/Untitled-2_Layer 1 copy.pdf</tspan><tspan x="0" y="24" font-family="'MyriadPro-Regular'" font-size="10">/Volumes/Secondary500/Temp/Untitled-2_Layer 1.pdf</tspan></text>
</svg>

我想从这个示例中得到一个包含这个确切内容的新文本文件:

/Volumes/Secondary500/Temp/Untitled-2_Layer 1 copy 2.pdf /Volumes/Secondary500/Temp/Untitled-2_Layer 1 copy.pdf /Volumes/Secondary500/Temp/Untitled-2_Layer 1.pdf

我想告诉 sed 打印 'font-size"10"&gt;' 和 '&lt;/tspan&gt;' 之间的所有匹配条目,但是......我得到的最好的是一个文件,其中整行包含我的字段分隔符。

如果你能解释完成的每一步,那就太好了。

  • 文件名可能或多或少。这3个只是一个例子。

【问题讨论】:

  • 在 Mac 上运行它吗?你有安装 fink 或 MacPorts 吗?如果是这样,命令提示符下的“which xsltproc”会告诉你什么?
  • 对于从 XML 文件中进行选择,解析它会是一个更好的选择,特别是如果文件名周围的标签可能在文件之间具有不同的属性。您是否可以使用 python 脚本之类的东西,还是必须是 bash/awk/sed?

标签: bash shell sed awk


【解决方案1】:

这个怎么样:

cat file.xml | sed -e's/^[^>]*>//' -e's/<.*$//' | grep \\.

它不是很通用,但要完全通用会复杂得多(XML 需要完整的解析器等)。

基本上,sed 脚本有两个部分。首先,去掉从行首 (^) 到第一个“>”字符的所有字符。请注意,为了做到这一点,我匹配所有非“>”。第二部分去除从最左边的“

然后,grep 语句只返回带有“.”的行。在其中,仅剩下文件名的行。

希望有帮助!

【讨论】:

  • sed -e's/^[^>]*>//' -e's/<. file.xml grep>
【解决方案2】:

用于此的sed 命令将是

 sed  -n 's|font-size="[0-9]*".\(.*\)</tspan.*|\1|p' file.xml
            -------------------  --  ---------
               prefix part       \1   suffix

这就是它的工作原理,

  • -n 禁止打印缓冲区中的所有行
  • 末尾的p 表示要打印替换的缓冲区
  • '|' 用作分隔符而不是通常的 '/' 有助于轻松过滤路径分隔符
  • 搜索字符串匹配font-size="[0-9]*".和`之间的所有内容
  • \(\) 之间的部分是我们感兴趣的部分
    • \1 表示我们希望将其保留在缓冲区中以供打印

此命令使用group operator which is described here

在你的文件上,

/Volumes/Secondary500/Temp/Untitled-2_Layer 1 copy 2.pdf
/Volumes/Secondary500/Temp/Untitled-2_Layer 1 copy.pdf
/Volumes/Secondary500/Temp/Untitled-2_Layer 1.pdf

请注意,获取正确的前缀和后缀字符串以获取所有匹配项非常重要。在您的示例中,这些是我在上面找到的 font-sizetspan 部分。但是,文件中的所有文件字符串可能并非如此。所以检查一下。

【讨论】:

    【解决方案3】:

    Sed 和 awk 通常不是读取 XML 的正确方法。它们可能有效,但 XML 可以随时更改布局并破坏事物,同时仍然是完全有效的 XML。

    更好的是使用类似 Perl 的东西。通过 CPAN 或在 ubunto 上使用“sudo apt-get install libxml-smart-perl”安装 XML::Smart 模块。

    然后是这样一个简单的脚本:

    use strict;
    use diagnostics;
    
    use XML::Smart;
    
    my $xml = XML::Smart->new ("svg.xml") || die "Cannot read XML: $!.";
    my $version = $xml->{svg}{version} || die "Cannot determine SVG version.";
    
    foreach my $file ($xml->{svg}{text}{tspan}('@')) {
        print $file->content . "\n";
    }
    

    将其保存为 svg.pl。将您的 XML 保存为 svg.xml。

    $ perl svg.pl /Volumes/Secondary500/Temp/Untitled-2_Layer 1 副本 2.pdf /Volumes/Secondary500/Temp/Untitled-2_Layer 1 copy.pdf /Volumes/Secondary500/Temp/Untitled-2_Layer 1.pdf

    这个:

    • 解析 XML,检查它是否正确。
    • 检查版本是否存在(实际上只是健全性检查)。
    • 循环遍历所有 svg/text/tspan 的数组并打印内容。

    玩得开心!

    【讨论】:

      【解决方案4】:

      其他人已经给出了很好的答案,说明如果您想绕过解析 XML,为什么应该使用适当的 XML 解析器,但至于如何在 sed 中完成此操作的解释,以防您遇到类似问题:

      #Full Command
      sed -n 's/^[^<]*<tspan[^>]*>\([^<]*\)<.*/\1/p'  ~/your_file.xml 
      

      n 选项使 sed 不发送任何输出,除非被要求这样做。通常 sed 会在末尾重复模式空间,这可能会造成混淆

      从 s 开始,因为是 [s] 替代。后面的“/”告诉 sed 我们将使用“/”来划分脚本的不同部分。

      从行首 (^) 开始抓取所有内容,以及之后不是左括号 ([^`

      获取 tspan 及其后面不是右括号 ([^>]*>) 的所有内容。这也将被丢弃。

      抓住右括号之后的所有内容,而不是左括号。这是我们要保留的部分,因此我们将其括在转义括号中。 “([^

      从最后一个右括号中抓取所有内容,直到 "<.>

      命令的第二部分:\1 这意味着:重复我们之前使用的第一组转义括号中的任何内容。只有一组括号,所以 \2、\3 等在这里没有意义,但您可以在其他脚本中使用它们。在您的情况下,您想重复我们从您的内部匹配的内容

      最后:“p”让 sed 打印出匹配项。这与开头的 -n 一起使用,相当于“不打印任何 'except' 匹配项”

      希望对您有所帮助...

      【讨论】:

        【解决方案5】:

        如果你有xmlgawk,你可以轻松搞定。

        @load xml
        
        BEGIN {
            XMLMODE = 1;
            XMLCHARSET = "utf-8";
        }
        
        XMLCHARDATA {
            data = $0;
        }
        
        XMLENDELEM == "tspan" {
            print data;
        }
        

        $ xgawk -f pick_from_svg.awk sample.xml 
        /Volumes/Secondary500/Temp/Untitled-2_Layer 1 copy 2.pdf
        /Volumes/Secondary500/Temp/Untitled-2_Layer 1 copy.pdf
        /Volumes/Secondary500/Temp/Untitled-2_Layer 1.pdf
        

        【讨论】:

          【解决方案6】:
          awk 'BEGIN{RS="font-size=\"10\">|</tspan>"}/pdf/' xml.txt
          

          结果

          $ awk 'BEGIN{RS="font-size=\"10\">|"}/pdf/' xml.txt /Volumes/Secondary500/Temp/Untitled-2_Layer 1 副本 2.pdf /Volumes/Secondary500/Temp/Untitled-2_Layer 1 copy.pdf /Volumes/Secondary500/Temp/Untitled-2_Layer 1.pdf

          此代码可能是最简单的代码,没有凌乱的正则表达式,它非常可扩展,您可以根据自己的喜好轻松调整它。我决定匹配术语“pdf”因此代码的/pdf/ 部分但是,例如,如果您想要匹配的其他文件不是 pdf 但确实包含“卷”这个词,您可以简单地请改用/Volumes/

          【讨论】: