Декартово произведение двух файлов (в виде наборов строк) в GNU / Linux - PullRequest
14 голосов
/ 25 октября 2009

Как я могу использовать однострочные оболочки и обычные инструменты GNU для объединения строк в два файла, как в декартовом продукте? Какой самый краткий, красивый и "линукс" способ?

Например, если у меня есть два файла:

$ cat file1
a
b
$ cat file2
c
d
e

Результат должен быть

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

Ответы [ 11 ]

14 голосов
/ 25 октября 2009

Вот сценарий оболочки, чтобы сделать это

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)))
6 голосов
/ 21 сентября 2016

Запятая не будет, но будет использоваться только join:

$ join -j 2 file1 file2
 a c
 a d
 a e
 b c
 b d
 b e
6 голосов
/ 21 августа 2014

Я не собираюсь притворяться, что это мило, но ...

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

(обновлено благодаря Iwan Aucamp ниже)

- объединение (GNU coreutils) 8,4

6 голосов
/ 25 октября 2009

Механический способ сделать это в оболочке, не используя 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 голосов
/ 25 октября 2009

Редактировать

Попытка 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
3 голосов
/ 19 февраля 2015

универсальная рекурсивная функция 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

Привет.

2 голосов
/ 25 октября 2009

Решение 1:

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

2 голосов
/ 25 октября 2009

Редактировать: Упс ... Извините, я думал, что это был тег python ...

Если у вас Python 2.6:

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'))))))))
1 голос
/ 31 июля 2010

Решение, использующее join, awk и замену процесса:

join <(xargs -I_ echo 1 _ < setA) <(xargs -I_ echo 1 _ < setB)
  | awk '{ printf("%s, %s\n", $2, $3) }'
1 голос
/ 25 октября 2009

Хорошо, это вывод решения Денниса Уильямсона, приведенного выше, поскольку он отметил, что его не читает из файла:

$ echo {`cat a | tr "\012" ","`}\,\ {`cat b | tr "\012" ","`}$'\n'
a, c
 a, d
 a, e
 b, c
 b, d
 b, e
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...