简体   繁体   English

linux命令输出一个csv文件(里面有不同的格式)

[英]linux command output a csv file(with different formats in it)

command output :命令输出:

//****** BMC SENSORS ******//
Object Id                     : 0xF000000
PCIe s:b:d.f                  : 0000:b2:00.0
Device Id                     : 0x0b30
Numa Node                     : 1
Ports Num                     : 01
Bitstream Id                  : 0x23000110030506
Bitstream Version             : 0.2.3
Pr Interface Id               : f3c99413-5081-4aad-bced-07eb84a6d0bb
( 1) Board Power              : 58.11 Watts
( 2) 12V Backplane Current    : 2.43 Amps
( 3) 12V Backplane Voltage    : 12.14 Volts
( 4) 1.2V Voltage             : 1.19 Volts
( 6) 1.8V Voltage             : 1.81 Volts
( 8) 3.3V Voltage             : 3.27 Volts
(10) FPGA Core Voltage        : 0.90 Volts
(11) FPGA Core Current        : 12.28 Amps
(12) FPGA Die Temperature     : 58.50 Celsius
(13) Board Temperature        : 43.00 Celsius
(14) QSFP0 Supply Voltage     : 0.00 Volts
(15) QSFP0 Temperature        : 0.00 Celsius
(24) 12V AUX Current          : 2.35 Amps
(25) 12V AUX Voltage          : 12.18 Volts
(37) QSFP1 Supply Voltage     : N/A
(38) QSFP1 Temperature        : N/A
(44) PKVL0 Core Temperature   : 0.00 Celsius
(45) PKVL0 SerDes Temperature : 0.00 Celsius
(46) PKVL1 Core Temperature   : 72.00 Celsius
(47) PKVL1 SerDes Temperature : 73.50 Celsius

I don't need first line, serial numbers.我不需要第一行,序列号。 Want left side part as heading and right side part as column of each heading respectively in CSV.在CSV中分别希望左侧部分作为标题和右侧部分作为每个标题的列。

I tried using sed in different ways -我尝试以不同的方式使用 sed -

sed ///****** BMC SENSORS ******/// fpgainfo bmc

fpgainfo bmc | sed ///****** BMC SENSORS ******/// >> ./fp.csv 

sed (1),(2),(3),(4),(6),(8),(10),(11),(12),(13),(14),(15),(24),(25),(37),(38),(44),(45),(46),(47)` fpgainfo bmc

but not ended up in what I expect.但最终没有达到我的预期。 Kindly help me to achieve the same.请帮助我实现同样的目标。

With the provided examples, would you please try the following:使用提供的示例,请您尝试以下操作:

fpgainfo bmc | sed -E '1d; s/^\([[:space:]]*[[:digit:]]+\)[[:space:]]*//; s/[[:space:]]+:[[:space:]]+/,/' > fp.csv
  • 1d skips the 1st line. 1d跳过第一行。
  • s/^\\([[:space:]]*[[:digit:]]+\\)[[:space:]]*// removes the leading serial numbers. s/^\\([[:space:]]*[[:digit:]]+\\)[[:space:]]*//删除前导序列​​号。
  • s/[[:space:]]+:[[:space:]]+/,/ replaces the colon and the surrounding whitespaces with a comma. s/[[:space:]]+:[[:space:]]+/,/用逗号替换冒号和周围的空格。

Scroll down to the last paragraph for my best recommendation.向下滚动到最后一段以获得我的最佳建议。 This presents some background around your attempt just to explain what's wrong before coming to the conclusion.这提供了一些关于您尝试的背景,只是为了在得出结论之前解释什么是错误的。

Of your attempts, only the middle one does anything remotely useful or syntactically well-defined - but the sed syntax is still wrong;在您的尝试中,只有中间的一个做任何远程有用或语法定义明确的事情 - 但sed语法仍然是错误的; you need to use a different delimiter or backslash the literal slashes in the regex for it to work.您需要使用不同的分隔符或反斜杠正则表达式中的文字斜杠才能使其工作。

But sed is probably not a good tool for this;但是sed可能不是一个很好的工具; its processing model is basically constrained to examine one input line at a time.它的处理模型基本上被限制为一次检查一个输入行。 You can make it perform more challenging processing, but it's definitely not a good first exercise for a beginner.可以让它执行更具挑战性的处理,但对于初学者来说,这绝对不是一个好的第一次练习。

I would perhaps prefer Awk for this.为此,我可能更喜欢 awk。 If you know another scripting language, or are planning to start learning one (eg Python) probably go with that instead;如果您知道另一种脚本语言,或者打算开始学习一种语言(例如 Python),请改用它; but Awk is straightforward enough that you can learn a good part of it in half an hour.但是 Awk 非常简单,您可以在半小时内学会其中的大部分内容。

The surrounding shell script will look the same regardless;无论如何,周围的 shell 脚本看起来都一样; you'll want to run your command and pipe its output to something which knows how to transform the lines it receives on standard input into your desired format.你要运行命令和管道输出到一些东西,知道如何接收标准输入线转换成你想要的格式。

fpgainfo bmc | awk ...

Here, then, is a simple Awk script to extract (what I think you are trying to say are) the fields you want into a CSV file, with field names on a header line before the values.那么,这里是一个简单的 Awk 脚本,用于将您想要的字段提取到 CSV 文件中(我认为您想说的是),字段名称位于值之前的标题行中。

awk 'BEGIN { sep=""; n=0 }
  /^\( *[1-9][0-9]*\)/ {
    # Parse out data; trim trailing whitespace
    key = substr($0, 6, 25)
    sub(/ +$/, "", key)
    value = substr($0, 33)
    sub(/ +$/, "", value)

    # Normalize index
    sub(/^\( +/, "")
    if ($1 ~ /^[123468]|1[12345]|2[45]|3[78]|4[4567])\)/) {
      printf("%s%s", sep, key)
      sep = ","
      v[++n] = value }
  }
  END { printf "\n"; sep=""; 
    for(i=1; i<=n; ++i) {
      printf("%s%s", sep, v[i])
      sep = "," }
    printf "\n"
  }'

Demo: https://ideone.com/BwRN72演示: https : //ideone.com/BwRN72

But this is still brittle because it hinges on the assumption that the output you showed is completely representative of everything we need to handle.但这仍然很脆弱,因为它取决于您显示的输出完全代表我们需要处理的所有内容的假设。 There could easily be a case where the program wraps a long line for legibility, or adjusts column widths depending on your terminal's size.很容易出现这样的情况:程序为了易读性而将长行换行,或者根据终端的大小调整列宽。

I'm not familiar with fpgainfo but most modern tools have an option to produce machine-readable output directly (JSON, YAML, XML etc);我不熟悉fpgainfo但大多数现代工具都可以选择直接生成机器可读的输出(JSON、YAML、XML 等); if that's available, it's generally vastly superior to hand-crafting a parser for mucky human-readable formats like this.如果可以的话,它通常比为这样的人类可读格式手工制作解析器要好得多。 Quick googling gets me https://opae.github.io/0.13.0/docs/fpga_tools/fpgainfo/fpgainfo.html which mentions a --json option - probably go with that instead (and notice that eg jq can further transform that into proper CSV with a simple option).快速谷歌搜索让我https://opae.github.io/0.13.0/docs/fpga_tools/fpgainfo/fpgainfo.html提到了一个--json选项 - 可能改为使用它(并注意例如jq可以进一步转换使用一个简单的选项转换为正确的 CSV)。 It's not necessarily less work, but it's going to be hugely more robust because the formats are documented, well-specified, and designed for programmatic manipulation.它不一定是更少的工作,但它会更加健壮,因为格式被记录在案、明确指定并且专为编程操作而设计。

This might work for you (GNU sed):这可能对你有用(GNU sed):

sed -E '1{x;s/^/\n/;x};/^\(/!d
        G;s/^(....).*: (.*)\n(.*)\n(.*)/\3,\1\n\4,\2/;h;$!d;s/.(.*\n)./\1/' file

Prime the hold space with a newline (columns will be to the left of the newline and values to the right).用换行符填充保留空间(列将在换行符的左侧,值在右侧)。

Junk the unwanted lines.垃圾不需要的行。

Append the hold space and substitute the current lines column and value into the accumulated columns/values.追加保留空间并将当前行列和值替换为累积的列/值。

Copy the accumulated columns/values to the hold space.将累积的列/值复制到保留空间。

Delete all but the last line.删除除最后一行之外的所有内容。

Remove comma artefacts and print the result.删除逗号伪影并打印结果。


Same solution but for real headings:相同的解决方案,但对于真正的标题:

sed -E '1{x;s/^/\n/;x};/^\(/!d
        G;s/^.....(.*\S)\s*: (.*)\n(.*)\n(.*)/\3,\1\n\4,\2/;h;$!d;s/.(.*\n)./\1/' file

For heading with parens:对于带括号的标题:

sed -E '1{x;s/^/\n/;x};/^\(/!d
        G;s/^(.*\S)\s*: (.*)\n(.*)\n(.*)/\3,\1\n\4,\2/;h;$!d;s/.(.*\n)./\1/' file

There are different ways to use awk to solve the problem.使用awk解决问题的方法有多种。

awk -F ' *: *' 'NR>1 { sub(/^\([0-9 ]*\) */,"");
  if(heading) { heading=heading ","; data=data ","}
  heading=heading $1; data = data $2
}
END {print heading; print data}' inputfile

With the input shown in the question this prints随着问题中显示的输入,这会打印

Object Id,PCIe s,Device Id,Numa Node,Ports Num,Bitstream Id,Bitstream Version,Pr Interface Id,Board Power,12V Backplane Current,12V Backplane Voltage,1.2V Voltage,1.8V Voltage,3.3V Voltage,FPGA Core Voltage,FPGA Core Current,FPGA Die Temperature,Board Temperature,QSFP0 Supply Voltage,QSFP0 Temperature,12V AUX Current,12V AUX Voltage,QSFP1 Supply Voltage,QSFP1 Temperature,PKVL0 Core Temperature,PKVL0 SerDes Temperature,PKVL1 Core Temperature,PKVL1 SerDes Temperature
0xF000000,b,0x0b30,1,01,0x23000110030506,0.2.3,f3c99413-5081-4aad-bced-07eb84a6d0bb,58.11 Watts,2.43 Amps,12.14 Volts,1.19 Volts,1.81 Volts,3.27 Volts,0.90 Volts,12.28 Amps,58.50 Celsius,43.00 Celsius,0.00 Volts,0.00 Celsius,2.35 Amps,12.18 Volts,N/A,N/A,0.00 Celsius,0.00 Celsius,72.00 Celsius,73.50 Celsius

Explanation:解释:

  • -F ' *: *' field separator is : with any number of leading and trailing spaces -F ' *: *'字段分隔符是:带有任意数量的前导和尾随空格
  • NR>1 do the commands in { ...} for all lines except first one NR>1对除第一行之外的所有行执行{ ...}的命令
  • sub(/^\\([0-9 ]*\\) */,""); any leading number in parentheses from the befinning of the line行首的括号中的任何前导数字
  • if(heading) { heading=heading ","; data=data ","} if(heading) { heading=heading ","; data=data ","} if heading is not empty, append a comma to both heading and data, ie us a comma as a delimiter except before the first values if(heading) { heading=heading ","; data=data ","}如果标题不为空,则在标题和数据后附加一个逗号,即我们使用逗号作为分隔符,除了第一个值之前
  • heading=heading $1; data = data $2 heading=heading $1; data = data $2 append the first field to the heading and the second to the data heading=heading $1; data = data $2将第一个字段附加到标题,将第二个字段附加到数据
  • END {print heading; print data} END {print heading; print data} at the end print heading and data. END {print heading; print data}在最后打印标题和数据。

I tested this with GNU awk only, which should be the default version of awk on Linux.我仅使用 GNU awk对此进行了测试,这应该是 Linux 上awk的默认版本。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM