Bash 从宽格式重塑为长格式

Bash 从宽格式重塑为长格式,bash,parsing,unix,Bash,Parsing,Unix,我尝试使用unix将制表符分隔的文件从短/宽格式转换为长格式,方法与R中的重塑函数类似。我希望为起始文件中的每一行创建三行。列4当前包含3个由逗号分隔的值。我希望每个起始行的第1、2和3列保持相同,但第4列是初始第4列的值之一。这个例子可能比我口头描述的更清楚: current file: A1 A2 A3 A4,A5,A6 B1 B2 B3 B4,B5,B6 C1 C2 C3 C4,C5,C6 goal: A1 A2 A3 A4 A1 A2

我尝试使用unix将制表符分隔的文件从短/宽格式转换为长格式,方法与R中的重塑函数类似。我希望为起始文件中的每一行创建三行。列4当前包含3个由逗号分隔的值。我希望每个起始行的第1、2和3列保持相同,但第4列是初始第4列的值之一。这个例子可能比我口头描述的更清楚:

current file:  
A1  A2  A3  A4,A5,A6  
B1  B2  B3  B4,B5,B6  
C1  C2  C3  C4,C5,C6  

goal:  
A1  A2  A3  A4  
A1  A2  A3  A5  
A1  A2  A3  A6  
B1  B2  B3  B4  
B1  B2  B3  B5  
B1  B2  B3  B6  
C1  C2  C3  C4  
C1  C2  C3  C5  
C1  C2  C3  C6  
作为一个刚刚熟悉这种语言的人,我最初的想法是使用sed查找逗号,并用硬回车替换

sed的/,/&\n/“data.frame

我真的不知道如何包含第1-3列的值。我对这项工作的希望很低,但我能想到的唯一一件事是尝试用{print$1,$2,$3}插入列值

sed的/,/&\n{print$1,$2,$3}/'data.frame

毫不奇怪,输出结果如下所示:

A1  A2  A3  A4  
{print $1, $2, $3}  A5  
{print $1, $2, $3}  A6  
B1  B2  B3  B4  
{print $1, $2, $3}  B5  
{print $1, $2, $3}  B6  
C1  C2  C3  C4  
{print $1, $2, $3}  C5  
{print $1, $2, $3}  C6  
似乎一种方法可能是存储列1-3的值,然后插入它们。我不确定如何存储这些值,我认为这可能涉及到使用以下脚本的改编,但我很难理解所有组件

NR==FNR{a[$1,$2,$3]=1}


提前感谢您的想法。

您可以为此编写一个简单的
read
循环,并使用大括号扩展来解析逗号分隔的字段:

#!/bin/bash

while read -r f1 f2 f3 c1; do
  # split the comma delimited field 'c1' into its constituents
  for c in ${c1//,/ }; do
     printf "$f1 $f2 $f3 $c\n"
  done
done < input.txt

您可以为此编写一个简单的
read
循环,并使用大括号扩展来解析逗号分隔的字段:

#!/bin/bash

while read -r f1 f2 f3 c1; do
  # split the comma delimited field 'c1' into its constituents
  for c in ${c1//,/ }; do
     printf "$f1 $f2 $f3 $c\n"
  done
done < input.txt

作为无需调用外部程序的解决方案:

#!/bin/bash

data_file="d"

while IFS=" " read -r f1 f2 f3 r
do
  IFS="," read f4 f5 f6 <<<"$r"
  printf "$f1 $f2 $f3 $f4\n$f1 $f2 $f3 $f5\n$f1 $f2 $f3 $f6\n"
done <"$data_file"
#/bin/bash
数据文件=“d”
而IFS=“”读取-r f1 f2 f3 r
做

IFS=“,”将f4 f5 f6读取为解决方案,而不调用外部程序:

#!/bin/bash

data_file="d"

while IFS=" " read -r f1 f2 f3 r
do
  IFS="," read f4 f5 f6 <<<"$r"
  printf "$f1 $f2 $f3 $f4\n$f1 $f2 $f3 $f5\n$f1 $f2 $f3 $f6\n"
done <"$data_file"
#/bin/bash
数据文件=“d”
而IFS=“”读取-r f1 f2 f3 r
做

如果您不需要在第四列的一组中以任何特定顺序输出,则可以使用以下awk one liner命令:

awk '{split($4,a,","); for(i in a) print $1,$2,$3,a[i]}' input.txt
其工作原理是将第4列拆分为一个数组,然后为数组的每个元素打印“新”四列

如果顺序很重要——即A4必须在A5之前,等等,那么您可以使用经典的
for
循环:

awk '{split($4,a,","); for(i=1;i<=length(a);i++) print $1,$2,$3,a[i]}' input.txt

如果您愿意。

如果您不需要在第四列的一组中以任何特定顺序输出,则以下awk one liner可以执行:

awk '{split($4,a,","); for(i in a) print $1,$2,$3,a[i]}' input.txt
其工作原理是将第4列拆分为一个数组,然后为数组的每个元素打印“新”四列

如果顺序很重要——即A4必须在A5之前,等等,那么您可以使用经典的
for
循环:

awk '{split($4,a,","); for(i=1;i<=length(a);i++) print $1,$2,$3,a[i]}' input.txt

如果你愿意的话。

在《伟大的磨坊主》中有这样做的机会

你会有

A1 A2 A3 A4
A1 A2 A3 A5
A1 A2 A3 A6
B1 B2 B3 B4
B1 B2 B3 B5
B1 B2 B3 B6
C1 C2 C3 C4
C1 C2 C3 C5
C1 C2 C3 C6

在伟大的磨坊主中,有这样做的机会

你会有

A1 A2 A3 A4
A1 A2 A3 A5
A1 A2 A3 A6
B1 B2 B3 B4
B1 B2 B3 B5
B1 B2 B3 B6
C1 C2 C3 C4
C1 C2 C3 C5
C1 C2 C3 C6

为什么要使用运行sed的子shell?
${c1//,/}
是否足够?为什么要使用运行sed的子shell?
${c1//,/}
就足够了吗?关于
mapfile
的一些问题,因为我以前没有用过,我很好奇。通常,在读取文件时,如果需要将其拆分为行,这意味着您可能要在这些行上进行迭代,从而在以后进行
循环。也许它的性能更好?但要真正从中受益,您需要一个足够大的文件,以便在内存中加载它可能成为一个问题。如果需要将循环的内部保持在主shell的范围内,可以使用
@Fred,您可以质疑性能。这些东西总是很难评估。对于我来说,即使表现出差异,我也不会认为这是一个因素,除非它是显而易见的。当然,东西是可以测量的。但在所有其他条件相同的情况下,如果它不影响你的一天,那么就选择任何感觉合适或更快的方式进行编程,因为你选择哪种方式并不重要。我理解你的意思。我的问题更多的是试图找出在哪些情况下(在阅读生活中),你会倾向于使用
mapfile
而不是
read
和循环或awk脚本。我想知道什么时候
mapfile
会是你的“最佳工程折衷方案”。@Fred,当然,“所有其他东西”在大多数情况下都不相等。OP没有提到他的数据集有多大,但如果它有很多GB,那么将数据加载到内存中的方法可能是不可能的。在这种情况下,awk或输入重定向是可行的方法,尽管内部映射文件仍然有用。@Fred,就提出用例而言。。mapfile的用例与读取循环的用例相同。它的性能明显优于读循环,但除了
-C
回调选项之外,它实际上并没有做任何读循环无法完成的事情。关于
mapfile
的一些问题,因为我以前没有使用过它,我很好奇。通常,在读取文件时,如果需要将其拆分为行,这意味着您可能要在这些行上进行迭代,从而在以后进行
循环。也许它的性能更好?但要真正从中受益,您需要一个足够大的文件,以便在内存中加载它可能成为一个问题。如果需要将循环的内部保持在主shell的范围内,可以使用
@Fred,您可以质疑性能。这些东西总是很难评估。对于我来说,即使表现出差异,我也不会认为这是一个因素,除非它是显而易见的。当然,东西是可以测量的。但在所有其他条件相同的情况下,如果它不影响你的一天,那么就选择任何感觉合适或更快的方式进行编程,因为你选择哪种方式并不重要。我理解你的意思。我的问题更多的是想找出我