将多行记录折叠为仅包含“值”部分的单个 CSV 行

将多行记录折叠为仅包含“值”部分的单个 CSV 行

我有多行记录的数据,如下所示:

Name>Ami  
Admin>2  
Oper>1  
Name>Sum  
Admin>3  
Total>2  
Name>Tar  
Admin>1  
Oper>2

现在我想将这些记录折叠成单个 CSV 行,这些行应该只包含记录元素NameAdmin和 的“值”部分Oper。对于该示例,最终输出应如下所示:

Ami,2,1  
Sum,3,
Tar,1,2  

我能够从中获取输出paste - - - -d,,但我不想使用它,因为我想匹配Name并将这些值放入第一列和Admin第二列,然后Oper放入第三列。

答案1

看来你想要

  • 将多行记录折叠成单个 CSV 行,
  • 只打印记录属性的值NameAdmin并且Oper
  • 并打印未给出这些属性之一的“显式”空字段。

我会推荐以下awk程序:

awk -F'>' 'function printrec(){printf "%s,%s,%s\n",buf["Name"],buf["Admin"],buf["Oper"]}
           (FNR>1 && $1=="Name"){printrec();delete buf}
           {sub(/[[:space:]]*$/,"",$2); buf[$1]=$2}
           END{printrec()}' input.txt

其工作原理如下:

  • 输入文件的字段分隔符设置为>

  • 记录的所有元素都存储在关联数组中buf

  • 定义了一个函数,它以逗号分隔printrec()打印相关字段。buf如果buf不包含特定键,则引用它将计算为空字符串,从而满足您对缺失属性的空字段的要求。

  • 假设一条记录以一行开头Name。如果遇到这样的一行,并且它是不是文件中的第一行(FNR>1),将打印先前缓冲的记录,并再次清除缓冲区。

  • 对于每一行,当前属性将存储在 中buf,其中“key”部分作为“数组索引”,“value”部分作为数组值。

    笔记我包含了一个sub()调用,以从您的输入示例包含的“值”部分中删除尾随空格。如果您确定现实中没有空格,则可以省略第 3 行的该部分。

  • 在文件末尾,将打印最终缓冲的记录。

将此程序应用于您的示例将产生

Ami,2,1
Sum,3,
Tar,1,2

笔记使用delete数组需要 GNU awk。如果您有不同的awk口味,则需要使用

split("",buf)

作为解决方法。

此外,如果一条记录包含某个属性的多个实例(除非Name始终将其视为记录开始),则后面出现的情况将覆盖以前出现的情况。

答案2

如果我明白你想要什么,那么你应该使用xargsawk命令:

xargs -n3 < your_file.txt  | awk  '{gsub(/(Name|Admin|Oper)>/,"");  print $1","$2","$3}' | awk -F',' --file script.awk

哪里script.awk包含这个:

#! /usr/bin/awk

{
   if($1 ~ ".*>.*") print ","$2","$3 
   else if($2 ~ ".*>.*") print $1",,"$3 
   else if($3 ~ ".*>.*") print $1","$2"," 
   else print
}

如果your_file.txt您有:

Name>Ami  
Admin>2  
Oper>1  
Name>Sum  
Admin>3  
Total>2  
Name>Tar  
Admin>1  
Oper>2

xargs -n3将每 3 行获得文件的输出(作为一行):

Name>Ami Admin>2 Oper>1
Name>Sum Admin>3 Total>2
Name>Tar Admin>1 Oper>2

具有awk '{gsub(/(Name|Admin|Oper)>/,""); print $1","$2","$3}'像这样的值名称> 管理员> 或操作员>将被替换为空字符串,并且print $1","$2","$3值(> 之后)将用逗号打印。

如果您使用:

xargs -n3 < data3  | awk  '{gsub(/(Name|Admin|Oper)>/,"");  print $1","$2","$3}

你会得到:

Ami,2,1
Sum,3,Total>2
Tar,1,2

现在,它将删除不必要的字符串,例如Total>2.使用 ,script.awk您可以删除它们,但在此之前我们必须定义分隔符,在本例中为逗号 ( ,)。像这样的 awk 代码$1 ~ ".*>.*"将验证当前字符串($1、$2 或 $3)是否与模式匹配.*>.*,如果匹配,则不会打印当前字符串。

重要的:作品script.awk以小组形式呈现。因此,如果存在无效列,则必须放置此列而不是姓名或者行政或者歌剧院。例如如果your_file.txt有:

Name>Ami  
Total>2  
Oper>1  
Name>Sum  
Admin>3  
Total>2  
Total>Tar  
Admin>1  
Oper>2

该脚本的输出将是:

Ami,,1
Sum,3,
,1,2

但如果你里面有your_file.txt

Total>Ami  
Total>2  
Oper>1  
Name>Sum  
Admin>3  
Total>2  
Total>Tar  
Admin>1  
Total>2

该命令不会按您的预期工作。

笔记:如果您your_file.txt也希望对其进行编辑,则必须tee your_file.txt在最后使用:

xargs -n3 < your_file.txt  | awk  '{gsub(/(Name|Admin|Oper)>/,"");  print $1","$2","$3}' | awk -F',' --file script.awk | tee your_file.txt

答案3

[不知道Name>...记录是否有时会丢失,而不是系统地定期出现在要处理的数据中,我会假设它总是存在。]

基于awk的简单解决方案:

  • 不使用中间数组,
  • 不需要求助于定义函数:
  • 修复了要处理的数据记录中尾随空格的问题
  • 允许将任意数量的输入文件列为awk下面脚本的空格分隔参数。

主观上来说,虽然可能不如@AdminBee的答案那么优雅,但更容易阅读。

 $ awk -F'>' '($1 == "Name") {
                  if (NR>1) printf "\n"; 
                  gsub(" ","",$2); 
                  printf "%s%s", $2,","}
              ($1 == "Admin") {
                  gsub(" ","",$2); 
                  printf "%s%s", $2,","} 
              ($1 == "Oper") {
                  gsub(" ","",$2); 
                  printf "%s", $2} 
              END {printf "\n"}' input_file
Ami,2,1
Sum,3,
Tar,1,2 
 

在上面,actiongsub(" ","",$2)抑制了第二个字段中的所有空格,表示为$2。 (OP 包括那些尾随空格,它们在显示结果时会造成严重破坏。)

答案4

如果输入数据看起来更像这样(rec 格式):

Name: Ami
Admin: 2
Oper: 1

Name: Sum
Admin: 3
Total: 2

Name: Tar
Admin: 1
Oper: 2

...然后我们可以轻松地用来rec2csv | csvcut -C Total生成 CSV 文档

Name,Admin,Oper
Ami,2,1
Sum,3,
Tar,1,2

rec2csv是 GNU recutils 中的一个实用程序,它将重新格式化的数据重新格式化为 CSV,并且csvcut是一个从 CSV 中选择列的实用程序(这里用于排除Total),来自 csvkit。

可以使用 将原始数据转换为 re-formatted 格式awk,然后将其输入rec2csvcsvcut

awk '/^Name>/ { print "" } { sub(">",": "); print }' file | rec2csv | csvcut -C Total

这只是确保以 开头的每一行Name>前面有一个空行,然后替换每行中第一次出现>的。:

tail -n +2如果您想删除 CSV 标头,请传递结果。

这显然假设Name>原始文件中的每次出现都会引入一条新记录。


如果输入看起来更像这样(xtab 格式):

Name Ami
Admin 2
Oper 1

Name Sum
Admin 3
Total 2

Name Tar
Admin 1
Oper 2

然后我们可以使用mlr(Miller) 首先“unsparsify”每个记录(为缺失的键分配空值),然后以 CSV 输出格式提取所需的键:

mlr --ixtab unsparsify | mlr --ocsv cut -f Name,Admin,Total

我们可以使用与上一个类似的命令将原始数据转换为 xtab 格式awk,然后通过 Miller 将其输入:

awk '/^Name>/ { print "" } { sub(">"," "); print }' file | mlr --ixtab unsparsify | mlr --ocsv cut -f Name,Admin,Oper

这会给你

Name,Admin,Oper
Ami,2,1
Sum,3,
Tar,1,2

使用mlr(管道中的最后一个)及其-N选项来删除 CSV 标头。

相关内容