Réponses:
En supposant que les éléments soient des chaînes de caractères autres que NUL et newline (sachez que newline est valide dans les noms de fichier), vous pouvez représenter un ensemble sous forme de fichier texte avec un élément par ligne et utiliser certains des utilitaires Unix standard.
$ 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
Tous les sous-ensembles possibles d’un ensemble d’espaces affichés séparés, un par ligne:
$ p() { [ "$#" -eq 0 ] && echo || (shift; p "$@") |
while read r; do printf '%s %s\n%s\n' "$1" "$r" "$r"; done; }
$ p $(cat set)
(suppose que les éléments ne contiennent pas SPC, TAB (en supposant la valeur par défaut $IFS
), barre oblique inverse, caractères génériques).
$ 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
Tous disponibles à http://www.catonmat.net/blog/set-operations-in-unix-shell-simplified/
sort set1 set2 | uniq -d
ne fonctionne pas pour les ensembles multiples. Pensez à utiliser sort <(sort -u set1) <(sort -u set2) | uniq -d
.
Sorte de. Vous devez gérer vous-même le tri, mais vous comm
pouvez l'utiliser pour traiter chaque ligne comme un membre du groupe: -12
intersection, -13
différence. (Et -23
vous donne une différence inversée, c'est-à-dire set2 - set1
au lieu de set1 - set2
.) Union est sort -u
dans cette configuration.
Je ne connais pas d'outil spécifique, mais vous pouvez utiliser Python, sa classe de jeu et ses opérateurs, pour écrire un petit script.
Pour exampe:
Python> s1 = set(os.listdir("/bin"))
Python> s2 = set(os.listdir("/usr/bin"))
Python> s1 & s2
set(['awk',
'basename',
'chroot', ...
Python> import os
Le petit outil de console «setop» est maintenant disponible dans Debian Stretch et Ubuntu depuis le 16.10. Vous pouvez l'obtenir via
sudo apt install setop
Voici quelques exemples. Les ensembles à utiliser sont donnés sous forme de fichiers d’entrée différents:
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
Les requêtes booléennes ne renvoient que EXIT_SUCCESS
dans le cas de true et, EXIT_FAILURE
sinon, dans un message. De cette façon, setop peut être utilisé dans le shell.
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)?
Il est également possible de décrire précisément comment les flux d'entrée doivent être analysés, en fait par des expressions régulières:
setop input.txt --input-separator "[[:space:]-]"
signifie qu'un espace ( \v
\t
\n
\r
\f
ou espace) ou un signe moins est interprété comme un séparateur entre les éléments (par défaut, nouvelle ligne, autrement dit, chaque ligne du fichier d'entrée correspond à un élément)setop input.txt --input-element "[A-Za-z]+"
signifie que les éléments ne sont que des mots composés de caractères latins, tous les autres caractères sont considérés comme des séparateurs entre les élémentsDe plus, vous pouvez
--count
tous les éléments du jeu de sortie,--trim
tous les éléments d'entrée (c.-à-d. effacez tous les caractères précédents et suivants indésirables comme l'espace, les virgules, etc.),--include-empty
,--ignore-case
,--output-separator
entre les éléments du flux de sortie (la valeur par défaut est \n
),Voir man setop
ou github.com/phisigma/setop pour plus d'informations.
Si vous voyez un fichier comme un ensemble de lignes, et les fichiers sont triés, il y a comm
.
Si vous voyez un fichier comme un (plusieurs) ensemble de lignes et que les lignes ne sont pas triées, vous grep
pouvez créer une différence et une intersection (cela permet d'obtenir une différence et une intersection définies, mais ne respecte pas le décompte des multisets). L'union est juste cat
.
grep -xF -f small large >intersection
grep -vxF -f small large >difference
cat small large >union
J'ai créé un utilitaire Python capable de créer des unions, des intersections, des différences et des produits de plusieurs fichiers. Il s’appelle SetOp, vous pouvez le trouver sur PyPI ( ici ). La syntaxe ressemble à ceci:
$ setop -i file1 file2 file3 # intersection
$ setop -d file1 file2 file3 # difference
J'ai écrit un petit outil pour le faire qui m'a été très utile à divers endroits. L'interface utilisateur n'est pas polie et je ne suis pas sûr des caractéristiques de performance pour les très gros fichiers (puisqu'il lit toute la liste en mémoire), mais "ça marche pour moi". Le programme est disponible sur https://github.com/nibrahim/lines . C'est en Python. Vous pouvez l'obtenir en utilisant pip install lines
.
Il supporte actuellement l'union, l'intersection, la différence et la différence symétrique de deux fichiers. Chaque ligne du fichier d'entrée est traitée comme un élément d'un ensemble.
Il a également deux opérations supplémentaires. Une des lignes vides dans un fichier et la seconde (qui m’a été très utile) consiste à parcourir le fichier et à le diviser en ensembles de chaînes similaires. J'avais besoin de cela pour rechercher des fichiers dans une liste qui ne correspondait pas au modèle général.
Je serais heureux de recevoir vos commentaires.
Le système de fichiers considère les noms de fichiers (noms de fichiers entiers, y compris les chemins) comme uniques.
Des opérations?
Vous pouvez copier les fichiers de / et b / dans le répertoire vide c / pour obtenir un nouveau jeu d’unions.
Avec des tests de fichiers tels que -e name
et des boucles ou find, vous pouvez rechercher des fichiers existant dans plusieurs répertoires afin d’obtenir l’intersection ou la différence.
Meilleure réponse ici: Setdown (un outil dédié)
J'ai écrit un programme appelé setdown qui effectue les opérations Set à partir de la CLI.
Il peut effectuer des opérations sur les ensembles en écrivant une définition similaire à celle que vous écririez dans un Makefile:
someUnion: "file-1.txt" \/ "file-2.txt"
someIntersection: "file-1.txt" /\ "file-2.txt"
someDifference: someUnion - someIntersection
C'est plutôt cool et vous devriez y jeter un coup d'œil. Personnellement, je ne recommande pas l’utilisation de commandes ad-hoc qui n’ont pas été créées pour que le travail puisse exécuter des opérations définies. . Non seulement cela, mais setdown vous permet d'écrire des opérations de set qui dépendent d'autres opérations de set!
En tout cas, je pense que c'est plutôt cool et que tu devrais le vérifier totalement.
Avec les zsh
tableaux (les zsh
tableaux peuvent contenir n’importe quelle séquence d’octets, même 0).
(notez également que vous pouvez faire en sorte que typeset -U array
ses éléments soient uniques).
if ((${array[(Ie)$element]})); then
echo '$element is in $array'
fi
(en utilisant le I
drapeau indice de tableau, pour obtenir l'index de la dernière occurrence de $element
dans le tableau (ou 0 si non trouvé). Remove e
(pour e
xact) pour $element
être pris comme modèle)
if ((n = ${(M)#array:#$element})); then
echo "\$element is found $n times in \$array'
fi
${array:#pattern}
étant une variation sur ksh ${var#pattern}
qui supprime les éléments qui correspondent au motif plutôt que de simplement supprimer la partie principale qui correspond au motif. Le paramètre(M)
(pour la correspondance ) inverse le sens et supprime tous les éléments sauf les éléments correspondants (utilisez-le $~element
pour qu'il soit pris comme motif).
common=("${(@)set1:*set2}")
${set1:*set2}
effectue l'intersection du tableau, mais la "${(@)...}"
syntaxe est nécessaire pour conserver les éléments vides.
[[ ${(j: :)${(q)array1}} = ${(j: :)${(q)array2}} ]]
Teste si les tableaux sont identiques (et dans le même ordre). Le q
paramètre de développement de paramètre cite les éléments (pour éviter les problèmes liés à des choses comme a=(1 "2 3")
vs b=("1 2" 3)
), et les (j: :)
joint à l’espace avant de procéder à une comparaison de chaîne.
Pour vérifier qu'ils ont les mêmes éléments, quel que soit leur ordre, utilisez le o
drapeau pour les commander. Voir aussi le u
drapeau (unique) pour supprimer les doublons.
[[ ${(j: :)${(qo)array1}} = ${(j: :)${(qo)array2}} ]]
n=$#array
if ((${#array1:*array2} == ${#array2})); then
echo '$array2 is included in $array1'
fi
union=("$array1[@]" "$array2[@]")
(voir typeset -U
ci - dessus ou l' u
indicateur d'expansion de paramètre pour prendre en compte les doublons). Encore une fois, si la chaîne vide ne fait pas partie des valeurs possibles, vous pouvez simplifier pour:
union=($array1 $array2)
complement=("${(@)array1:|array2}")
pour les éléments de $array1
ce ne sont pas dans $array2
.
min=${${(o)array}[1]} max=${${(o)array}[-1]}
min=${${(no)array}[1]} max=${${(no)array}[-1]}