【问题标题】:Joining multiple files in Linux在 Linux 中加入多个文件
【发布时间】:2016-07-21 04:06:25
【问题描述】:

我有以下文件(下划线代表制表符,文件名不包含在文件内容中):

(sample001.file)

Name_____scores_____gender   
Joey_____54_____Boy  
Kyle_____87_____Girl  
Sia______43_____Girl  
Marge____87_____Girl

(sample002.file)

Name_____scores_____gender   
Joey_____23_____Boy  
Pedro____76_____Boy  
Kyle_____76_____Girl  

(sample003.file)

Name_____scores_____gender   
Kyle_____34_____Girl  
James____65_____Boy  
Pedro____76_____Boy  
Sia______65_____Girl  
Marge____23_____Girl  

我希望将所有这些文件集成到一个文件中,仅包含第一列和第二列数据。它看起来像这样:

(集成.文件)

Name_____sample001____sample002_____sample003  
Joey_____54_____23____0  
Kyle_____87_____76____34  
Sia______43_____0_____65  
Marge____87_____0_____23  
Pedro____0______76____76  
James____0______0_____65

基本上,名称在第一列中应该只有一个条目,如果任何样本都没有数据,则它应该为零。标题不是必需的,但它们可以存在。

谁能帮我解决这个问题?

【问题讨论】:

标签: linux join awk multiple-columns cat


【解决方案1】:

使用 Bash 和 process substitution,您可以在一个(相当长的)命令管道中对三个文件执行此操作:

join -e 0 -a 1 -a 2 -t $'\t' -o 0,1.2,2.2 \
     <(sed 1d sample001.file | sort) \
     <(sed 1d sample002.file | sort) |
join -e 0 -a 1 -a 2 -t $'\t' -o 0,1.2,1.3,2.2 \
   - <(sed 1d sample003.file | sort)

请注意,join 要求其输入在连接列上排序,在本例中为第 1 列。 sed 1d 命令在对数据进行排序之前删除标题行。

当缺少值时,-e0 会说 'put 0-a1-a2 选项表示“保留文件 1 和文件 2 中的所有行”。 -t $'\t' 选项使用 Bash 的 ANSI C Quoting 为分隔符生成选项卡。如果您省略 -t 选项,它“有效”,但输出列由空格分隔,而不是制表符。 -o 选项指定要打印的列:0 是连接列(每个文件中的第 1 列); 1.2 是文件 1 中的第 2 列,等等。第二个 join 中的文件名 - 表示“读取标准输入”。

样本数据的输出是:

James   0       0       65
Joey    54      23      0
Kyle    87      76      34
Marge   87      0       23
Pedro   0       76      76
Sia     43      0       65

这里有一些处理 10 个示例文件的代码。我也需要生成数据,所以我使用了我的工具包中的一些工具来做到这一点——randomperturbrange(非常类似于标准的seq)和shuffle

for sample in $(range -f '%03d' 1 10)
do
    random -n 9 -T '%{ABCDEFJKMPS}s %[11:90]d   %{BG}s' |
    sort -u -k1,1 |
    join -o 1.2,2.2,2.3 names - |
    shuffle |
    sed 's/ /   /g' |
    perturb -f '%2.0f' -p 10 -c 2 > "sample$sample.file"
done

随机数据生成器的一个小问题是它还不允许您从(多字符)名称列表中选择随机条目,因此我使用了首字母列表并将它们映射到带有 @ 的名称987654345@ 文件。这有点奇怪,但您应该已经拥有数据并且不需要生成随机数据。 文件names 包含:

A Alex
B Belle
C Cynthia
D Doreen
E Elizabeth
F Ferdinand
J James
J Joey
K Kyle
M Marge
P Pedro
S Sia

例如,sample001.file 最终包含:

Belle   81      B
Marge   62      B
Ferdinand       37      B
Sia     44      B
Doreen  45      G
Elizabeth       18      G
Joey    16      B
James   19      B

然后加入代码需要在进行任何加入之前生成所有名称的列表,否则您不会看到第一个示例文件中没有出现的名称的正确分数。这不使用任何非标准工具。

tmp=$(mktemp ./tmp.XXXXXX)
trap 'rm -f "$tmp" "$tmp".?; exit 1' 0 1 2 3 13 15

sed 's/[[:space:]].*//' "$@" | sort -u > $tmp.0

join_cmd()
{
    join -e 0 -a 1 -a 2 -o "$outcols" "$@" > "$tmp.2"
}

outcols="0,2.2"
# Generate list of all names
join_cmd "$tmp.0" <(sort "$1")
mv "$tmp.2" "$tmp.1"
shift
outcols="0,1.2,2.2"  

for sample in "$@"
do
    join_cmd "$tmp.1" <(sort "$sample")
    sed 's/[[:space:]]\([0-9][0-9]*\)$/,\1/' "$tmp.2" > "$tmp.1"
done

# Don't hard code the output file name — do that on the command line that
# invokes this script (same as you specify the input file names on the command line).
sed 's/,/    /g' "$tmp.1" # > integrate.file

rm -f "$tmp" "$tmp".?
trap 0 1 2 3 13 15

这无需通过将数字映射到逗号分隔的列表来不断扩展连接列的列表。

$ column -t integrate.file
Alex       0   0   78  0   65  21  0   38  64  0
Belle      81  12  15  58  0   27  0   13  0   52
Cynthia    0   58  0   52  12  0   0   77  0   94
Doreen     45  49  0   85  0   0   57  32  81  63
Elizabeth  18  64  19  39  18  94  52  0   0   25
Ferdinand  37  0   0   0   0   64  72  21  0   28
James      19  0   0   77  0   48  78  59  39  23
Joey       16  0   0   79  0   48  78  70  39  19
Kyle       0   80  0   65  54  26  0   88  0   0
Marge      62  37  13  0   0   81  0   0   24  69
Pedro      0   0   40  0   47  74  79  0   0   0
Sia        44  0   27  0   55  0   43  0   32  0
$

您可以在输出的第 2 列中看到 sample000.file 的内容。并且可以看到names中的所有名字都出现在了输出中,并且每个样本文件都有一个编号。

【讨论】:

  • 非常感谢。虽然,我要合并很多文件。我怎样才能做到这一点?
  • 请参阅我对这个问题提出的comment - 这是涉及多个文件的两个问题。基本技术是加入 2 个文件并创建一个新文件;然后加入新文件和另一个原始文件以创建另一个文件,重复直到处理完所有文件。另一种选择是生成一个类似于此答案中所示的 shell 脚本,为每个额外的数据文件添加一个新的 join
  • 您可能想考虑是否可以通过在数字之间使用空格以及仅在名称和第一个数字之间使用制表符来简化连接处理。这将允许您在每组命令中拥有相同的连接列表。或者您可以决定将整个工作迁移到 Perl 或 Python。
猜你喜欢
  • 2014-10-23
  • 2011-05-21
  • 2014-06-04
  • 1970-01-01
  • 2012-05-30
  • 1970-01-01
  • 2012-07-12
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多