代码之家  ›  专栏  ›  技术社区  ›  P Shved

GNU/Linux中两个文件(作为行集)的笛卡尔积

  •  14
  • P Shved  · 技术社区  · 15 年前

    如何使用shell一行程序和通用GNU工具将两个文件中的行连接起来,就像在笛卡尔乘积中一样?最简洁、最美丽、最“利努西”的方式是什么?

    例如,如果我有两个文件:

    $ cat file1
    a
    b
    $ cat file2
    c
    d
    e
    

    结果应该是

    a, c
    a, d
    a, e
    b, c
    b, d
    b, e
    
    11 回复  |  直到 3 年前
        1
  •  14
  •   pixelbeat    15 年前

    下面是执行此操作的shell脚本

    while read a; do while read b; do echo "$a, $b"; done < file2; done < file1
    

    虽然那会很慢。 速度的下一步是在awk/perl中完成上述操作。

    awk 'NR==FNR { a[$0]; next } { for (i in a) print i",", $0 }' file1 file2
    

    paste -d, <(sed -n "$(yes 'p;' | head -n $(wc -l < file2))" file1) \
              <(cat $(yes 'file2' | head -n $(wc -l < file1)))
    
        2
  •  9
  •   James Brown    8 年前

    join :

    $ join -j 2 file1 file2
     a c
     a d
     a e
     b c
     b d
     b e
    
        3
  •  7
  •   P Shved    15 年前

    在shell中执行此操作的机械方法(不使用Perl或Python)是:

    while read line1
    do
        while read line2
        do echo "$line1, $line2"
        done < file2
    done < file1
    

    这个 join 命令有时可以用于这些操作-但是,我不清楚它是否可以作为退化情况进行笛卡尔积。

    从双回路向上的一个步骤是:

    while read line1
    do
        sed "s/^/$line1, /" file2
    done < file1
    
        4
  •  7
  •   Digital Trauma    9 年前

    我不会假装这很漂亮,但是。。。

    join -t, -j 9999 -o 2.1,1.1 /tmp/file1 /tmp/file2
    

    --加入(GNU coreutils)8.4

        5
  •  5
  •   Dennis Williamson    14 年前

    编辑 :

    DVK 的尝试激发了我的灵感 eval

    script='1{x;d};${H;x;s/\n/\,/g;p;q};H'
    eval "echo {$(sed -n $script file1)}\,\ {$(sed -n $script file2)}$'\n'"|sed 's/^ //'
    

    还是更简单的 sed 脚本:

    script=':a;N;${s/\n/,/g;b};ba'
    

    如果没有 -n 转换

    其中:

    a, c
    a, d
    a, e
    b, c
    b, d
    b, e
    

    在Bash中,您可以这样做。它不会读取文件,但这是一个巧妙的技巧:

    $ echo {a,b}\,\ {c,d,e}$'\n'
    a, c
     a, d
     a, e
     b, c
     b, d
     b, e
    

    更简单地说:

    $ echo {a,b}{c,d,e}
    ac ad ae bc bd be
    
        6
  •  3
  •   ATorras    9 年前

    通用递归BASH函数可以是这样的:

    foreachline() {
    
        _foreachline() {
    
            if [ $#  -lt 2 ]; then
                printf "$1\n"
                return
            fi
    
            local prefix=$1
            local file=$2
            shift 2
    
            while read line; do
                _foreachline "$prefix$line, " $*
            done <$file
        }
    
        _foreachline "" $*
    }
    
    foreachline file1 file2 file3
    

    当做

        7
  •  2
  •   DVK    15 年前

    解决方案1:

    perl -e '{use File::Slurp; @f1 = read_file("file1"); @f2 = read_file("file2"); map { chomp; $v1 = $_; map { print "$v1,$_"; } @f2 } @f1;}'

        8
  •  2
  •   unutbu    15 年前

    from itertools import product
    print('\n'.join((', '.join(elt) for elt in (product(*((line.strip() for line in fh) for fh in (open('file1','r'), open('file2','r'))))))))
    
    a, c
    a, d
    a, e
    b, c
    b, d
    b, e
    

    如果您有python 2.6之前的版本:

    def product(*args, **kwds):
        '''
        Source: http://docs.python.org/library/itertools.html#itertools.product
        '''
        # product('ABCD', 'xy') --> Ax Ay Bx By Cx Cy Dx Dy
        # product(range(2), repeat=3) --> 000 001 010 011 100 101 110 111
        pools = map(tuple, args) * kwds.get('repeat', 1)
        result = [[]]
        for pool in pools:
            result = [x+[y] for x in result for y in pool]
        for prod in result:
            yield tuple(prod)
    print('\n'.join((', '.join(elt) for elt in (product(*((line.strip() for line in fh) for fh in (open('file1','r'), open('file2','r'))))))))
    
        9
  •  2
  •   yassin    14 年前

    使用 join , awk 和过程替代:

    join <(xargs -I_ echo 1 _ < setA) <(xargs -I_ echo 1 _ < setB)
      | awk '{ printf("%s, %s\n", $2, $3) }'
    
        10
  •  1
  •   ghostdog74    15 年前
    awk 'FNR==NR{ a[++d]=$1; next}
    {
      for ( i=1;i<=d;i++){
        print $1","a[i]
      }
    }' file2 file1
    
    # ./shell.sh
    a,c
    a,d
    a,e
    b,c
    b,d
    b,e
    
        11
  •  1
  •   DVK    15 年前

    好的,这是上面丹尼斯·威廉姆森解决方案的推导,因为他注意到他的解决方案不从文件中读取:

    $ echo {`cat a | tr "\012" ","`}\,\ {`cat b | tr "\012" ","`}$'\n'
    a, c
     a, d
     a, e
     b, c
     b, d
     b, e
    
        12
  •  1
  •   Thor    5 年前

    GNU并行:

    parallel echo "{1}, {2}" :::: file1 :::: file2
    

    输出:

    a, c
    a, d
    a, e
    b, c
    b, d
    b, e
    
        13
  •  0
  •   Thor    4 年前

    当然 perl 有一个模块用于:

    #!/usr/bin/perl
    
    use File::Slurp;
    use Math::Cartesian::Product;
    
    use v5.10;
    $, = ", ";
    
    @file1 = read_file("file1", chomp => 1);
    @file2 = read_file("file2", chomp => 1);
    
    cartesian { say @_ } \@file1, \@file2;
    

    输出:

    a, c
    a, d
    a, e
    b, c
    b, d
    b, e
    
        14
  •  0
  •   glenn jackman    3 年前

    在里面 这是一条单行线

    printf '%s\n' (cat file1)", "(cat file2)