2012-04-28 44 views
0

我正在寻找想法,而不是shell(linux)中下一个问题的完整解决方案。什么是最好的解决方案? (awk,while-loop,sed ....)用键合并两个文件 - >值结构

我有两个文件具有相同的行结构:key-value-value。我想合并这两个文件。如果该值不存在,该脚本将插入一个新行。如果它存在,脚本会更新这些值(通过添加它们)。

Example: 
File 1: 

john-15-40 
doo-10-91 
mary-14-19 
foo-11-0 

File 2: 

foo-110-10 
john-22-11 
ghost-1000-1000 

Result: 
foo-121-10 
john-37-51 
ghost-1000-1000 
doo-10-91 
mary-14-19 

我该怎么做?

回答

4

简单使用awk

awk ' 
    BEGIN {FS = OFS = "-"} 
    {v1[$1] += $2; v2[$1] += $3} 
    END {for (key in v1) {print key, v1[key], v2[key]}} 
' F1 F2 
+0

+1美丽的解决方案。 – kev 2012-04-28 11:30:26

+0

这是美丽的 – flatronka 2012-04-28 11:36:40

+0

美是主观的,有时只有皮肤深,并伴有低智力。在这里,“美”植根于能够将字符串值视为数字,并能够增加从未定义的变量。如果你拼错了一个变量,awk没有警告。如果一个字符串包含的东西不是一个数字,它只是零,那么...... awk'{print'a'+ 1}''高兴地产生1。没问题,只是总是用你的“美丽的节目”“美丽的输入”。 – Kaz 2012-05-15 22:19:29

1

你需要一个具有相关数组的语言。你的任务对于任何脚本语言都非常简单,但perl和awk特别适合逐行处理文本文件。

伪代码:

read line from file1, file2 
split line to key and values 
if there are no key in hash 
    add key and values 
else 
    add values and print key/values 
+0

这是一个很好的解决方案感谢,但不是最聪明在壳(Linux)的, – flatronka 2012-04-28 10:33:07

+0

@hunor实际上,击4确实有天然关联数组;如果您不需要它来使用旧版本的语言,则可以在本机shell中完成此操作。 – 2012-04-28 11:49:24

+0

@Andrey否,关联数组对此不需要 - 如果在处理之前对输入流进行排序。 – 2012-04-28 12:15:11

1

我知道你没有在PHP中要求它,但它可能的帮助。有可能是另一种语言的类似的东西,如果你宁愿:

<?PHP 

$file_handle = fopen("file1", "r"); 

while (!feof($file_handle)) { 
$line_of_text = fgets($file_handle); 
list($name,$value1,$value2) = explode('-', $line_of_text); 
$file1[$name]=array($value1,$value2); 
} 
fclose($file_handle); 
// repeate for file2 
//then use the 2 arrays, $file1[] and $file2[] to rewrite the file as 'file3' or whatever. 
//Checking for duplicates and doing the math. 
?> 
+0

感谢您的解决方案,我爱PHP,它类似于Andrey Yazu伪代码 – flatronka 2012-04-28 10:52:38

+0

我以为同样的事情。他在我写作的时候发布了它。 – TecBrat 2012-04-28 11:06:46

+0

绝对感谢,但我正在寻找一个智能的,基于shell(linux)的解决方案 – flatronka 2012-04-28 11:13:21

1

这可以在击4本身做:

#!/bin/bash 
declare -A vals_one vals_two 
while IFS=- read key val1 val2; do 
    if [[ ${vals_one["$key"]} ]] ; then 
    vals_one["$key"]=$((${vals_one["$key"]} + val1)) 
    vals_two["$key"]=$((${vals_two["$key"]} + val2)) 
    else 
    vals_one["$key"]=$val1 
    vals_two["$key"]=$val2 
    fi 
done < <(cat input1.txt input2.txt) 
for key in "${!vals_one[@]}"; do 
    printf '%s-%s-%s\n' "$key" "${vals_one[$key]}" "${vals_two[$key]}" 
done 

注意,这种方法有点记忆效率低下;一个更有效率的内存方法会在合并之前对文件进行排序(如果排序的内容不能适应内存,那么GNU排序能够生成临时文件,因此比我们写的任何合理的脚本更有能力),因此只需要同时储存两行内存:

#!/bin/bash 

function merge_inputs { 
    IFS=- read key val1 val2 
    while IFS=- read new_key new_val1 new_val2; do 
     if [[ $key = "$new_key" ]] ; then 
     val1=$((val1 + new_val1)) 
     val2=$((val2 + new_val2)) 
     else 
     printf '%s-%s-%s\n' "$key" "$val1" "$val2" 
     key=$new_key 
     val1=$new_val1 
     val2=$new_val2 
     fi 
    done 
    printf '%s-%s-%s\n' "$key" "$val1" "$val2" 
} 
sort input1.txt input2.txt | merge_inputs 

此外,这后一种形式不需要关联数组,并会与旧版本的bash的工作(或者,某些修改,其他shell) 。

+0

感谢您的工作:d,我喜欢它,很好 – flatronka 2012-04-28 12:26:55

1

我喜欢glenn的short fat解决方案。并有一个tall thin解决方案。

如果您有两个文件:1.txt2.txt

sort {1,2}.txt | 
awk -F- -vOFS=- ' 
NR==1{ 
    x=$1 
} 
x==$1{ 
    y+=$2 
    z+=$3 
    next 
} 
{ 
    print x,y,z; 
    x=$1 
    y=$2 
    z=$3 
} 
END{ 
    print 
}'