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


81

Хтось знає про будь-який інструмент Linux, спеціально розроблений для розгляду файлів як наборів та виконання операцій із встановленням на них? Як різниця, перехрестя тощо?

Відповіді:


110

Якщо припустити, що елементи є рядками символів, відмінними від NUL та newline (будьте обережні, що новий рядок дійсний у назвах файлів), ви можете представляти набір у текстовому файлі з одним елементом у рядку та використовувати деякі стандартні утиліти 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/


1
Я думаю, що версія Python набагато простіша та інтуїтивніша. ;-)
Кіт

Я думаю, що це найповніша відповідь. На жаль, які команди для запуску або які аргументи (comm -12, -23, -13) у кожному випадку не завжди є інтуїтивно зрозумілими як "перетин" або "різниця". Можливо, створіть навколо них обгортку, оскільки я завжди використовую ці речі.
nilton

Я побіг [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.) Союз є sort -uв цій установці.


1
Дійсно, кому здається, що це робить більшість матеріалів. Хоча аргументи дуже неінтуїтивні. Дякую!
nilton

7

Я не знаю конкретного інструменту, але ви можете використовувати Python та його набір класів та операторів, щоб написати невеликий сценарій для цього.

Для іспиту:

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

set(['awk',
     'basename',
     'chroot', ...

Так, приємна відповідь. Навіщо використовувати awk, якщо доступний python?
guettli

Ви забули: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у випадку істинного, EXIT_FAILUREа також повідомлення в іншому випадку. Таким чином, сетоп можна використовувати в оболонці. 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

1

Я написав невеликий інструмент для цього, який був мені дуже корисний у різних місцях. Користувацький інтерфейс не є полірованим, і я не впевнений у характеристиках продуктивності дуже великих файлів (оскільки він читає весь список у пам'яті), але "він працює для мене". Програма знаходиться за адресою https://github.com/nibrahim/lines . Це в Python. Ви можете отримати це за допомогою pip install lines.

В даний час він підтримує об'єднання, перетин, різницю та симетричну різницю двох файлів. Кожен рядок вхідного файлу трактується як елемент набору.

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

Я буду вітаю відгуки.


0

Файлова система розглядає назви файлів (цілі назви файлів, включаючи шляхи) як унікальні.

Операції?

Ви можете скопіювати файли в / і b / в порожній каталог c /, щоб отримати новий, об'єднаний набір.

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


1
Я мав на увазі трактувати вміст файлів як елементи набору (скажімо, один елемент у рядку), а самі файли - як набори.
nilton

0

Найкраща відповідь тут: Setdown (спеціальний інструмент)

Я написав програму під назвою setdown, яка виконує операції Set з cli.

Він може виконувати задані операції, записавши визначення, подібне до того, яке ви б написали у Makefile:

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

Це досить круто, і ви повинні перевірити це. Я особисто не рекомендую використовувати спеціальні команди, які не були створені для завдання для виконання заданих операцій. Він не працюватиме добре, коли вам потрібно виконати багато операцій набору або якщо у вас є якісь задані операції, які залежать один від одного. . Мало того, але налаштування дозволяє писати задані операції, які залежать від інших заданих операцій!

У будь-якому випадку, я думаю, що це досить круто, і ви повинні повністю перевірити це.


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

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]}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.