Bash - пара каждой строки файла


10

Этот вопрос тесно связан с этим и с этим вопросом. У меня есть файл, который содержит несколько строк, где каждая строка представляет собой путь к файлу. Теперь я хочу , чтобы соединить каждую строку с каждой иной линией (не сам). Также пара для моих целей A Bравна B Aпаре, поэтому должна быть получена только одна из этих комбинаций.

пример

files.dat читается как это в сокращенной записи, каждая буква представляет собой путь к файлу (абсолютный или относительный)

a
b
c
d
e

Тогда мой результат должен выглядеть примерно так:

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

Желательно, чтобы я решил это в bash. В отличие от других вопросов, мой список файлов довольно мал (около 200 строк), поэтому использование циклов и объема оперативной памяти не представляет проблем.


Должен ли он быть в самом bash или просто в командной строке bash? Другие утилиты лучше расположены для обработки текста.
Джефф Шаллер

@JeffSchaller Что-то доступное через командную строку bash. Мне было немного неясно, извините
Энно

Это почти становится Code Golf : P
Ричард де Вит

3
Как правило, если вам нужно сделать что-то нетривиальное, используйте ваш любимый язык сценариев поверх BASH. Он будет менее хрупким (например, по отношению к специальным символам или пробелам), и его будет намного легче расширять, когда вам это нужно (если вам нужно три или отфильтровать некоторые из них). Python или Perl должны быть установлены практически на любой Linux-системе, поэтому они являются хорошим выбором (если вы не работаете со встроенными системами, такими как Busybox).
Davidmh

Ответы:


7

Используйте эту команду:

awk '{ name[$1]++ }
    END { PROCINFO["sorted_in"] = "@ind_str_asc"
        for (v1 in name) for (v2 in name) if (v1 < v2) print v1, v2 }
        ' files.dat

PROCINFOможет быть gawkпродолжением. Если ваш awkне поддерживает его, просто пропустите PROCINFO["sorted_in"] = "@ind_str_asc"строку и направьте вывод в sort(если вы хотите, чтобы вывод был отсортирован).

(Это не требует сортировки входных данных.)


8
$ join -j 2 -o 1.1,2.1 file file | awk '!seen[$1,$2]++ && !seen[$2,$1]++'
a b
a c
a d
a e
b c
b d
b e
c d
c e
d e

Это предполагает, что ни одна строка во входном файле не содержит пробелов. Также предполагается, что файл отсортирован .

Команда joinсоздает полное перекрестное произведение строк в файле. Это делается путем соединения файла с самим собой на несуществующем поле. Нестандартный -j 2может быть заменен на -1 2 -2 2(но не на, -j2если вы не используете GNU join).

Команда awkчитает результат этого и выводит только те результаты, которые еще не были просмотрены.


Что вы подразумеваете под "файл отсортирован"? Сортировка по каким критериям?
Энно

@Enno Сортировал так, как sort -bбы сортировал. joinтребуют отсортированные входные файлы.
Кусалананда

8

pythonРаствор. Входной файл подается itertools.combinationsиз стандартной библиотеки, которая генерирует кортежи длиной 2, которые форматируются и печатаются в стандартный вывод.

python3 -c 'from itertools import combinations
with open("file") as f:
    lines = (line.rstrip() for line in f)
    lines = ("{} {}".format(x, y) for x, y in combinations(lines, 2))
    print(*lines, sep="\n")
'

6

Если вы rubyустановили:

$ ruby -0777 -F'\n' -lane '$F.combination(2) { |c| puts c.join(" ")}' ip.txt
a b
a c
a d
a e
b c
b d
b e
c d
c e
d e
  • -0777 хлебать весь файл (должно быть в порядке, так как в OP указано, что размер файла небольшой)
  • -F'\n'разделить на основе новой строки, поэтому каждая строка будет элементом в $Fмассиве
  • $F.combination(2)генерировать комбинации 2элементов одновременно
  • { |c| puts c.join(" ")} распечатывать по мере необходимости
  • если входной файл может содержать дубликаты, используйте $F.uniq.combination(2)


для 3 элементов одновременно:

$ ruby -0777 -F'\n' -lane '$F.combination(3) { |c| puts c.join(" ")}' ip.txt
a b c
a b d
a b e
a c d
a c e
a d e
b c d
b c e
b d e
c d e


С perl(не универсальный)

$ perl -0777 -F'\n' -lane 'for $i (0..$#F) {
                             for $j ($i+1..$#F) { 
                               print "$F[$i] $F[$j]\n" } }' ip.txt
a b
a c
a d
a e
b c
b d
b e
c d
c e
d e


С awk

$ awk '{ a[NR]=$0 }
       END{ for(i=1;i<=NR;i++)
              for(j=i+1;j<=NR;j++)
                print a[i], a[j] }' ip.txt 
a b
a c
a d
a e
b c
b d
b e
c d
c e
d e

5

Вот один в чистой оболочке.

test $# -gt 1 || exit
a=$1
shift
for f in "$@"
do
  echo $a $f
done
exec /bin/sh $0 "$@"

Пример:

~ (137) $ sh test.sh $(cat file.dat)
a b
a c
a d
a e
b c
b d
b e
c d
c e
d e
~ (138) $ 

1
Команда замены полосы задней новой строки, так что вы лучше с чем - то вроде <file.dat xargs test.shчемtest.sh $(cat file.dat)
Iruvar

1

Используя Perlмы можем сделать это как показано:

$ perl -lne '
     push @A, $_}{
     while ( @A ) {
        my $e = shift @A;
        print "$e $_" for @A;
     }
' input.txt
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.