Ответы:
Предполагая, что элементы - это строки символов, отличные от 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/
sort set1 set2 | uniq -d
не работает для множеств. Подумайте об использовании sort <(sort -u set1) <(sort -u set2) | uniq -d
.
Вроде, как бы, что-то вроде. Вам нужно разобраться с сортировкой самостоятельно, но для этого comm
можно использовать обработку каждой линии как элемента набора: -12
для пересечения, -13
для различия. (И -23
дает вам перевернутую разницу, то есть set2 - set1
вместо set1 - set2
.) Union находится sort -u
в этой настройке.
Я не знаю конкретного инструмента, но вы можете использовать Python, его набор классов и операторов, чтобы написать небольшой скрипт для этого.
Для экзамена:
Python> s1 = set(os.listdir("/bin"))
Python> s2 = set(os.listdir("/usr/bin"))
Python> s1 & s2
set(['awk',
'basename',
'chroot', ...
Python> import os
Крошечный консольный инструмент «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 для получения дополнительной информации.
Если вы видите файл в виде набора строк, и файлы отсортированы, есть comm
.
Если вы видите файл как (мульти) набор линий, и строки не отсортированы, вы grep
можете различать и пересекать (это достигает разности и пересечения наборов, но не учитывает счет для мультимножеств). Союз это просто cat
.
grep -xF -f small large >intersection
grep -vxF -f small large >difference
cat small large >union
Я создал утилиту Python, которая может выполнять линейное объединение, пересечение, различие и произведение нескольких файлов. Он называется SetOp, вы можете найти его в PyPI ( здесь ). Синтаксис выглядит так:
$ setop -i file1 file2 file3 # intersection
$ setop -d file1 file2 file3 # difference
Я написал небольшой инструмент для этого, который был весьма полезен для меня в разных местах. Пользовательский интерфейс не полирован, и я не уверен насчет характеристик производительности для очень больших файлов (поскольку он считывает весь список в память), но «он работает для меня». Программа находится по адресу https://github.com/nibrahim/lines . Это на Python. Вы можете получить это используя pip install lines
.
В настоящее время он поддерживает объединение, пересечение, разность и симметричную разность двух файлов. Каждая строка входного файла обрабатывается как элемент набора.
У этого также есть две дополнительные операции. Одна из них - выжать пустые строки в файле, а вторая (что было очень полезно для меня) - просмотреть файл и разделить его на наборы похожих строк. Мне нужно было это для поиска файлов в списке, которые не соответствуют общему шаблону.
Буду рад обратной связи.
Файловая система рассматривает имена файлов (целые имена файлов, включая пути) как уникальные.
Операции?
Вы можете скопировать файлы в / и b / в пустой каталог c /, чтобы получить новый объединенный набор.
С помощью файловых тестов, таких как -e name
и циклы или находки, вы можете проверить наличие файлов в двух или более каталогах, чтобы получить пересечение или разницу.
Лучший ответ здесь: Setdown (специальный инструмент)
Я написал программу под названием setdown, которая выполняет операции Set из cli.
Он может выполнять операции над множествами, написав определение, похожее на то, что вы написали бы в Makefile:
someUnion: "file-1.txt" \/ "file-2.txt"
someIntersection: "file-1.txt" /\ "file-2.txt"
someDifference: someUnion - someIntersection
Это довольно круто, и вы должны это проверить. Лично я не рекомендую использовать специальные команды, которые не были созданы для выполнения операции над множествами. Это не будет работать хорошо, когда вам действительно нужно выполнять много операций над множествами или если у вас есть какие-либо операции над множествами, которые зависят друг от друга , Кроме того, setdown позволяет вам писать операции над множествами, которые зависят от других операций над множествами!
Во всяком случае, я думаю, что это довольно круто, и вы должны полностью проверить это.
Пример шаблона для нескольких файлов (в данном случае это пересечение):
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
С zsh
массивами ( zsh
массивы могут содержать любую произвольную последовательность байтов, даже 0).
(также обратите внимание, что вы можете сделать, typeset -U array
чтобы гарантировать, что его элементы уникальны).
if ((${array[(Ie)$element]})); then
echo '$element is in $array'
fi
(используя I
флаг индекса массива, чтобы получить индекс последнего вхождения $element
в массиве (или 0, если не найден). Удалить e
(для e
xact), $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]}