如何将 cat 命令格式化为 bash 中的 table

How to format cat command as a table in bash

我有以下代码解析 XML 以显示文件中每个元素的节点值。

#Abbreviation - symbol
cat elements/*.xml |  egrep "<symbol>.*</symbol>" |sed -e "s/<symbol>\(.*\)<\/symbol>//"|tr "|" " "

#Weight - atomic-weight
cat elements/*.xml |  egrep "<atomic-weight>.*</atomic-weight>" |sed -e "s/<atomic-weight>\(.*\)<\/atomic-weight>//"|tr "|" " "

#Number atomic-number
cat elements/*.xml |  egrep "<atomic-number>.*</atomic-number>" |sed -e "s/<atomic-number>\(.*\)<\/atomic-number>//"|tr "|" " " > number

如何将这三个输出格式化为 table 而不是一个巨大的顺序列表?

样本数据-

文件 1 -

  <symbol>Ag</symbol>
  <atomic-number>47</atomic-number>
  <atomic-weight>107.8682</atomic-weight>

文件 2 -

  <symbol>Ba</symbol>
  <atomic-number>56</atomic-number>
  <atomic-weight>137.327</atomic-weight>

期望的输出 -

Symbol   Number   Weight
Ag       47       107.8682
Ba       56       137.327

试试这个:

#!/bin/bash

printf '%-9s %-9s %-9s\n' "Symbol" "Number" "Weight"

for F in *.xml
do
    symbol=$(grep -E "<symbol>.*</symbol>" "$F"               | sed -e "s/.*<symbol>\(.*\)<\/symbol>.*//")
    number=$(grep -E "<atomic-number>.*</atomic-number>" "$F" | sed -e "s/.*<atomic-number>\(.*\)<\/atomic-number>.*//")
    weight=$(grep -E "<atomic-weight>.*</atomic-weight>" "$F" | sed -e "s/.*<atomic-weight>\(.*\)<\/atomic-weight>.*//")

    printf '%-9s %-9s %-9s\n' "$symbol" "$number" "$weight"
done
  • printf 允许您设置打印文本(或数字,或浮点数,...)的宽度和对齐方式的格式。
  • 为了避免对权重值(例如小数位数)的任何解释,所有值都打印为字符串。
  • for printf, '%-9s' 表示它将使用 9 个字符宽打印值,左对齐。如果没有 -,它将右对齐。
  • printf 不输出回车 return 除非你告诉它,这解释了 \n.
  • 我重复使用了您的 grep ... | sed ... 命令,但有 2 个细节。 1 使用 grep -E 而不是 egrep2sed 的开头和结尾添加了 .* 以消除 <SOMETHING> 标签的前缀或后缀。

我得到的输出是:

$ ./so.bash 
Symbol    Number    Weight   
Ag        47        107.8682 
Ba        56        137.327  

如果您只知道它是有效的 XML,您最好使用 XML 解析器。例如,这样的解析器包含在 RubyPerl 中。这将使您还可以解析看起来像

的文件内容
<atomic-weight>
  107.8682
</atomic-weight>

但是如果您可以确保输入文件完全您发布的格式,您可以这样做:

for file in File1 File2
do
  tr '<>' ' ' <$file | cut -d ' ' -f 3
done

如果您需要将数据格式化为特定位置的列,您可以这样做

for file in File1 File2
do
  printf ' put your format specification here ' $(tr '<>' ' ' <$file | cut -d ' ' -f 3)
done

假设输入文件是 XML 外部通用解析实体, 如果包装在根元素中,则连接到 well-formed XML, 您可以使用 一次性处理它们:

printf '<doc>%s</doc>\n' "$(cat file*.xml)" |
xmlstarlet select --template --var ofs="'$(printf "\t")'" \
    --value-of 'concat("Symbol", $ofs, "Number", $ofs, "Weight")' --nl \
    --match '*/*[position() mod 3 = 1]' --sort 'A:T:-' '.' \
    --value-of 'concat(., $ofs, following-sibling::*[1], $ofs, following-sibling::*[2])' --nl
  • printf:将文档元素包裹在 input
  • 周围
  • --var ofs : 定义输出字段分隔符
  • 首先--value-of:发出header
  • 列出每第 3 个元素(即符号),按符号排序为 A递增 Text
  • 改为按 atomic-number 排序:--sort 'A:N:-' 'following-sibling::*[1]'

输出:

Symbol  Number  Weight
Ag      47      107.8682
Ba      56      137.327