Инструменты Linux для обработки файлов как наборов и выполнения операций над ними

82

Кто-нибудь знает какой-либо инструмент Linux, специально предназначенный для обработки файлов как наборов и выполнения операций над ними? Как разница, пересечение и т. Д.?

Nilton
источник

Ответы:

110

Предполагая, что элементы - это строки символов, отличные от NUL и новой строки (однако следует помнить, что символ новой строки допустим в именах файлов), вы можете представить набор в виде текстового файла с одним элементом в строке и использовать некоторые стандартные утилиты Unix.

Установить членство

$ grep -Fxc 'element' set   # outputs 1 if element is in set
                            # outputs >1 if set is a multi-set
                            # outputs 0 if element is not in set

$ grep -Fxq 'element' set   # returns 0 (true)  if element is in set
                            # returns 1 (false) if element is not in set

$ awk '$0 == "element" { s=1; exit }; END { exit !s }' set
# returns 0 if element is in set, 1 otherwise.

$ awk -v e='element' '$0 == e { s=1; exit } END { exit !s }'

Установить пересечение

$ comm -12 <(sort set1) <(sort set2)  # outputs intersect of set1 and set2

$ grep -xF -f set1 set2

$ sort set1 set2 | uniq -d

$ join -t <(sort A) <(sort B)

$ awk '!done { a[$0]; next }; $0 in a' set1 done=1 set2

Установить равенство

$ cmp -s <(sort set1) <(sort set2) # returns 0 if set1 is equal to set2
                                   # returns 1 if set1 != set2

$ cmp -s <(sort -u set1) <(sort -u set2)
# collapses multi-sets into sets and does the same as previous

$ awk '{ if (!($0 in a)) c++; a[$0] }; END{ exit !(c==NR/2) }' set1 set2
# returns 0 if set1 == set2
# returns 1 if set1 != set2

$ awk '{ a[$0] }; END{ exit !(length(a)==NR/2) }' set1 set2
# same as previous, requires >= gnu awk 3.1.5

Установить кардинальность

$ wc -l < set     # outputs number of elements in set

$ awk 'END { print NR }' set

$ sed '$=' set

Тест подмножества

$ comm -23 <(sort -u subset) <(sort -u set) | grep -q '^'
# returns true iff subset is not a subset of set (has elements not in set)

$ awk '!done { a[$0]; next }; { if !($0 in a) exit 1 }' set done=1 subset
# returns 0 if subset is a subset of set
# returns 1 if subset is not a subset of set

Установить Союз

$ cat set1 set2     # outputs union of set1 and set2
                    # assumes they are disjoint

$ awk 1 set1 set2   # ditto

$ cat set1 set2 ... setn   # union over n sets

$ sort -u set1 set2  # same, but doesn't assume they are disjoint

$ sort set1 set2 | uniq

$ awk '!a[$0]++' set1 set2       # ditto without sorting

Установить дополнение

$ comm -23 <(sort set1) <(sort set2)
# outputs elements in set1 that are not in set2

$ grep -vxF -f set2 set1           # ditto

$ sort set2 set2 set1 | uniq -u    # ditto

$ awk '!done { a[$0]; next }; !($0 in a)' set2 done=1 set1

Установить симметричную разницу

$ comm -3 <(sort set1) <(sort set2) | tr -d '\t'  # assumes not tab in sets
# outputs elements that are in set1 or in set2 but not both

$ sort set1 set2 | uniq -u

$ cat <(grep -vxF -f set1 set2) <(grep -vxF -f set2 set1)

$ grep -vxF -f set1 set2; grep -vxF -f set2 set1

$ awk '!done { a[$0]; next }; $0 in a { delete a[$0]; next }; 1;
       END { for (b in a) print b }' set1 done=1 set2

Набор питания

Все возможные подмножества набора отображают разделенное пространство, по одному на строку:

$ p() { [ "$#" -eq 0 ] && echo || (shift; p "$@") |
        while read r; do printf '%s %s\n%s\n' "$1" "$r" "$r"; done; }
$ p $(cat set)

(предполагается, что элементы не содержат SPC, TAB (предполагается значение по умолчанию $IFS), обратный слеш, символы подстановки).

Установить декартово произведение

$ while IFS= read -r a; do while IFS= read -r b; do echo "$a, $b"; done < set1; done < set2

$ awk '!done { a[$0]; next }; { for (i in a) print i, $0 }' set1 done=1 set2

Тест непересекающихся множеств

$ comm -12 <(sort set1) <(sort set2)  # does not output anything if disjoint

$ awk '++seen[$0] == 2 { exit 1 }' set1 set2 # returns 0 if disjoint
                                             # returns 1 if not

Тест пустого набора

$ wc -l < set            # outputs 0  if the set is empty
                         # outputs >0 if the set is not empty

$ grep -q '^' set        # returns true (0 exit status) unless set is empty

$ awk '{ exit 1 }' set   # returns true (0 exit status) if set is empty

минимальный

$ sort set | head -n 1   # outputs the minimum (lexically) element in the set

$ awk 'NR == 1 { min = $0 }; $0 < min { min = $0 }; END { print min }'
# ditto, but does numeric comparison when elements are numerical

максимальная

$ sort test | tail -n 1    # outputs the maximum element in the set

$ sort -r test | head -n 1

$ awk '$0 > max { max = $0 }; END { print max }'
# ditto, but does numeric comparison when elements are numerical

Все доступно на http://www.catonmat.net/blog/set-operations-in-unix-shell-simplified/

llhuii
источник
1
Я думаю, что версия Python намного проще и интуитивнее. ;-)
Кит
Я думаю, что это самый полный ответ. К сожалению, какие команды выполнять или какие аргументы (comm -12, -23, -13) в каждом случае не всегда интуитивно понятны как «пересечение» или «разница». Возможно создам обертку вокруг них, так как я всегда использую эти вещи.
Нилтон
Я запустил [pol @ localhost inst] $ grep -xc и INSTALL-BINARY 0 [pol @ localhost inst] $, но я не понимаю, что это значит. Слово «и» должно встречаться в файле много раз. Что я делаю неправильно?
Vérace
1
Пересечение множеств: sort set1 set2 | uniq -dне работает для множеств. Подумайте об использовании sort <(sort -u set1) <(sort -u set2) | uniq -d.
нео
11

Вроде, как бы, что-то вроде. Вам нужно разобраться с сортировкой самостоятельно, но для этого commможно использовать обработку каждой линии как элемента набора: -12для пересечения, -13для различия. (И -23дает вам перевернутую разницу, то есть set2 - set1вместо set1 - set2.) Union находится sort -uв этой настройке.

geekosaur
источник
1
Действительно, кажется, что комм делает большинство вещей. Хотя аргументы очень не интуитивны. Спасибо!
Нилтон
7

Я не знаю конкретного инструмента, но вы можете использовать Python, его набор классов и операторов, чтобы написать небольшой скрипт для этого.

Для экзамена:

Python> s1 = set(os.listdir("/bin"))
Python> s2 = set(os.listdir("/usr/bin"))
Python> s1 & s2

set(['awk',
     'basename',
     'chroot', ...
Кит
источник
Да, хороший ответ. Зачем использовать awk, если Python доступен?
Геттли
Вы забыли:Python> import os
Джеймс Бауэри
7

Крошечный консольный инструмент «setop» теперь доступен в Debian Stretch и в Ubuntu с 16.10. Вы можете получить это через sudo apt install setop

Вот несколько примеров. Наборы, с которыми нужно работать, представлены в виде разных входных файлов: setop input # is equal to "sort input --unique" setop file1 file2 --union # option --union is default and can be omitted setop file1 file2 file3 --intersection # more than two inputs are allowed setop file1 - --symmetric-difference # ndash stands for standard input setop file1 -d file2 # all elements contained in 1 but not 2

Булевы запросы возвращаются только EXIT_SUCCESSв случае true, EXIT_FAILUREа также в противном случае сообщения. Таким образом, setop можно использовать в оболочке. setop inputfile --contains "value" # is element value contained in input? setop A.txt B.txt --equal C.txt # union of A and B equal to C? setop bigfile --subset smallfile # analogous --superset setop -i file1 file2 --is-empty # intersection of 1 and 2 empty (disjoint)?

Также возможно точно описать, как входные потоки должны быть проанализированы, фактически с помощью регулярных выражений:

  • setop input.txt --input-separator "[[:space:]-]"означает, что пробел (то есть \v \t \n \r \fили пробел) или знак минус интерпретируется как разделитель между элементами (по умолчанию - новая строка, т.е. каждая строка входного файла является одним элементом)
  • setop input.txt --input-element "[A-Za-z]+" означает, что элементы - это только слова, состоящие из латинских символов, все остальные символы считаются разделителями между элементами

Кроме того, вы можете

  • --count все элементы выходного набора,
  • --trim все входные элементы (т.е. стереть все ненужные предшествующие и последующие символы, такие как пробел, запятая и т. д.),
  • рассмотреть пустые элементы , как действует через --include-empty,
  • --ignore-case,
  • установить --output-separatorмежду элементами выходного потока (по умолчанию \n),
  • и так далее.

Смотрите man setopили github.com/phisigma/setop для получения дополнительной информации.

Фрэнк
источник
3

Если вы видите файл в виде набора строк, и файлы отсортированы, есть comm.

Если вы видите файл как (мульти) набор линий, и строки не отсортированы, вы grepможете различать и пересекать (это достигает разности и пересечения наборов, но не учитывает счет для мультимножеств). Союз это просто cat.

grep -xF -f small large >intersection
grep -vxF -f small large >difference
cat small large >union
жилль
источник
2

Я создал утилиту Python, которая может выполнять линейное объединение, пересечение, различие и произведение нескольких файлов. Он называется SetOp, вы можете найти его в PyPI ( здесь ). Синтаксис выглядит так:

$ setop -i file1 file2 file3  # intersection
$ setop -d file1 file2 file3  # difference
Tigr
источник
1

Я написал небольшой инструмент для этого, который был весьма полезен для меня в разных местах. Пользовательский интерфейс не полирован, и я не уверен насчет характеристик производительности для очень больших файлов (поскольку он считывает весь список в память), но «он работает для меня». Программа находится по адресу https://github.com/nibrahim/lines . Это на Python. Вы можете получить это используя pip install lines.

В настоящее время он поддерживает объединение, пересечение, разность и симметричную разность двух файлов. Каждая строка входного файла обрабатывается как элемент набора.

У этого также есть две дополнительные операции. Одна из них - выжать пустые строки в файле, а вторая (что было очень полезно для меня) - просмотреть файл и разделить его на наборы похожих строк. Мне нужно было это для поиска файлов в списке, которые не соответствуют общему шаблону.

Буду рад обратной связи.

Нуфал Ибрагим
источник
0

Файловая система рассматривает имена файлов (целые имена файлов, включая пути) как уникальные.

Операции?

Вы можете скопировать файлы в / и b / в пустой каталог c /, чтобы получить новый объединенный набор.

С помощью файловых тестов, таких как -e nameи циклы или находки, вы можете проверить наличие файлов в двух или более каталогах, чтобы получить пересечение или разницу.

неизвестный пользователь
источник
1
Я имел в виду трактовать содержимое файлов как элементы набора (скажем, один элемент в строке), а сами файлы как наборы.
Нилтон
0

Лучший ответ здесь: Setdown (специальный инструмент)

Я написал программу под названием setdown, которая выполняет операции Set из cli.

Он может выполнять операции над множествами, написав определение, похожее на то, что вы написали бы в Makefile:

someUnion: "file-1.txt" \/ "file-2.txt"
someIntersection: "file-1.txt" /\ "file-2.txt"
someDifference: someUnion - someIntersection

Это довольно круто, и вы должны это проверить. Лично я не рекомендую использовать специальные команды, которые не были созданы для выполнения операции над множествами. Это не будет работать хорошо, когда вам действительно нужно выполнять много операций над множествами или если у вас есть какие-либо операции над множествами, которые зависят друг от друга , Кроме того, setdown позволяет вам писать операции над множествами, которые зависят от других операций над множествами!

Во всяком случае, я думаю, что это довольно круто, и вы должны полностью проверить это.

Роберт Массайоли
источник
0

Пример шаблона для нескольких файлов (в данном случае это пересечение):

eval `perl -le 'print "cat ",join(" | grep -xF -f- ", @ARGV)' t*`

Расширяется до:

cat t1 | grep -xF -f- t2 | grep -xF -f- t3

Тестовые файлы:

seq 0 20 | tee t1; seq 0 2 20 | tee t2; seq 0 3 20 | tee t3

Выход:

0
6
12
18
BSB
источник
0

С zshмассивами ( zshмассивы могут содержать любую произвольную последовательность байтов, даже 0).

(также обратите внимание, что вы можете сделать, typeset -U arrayчтобы гарантировать, что его элементы уникальны).

установить членство

if ((${array[(Ie)$element]})); then
  echo '$element is in $array'
fi

(используя Iфлаг индекса массива, чтобы получить индекс последнего вхождения $elementв массиве (или 0, если не найден). Удалить e(для exact), $elementчтобы быть принятым в качестве шаблона)

if ((n = ${(M)#array:#$element})); then
  echo "\$element is found $n times in \$array'
fi

${array:#pattern}будучи вариацией ksh, ${var#pattern}которая удаляет элементы, которые соответствуют шаблону, а не просто удаляет ведущую часть, которая соответствует шаблону. Символ (M)(для сопоставленного ) меняет значение и удаляет все элементы, кроме сопоставленных (используйте $~elementего как шаблон).

установить пересечение

common=("${(@)set1:*set2}")

${set1:*set2}выполняет пересечение массива, но "${(@)...}"синтаксис необходим для сохранения пустых элементов.

установить равенство

[[ ${(j: :)${(q)array1}} = ${(j: :)${(q)array2}} ]]

Проверяет, идентичны ли массивы (и в том же порядке). qФлаг расширения параметра приводит элементы (чтобы избежать проблем с вещами , как a=(1 "2 3")против b=("1 2" 3)), и (j: :)присоединяется к ним с пространством , прежде чем делать сравнения строк.

Чтобы проверить, что они имеют одинаковые элементы, независимо от порядка, используйте oфлаг, чтобы упорядочить их. Смотрите также uфлаг (уникальный) для удаления дубликатов.

[[ ${(j: :)${(qo)array1}} = ${(j: :)${(qo)array2}} ]]

установить мощность

n=$#array

тест подмножества

if ((${#array1:*array2} == ${#array2})); then
  echo '$array2 is included in $array1'
fi

союз

union=("$array1[@]" "$array2[@]")

(см. typeset -Uвыше или uфлаг расширения параметра для учета дубликатов). Опять же, если пустая строка не является одним из возможных значений, вы можете упростить до:

union=($array1 $array2)

дополнение

complement=("${(@)array1:|array2}")

для элементов $array1этого нет в $array2.

минимум / максимум (лексическое сравнение)

min=${${(o)array}[1]} max=${${(o)array}[-1]}

минимум / максимум (сравнение десятичных целых)

min=${${(no)array}[1]} max=${${(no)array}[-1]}
Стефан Шазелас
источник