Ferramentas do Linux para tratar arquivos como conjuntos e executar operações definidas neles

77

Alguém sabe de alguma ferramenta do Linux especificamente projetada para tratar arquivos como conjuntos e executar operações definidas neles? Como diferença, intersecção, etc?

    
por nilton 14.04.2011 / 23:19

11 respostas

103

Supondo que elementos são strings de caracteres diferentes de NUL e newline (tenha em mente que newline é válido em nomes de arquivos), você pode representar um set como um arquivo de texto com um elemento por linha e usar alguns dos utilitários padrão do Unix.

Definir associação

$ 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 }'

Definir interseção

$ 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

Definir igualdade

$ 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

Definir cardinalidade

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

$ awk 'END { print NR }' set

$ sed '$=' set

Teste de subconjunto

$ 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

Definir união

$ 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

Definir complemento

$ 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

Definir diferença simétrica

$ 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

Conjunto de energia

Todos os subconjuntos possíveis de um conjunto de espaços exibidos separados, um por linha:

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

(assume que os elementos não contêm SPC, TAB (assumindo o valor padrão de $IFS ), barra invertida, caracteres curinga).

Definir produto cartesiano

$ 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

Teste de Conjunto Disjuntivo

$ 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

Teste de conjunto vazio

$ 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

Mínimo

$ 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

Máximo

$ 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

Todos disponíveis no link

    
por 20.04.2011 / 03:37
11

Classificar de. Você precisa lidar com a classificação, mas comm pode ser usado para fazer isso, tratando cada linha como um membro do conjunto: -12 para interseção, -13 para diferença. (E -23 dá a você uma diferença invertida, ou seja, set2 - set1 em vez de set1 - set2 .) União é sort -u nessa configuração.

    
por 14.04.2011 / 23:24
7

Eu não sei de uma ferramenta específica, mas você pode usar o Python, e sua classe e operadores, para escrever um pequeno script para fazer isso.

Por exemplo:

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

set(['awk',
     'basename',
     'chroot', ...
    
por 15.04.2011 / 02:56
6

A minúscula ferramenta de console “setop” agora está disponível no Debian Stretch e no Ubuntu desde 16.10. Você pode obtê-lo via sudo apt-get install setop

Aqui estão alguns exemplos. Os conjuntos a serem operados são dados como arquivos de entrada diferentes:

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

As consultas booleanas retornam apenas EXIT_SUCCESS (= 0) no caso de true e um valor de erro, bem como uma mensagem de outra forma. Dessa forma, o setop pode ser usado no 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)?

Também é possível descrever com precisão como os fluxos de entrada devem ser analisados, na verdade, por expressões regulares:

  • setop input.txt --input-separator "[[:space:]-]" significa que um espaço em branco (ou seja, \v \t \n \r \f ou espaço) ou um sinal de menos é interpretado como um separador entre elementos (o padrão é nova linha, isto é, cada linha da entrada arquivo é um elemento)
  • setop input.txt --input-element "[A-Za-z]+" significa que os elementos são apenas palavras que consistem em caracteres latinos, todos os outros caracteres são considerados separadores entre elementos

Além disso, você pode

  • --count todos os elementos do conjunto de saída,
  • --trim todos os elementos de entrada (isto é, apagar todos os caracteres anteriores e posteriores não desejados, como espaço, vírgula, etc.),
  • considere elementos vazios como válidos por meio de --include-empty ,
  • --ignore-case ,
  • define o --output-separator entre os elementos do fluxo de saída (o padrão é \n ),
  • e assim por diante.

Veja man setop ou github.com/phisigma/setop para mais informações.

    
por 25.07.2017 / 12:18
3

Se você vir um arquivo como um conjunto de linhas e os arquivos estiverem ordenados, haverá comm .

Se você vir um arquivo como um (multi) conjunto de linhas e as linhas não estiverem classificadas, grep poderá fazer diferença e interseção (ele atingirá a diferença e a interseção definidas, mas não respeitará a contagem de multiconfícios) . União é apenas cat .

grep -xF -f small large >intersection
grep -vxF -f small large >difference
cat small large >union
    
por 15.04.2011 / 01:23
2

Eu criei um utilitário Python que pode fazer união, intersecção, diferença e produto de vários arquivos. Chama-se SetOp, você pode encontrá-lo em PyPI ( aqui ). A sintaxe é assim:

$ setop -i file1 file2 file3  # intersection
$ setop -d file1 file2 file3  # difference
    
por 13.05.2012 / 03:51
1

Eu escrevi uma pequena ferramenta para fazer isso que foi bastante útil para mim em vários lugares. A interface do usuário não é polida e não tenho certeza sobre as características de desempenho de arquivos muito grandes (já que lê toda a lista na memória), mas "funciona para mim". O programa está no link . Está em Python. Você pode obtê-lo usando pip install lines .

Atualmente suporta união, interseção, diferença e diferença simétrica de dois arquivos. Cada linha do arquivo de entrada é tratada como um elemento de um conjunto.

Ele também tem duas operações extras. Um de espremer linhas em branco em um arquivo eo segundo (que tem sido muito útil para mim) é olhar através do arquivo e dividi-lo em conjuntos de seqüências de caracteres semelhantes. Eu precisava disso para procurar por arquivos em uma lista que não correspondesse ao padrão geral.

Eu gostaria de receber feedback.

    
por 10.11.2013 / 09:45
0

O sistema de arquivos trata nomes de arquivos (nomes de arquivos completos, incluindo caminhos) como únicos.

Operações?

Você pode copiar os arquivos em a / eb / para o diretório vazio c /, para obter um novo conjunto de união.

Com testes de arquivo como -e name e loops ou find, você pode verificar os arquivos existentes em dois ou mais diretórios, para obter a interseção ou a diferença.

    
por 20.04.2011 / 07:40
0

Melhor resposta aqui: Setdown (uma ferramenta dedicada)

Eu escrevi um programa chamado setdown que executa as operações Set do cli.

Ele pode realizar operações de conjunto escrevendo uma definição semelhante à que você escreveria em um Makefile:

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

É bem legal e você deveria dar uma olhada. Eu pessoalmente não recomendo usar comandos ad-hoc que não foram criados para que o trabalho execute operações de conjunto. Não funcionará bem quando você realmente precisar executar muitas operações de configuração ou se tiver operações definidas que dependam umas das outras. . Não só isso, mas o setdown permite que você escreva operações que dependem de outras operações definidas!

De qualquer forma, acho que é muito legal e você deve checar totalmente.

    
por 30.01.2015 / 03:48
0

Exemplo de padrão para vários arquivos (intersecção, neste caso):

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

Expande para:

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

Arquivos de teste:

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

Saída:

0
6
12
18
    
por 15.08.2015 / 01:21
0

Com zsh arrays ( zsh arrays pode conter qualquer sequência arbitrária de bytes, até 0).

(observe também que você pode fazer typeset -U array para garantir que seus elementos sejam únicos).

definir associação

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

(usando o sinalizador% subscrito da matriz I , para obter o índice da última ocorrência de $element na matriz (ou 0, se não encontrado). Remover e (para e xact) para $element a ser tomado como um padrão)

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

${array:#pattern} é uma variação do ${var#pattern} do ksh que remove os elementos que correspondem ao padrão, em oposição a apenas remover a parte principal que corresponde ao padrão. O (M) (para correspondido ) inverte o significado e remove todos, menos os elementos correspondentes (use $~element para que seja tomado como um padrão).

interseção de conjunto

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

${set1:*set2} faz a interseção da matriz, mas a sintaxe "${(@)...}" é necessária para preservar os elementos vazios.

definir igualdade

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

Testa se as matrizes são idênticas (e na mesma ordem). O sinalizador de expansão do parâmetro q cita os elementos (para evitar problemas com coisas como a=(1 "2 3") vs b=("1 2" 3) ) e (j: :) os associa a espaço antes de fazer uma comparação de cadeia de caracteres.

Para verificar se eles têm os mesmos elementos, independentemente do pedido, use o sinalizador o para encomendá-los. Veja também o u flag (unique) para remover duplicatas.

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

definir cardinalidade

n=$#array

teste de subconjunto

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

union

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

(consulte typeset -U acima ou o sinalizador de expansão do parâmetro u para obter o caso de duplicatas). Novamente, se a string vazia não for um dos valores possíveis, você pode simplificar para:

union=($array1 $array2)

complemento

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

para os elementos de $array1 que não estão em $array2 .

mínimo / máximo (comparação lexical)

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

mínimo / máximo (comparação de números inteiros decimais)

min=${${(no)array}[1]} max=${${(no)array}[-1]}
    
por 17.05.2018 / 23:32