Ame*_*ina 3 zsh xml text-processing csv
我有大量具有相同结构的 XML 文件:
$ cat file_<ID>.xml
...
...
...
<double>1.2342</double>
<double>2.3456</double>
...
...
...
...
Run Code Online (Sandbox Code Playgroud)
其中<double>每个 XML 文件中此类条目的数量是固定且已知的(在我的特定情况下为 168)。
我需要构建一个csv包含所有这些 XML 文件内容的文件,如下所示:
file_0001 1.2342 2.3456 ...
file_0002 1.2342 2.3456 ...
Run Code Online (Sandbox Code Playgroud)
等等。
我怎样才能有效地做到这一点?
我想出的最好的是:
$ cat file_<ID>.xml
...
...
...
<double>1.2342</double>
<double>2.3456</double>
...
...
...
...
Run Code Online (Sandbox Code Playgroud)
当我在一个包含 ~10K XML 文件的文件夹中计时上述脚本时,我得到:
./from_xml_to_csv.sh 100.45s user 94.84s system 239% cpu 1:21.48 total
Run Code Online (Sandbox Code Playgroud)
并不可怕,但我希望能处理 100 倍或 1000 倍以上的文件。我怎样才能使这个处理更有效率?
另外,使用我上面的解决方案,我是否会遇到全局扩展达到限制的情况,例如在处理数百万个文件时?(典型"too many args"问题)。
对于对这个问题的一个很好的解决方案感兴趣的人,请阅读@mikeserve 的回答。到目前为止,它是最快的,也是扩展最好的。
这应该可以解决问题:
awk -F '[<>]' '
NR!=1 && FNR==1{printf "\n"}
FNR==1{sub(".*/", "", FILENAME); sub(".xml$", "", FILENAME); printf FILENAME}
/double/{printf " %s", $3}
END{printf "\n"}
' $path_to_xml/*.xml > final_table.csv
Run Code Online (Sandbox Code Playgroud)
awk: 使用程序awk,我用 GNU awk 4.0.1 测试过-F '[<>]': 使用<和>作为字段分隔符NR!=1 && FNR==1{printf "\n"}: 如果不是整体的第一行 ( NR!=1) 而是文件的第一行 ( FNR==1) 打印换行符FNR==1{sub(".*/", "", FILENAME); sub(".xml$", "", FILENAME); printf FILENAME}: 如果它是文件的第一行,则去掉文件名/( sub(".*/", "", FILENAME)) 中最后( ) 的所有内容FILENAME,去掉尾部.xml( sub(".xml$", "", FILENAME)) 并打印结果 ( printf FILENAME)/double/{printf " %s", $3}如果一行包含“double” ( /double/),则打印一个空格,后跟第三个字段 ( printf " %s", $3)。使用<和>作为分隔符,这将是数字(第一个字段是第一个字段之前的任何内容<,第二个字段是double)。如果需要,您可以在此处格式化数字。例如,使用%8.3f代替%s任何数字将打印 3 个小数位,并且总长度(包括点和小数位)至少为 8。$path_to_xml/*.xml: 文件列表> final_table.csv:final_table.csv通过重定向输出将结果放入在“argument list to long”错误的情况下,可以使用findwith参数-exec生成文件列表,而不是直接传递:
find $path_to_xml -maxdepth 1 -type f -name '*.xml' -exec awk -F '[<>]' '
NR!=1 && FNR==1{printf "\n"}
FNR==1{sub(".*/", "", FILENAME); sub(".xml$", "", FILENAME); printf FILENAME}
/double/{printf " %s", $3}
END{printf "\n"}
' {} + > final_table.csv
Run Code Online (Sandbox Code Playgroud)
find $path_to_xml: 告诉find列出文件$path_to_xml-maxdepth 1: 不要下降到子文件夹 $path_to_xml-type f: 只列出常规文件(这也不包括$path_to_xml它本身)-name '*.xml': only list files that match the pattern*.xml`,这需要引用,否则 shell 将尝试扩展模式-exec COMMAND {} +:COMMAND使用匹配的文件作为参数运行命令代替{}. +表示可以一次传递多个文件,从而减少分叉。如果您使用\;(;需要被引用,否则它由 shell 解释) 而不是+单独为每个文件运行命令。您还可以xargs结合使用find:
find $path_to_xml -maxdepth 1 -type f -name '*.xml' -print0 |
xargs -0 awk -F '[<>]' '
NR!=1 && FNR==1{printf "\n"}
FNR==1{sub(".*/", "", FILENAME); sub(".xml$", "", FILENAME); printf FILENAME}
/double/{printf " %s", $3}
END{printf "\n"}
' > final_table.csv
Run Code Online (Sandbox Code Playgroud)
-print0: 输出以空字符分隔的文件列表|(pipe): 将标准输出重定向find到标准输入xargsxargs: 从标准输入构建和运行命令,即为每个传递的参数(此处为文件名)运行一个命令。-0: 直接xargs假设参数由空字符分隔awk -F '[<>]' '
BEGINFILE {sub(".*/", "", FILENAME); sub(".xml$", "", FILENAME); printf FILENAME}
/double/{printf " %s", $3}
ENDFILE {printf "\n"}
' $path_to_xml/*.xml > final_table.csv
Run Code Online (Sandbox Code Playgroud)
where BEGINFILE,ENDFILE在更改文件时调用(如果您的 awk 支持它)。
| 归档时间: |
|
| 查看次数: |
3029 次 |
| 最近记录: |