Como saber se um arquivo de texto é um subconjunto de outro

12

Estou tentando encontrar uma maneira de determinar se um arquivo de texto é um subconjunto de outro ..

Por exemplo:

foo
bar

é um subconjunto de

foo
bar
pluto

Enquanto:

foo
pluto

e

foo
bar

não são um subconjunto um do outro ...

Existe uma maneira de fazer isso com um comando?

Essa verificação deve ser uma verificação cruzada e deve retornar:

file1 subset of file2 :    True
file2 subset of file1 :    True
otherwise             :    False
gc5
fonte
Solução potencialmente mais eficiente (se os arquivos também forem solicitados): github.com/barrycarter/bcapps/blob/master/…
barrycarter

Respostas:

11

Se esses conteúdos de arquivo são chamados file1, file2e file3a fim de apearance então você pode fazê-lo com o one-liner seguinte:

 # python -c "x=open('file1').read(); y=open('file2').read(); print x in y or y in x"
 True
 # python -c "x=open('file2').read(); y=open('file1').read(); print x in y or y in x"
 True
 # python -c "x=open('file1').read(); y=open('file3').read(); print x in y or y in x"
 False
Timo
fonte
Obrigado pela sua resposta .. +1 .. Não sei se aceito minha resposta porque a sua não é específica para unix-linux e minha resposta é um pouco mais rápida, tanto quanto eu testei .. o que você acha?
Gc5
Bem-vindo, é claro que existem outras soluções com mais ferramentas específicas para unix. Mas isso parece ser um bom uso do inoperador do Python .
Timo
Existe um wrapper de linha de comando python para torná-lo mais unix, com a tubulação incorporada, chamada pyp: code.google.com/p/pyp . Acho que é trivial tornar essa solução mais unix como uma ferramenta de liner.
IBr
3

Com perl:

if perl -0777 -e '$n = <>; $h = <>; exit(index($h,$n)<0)' needle.txt haystack.txt
then echo needle.txt is found in haystack.txt
fi

-0octaldefine o delimitador de registro. Quando esse número octal é maior que 0377 (o valor máximo de bytes), significa que não há delimitador, é equivalente a fazer $/ = undef. Nesse caso, <>retorna o conteúdo completo de um único arquivo, que é o modo slurp .

Uma vez que tenhamos o conteúdo dos arquivos em dois $he $nvariáveis, podemos usar index()para determinar se um é encontrado no outro.

Isso significa, no entanto, que os arquivos inteiros são armazenados na memória, o que significa que esse método não funcionará para arquivos muito grandes.

Para arquivos mmappable (geralmente inclui arquivos regulares e os arquivos mais procuráveis, como dispositivos de bloco), isso pode ser contornado usando mmap()os arquivos, como no Sys::Mmapmódulo perl:

if 
  perl -MSys::Mmap -le '
    open N, "<", $ARGV[0] || die "$ARGV[0]: $!";
    open H, "<", $ARGV[1] || die "$ARGV[1]: $!";
    mmap($n, 0, PROT_READ, MAP_SHARED, N);
    mmap($h, 0, PROT_READ, MAP_SHARED, H);
    exit (index($h, $n) < 0)' needle.txt haystack.txt
then
  echo needle.txt is found in haystack.txt
fi
Stéphane Chazelas
fonte
2

Encontrei uma solução graças a esta pergunta

Basicamente, estou testando dois arquivos a.txte b.txtcom este script:

#!/bin/bash

first_cmp=$(diff --unchanged-line-format= --old-line-format= --new-line-format='%L' "$1" "$2" | wc -l)
second_cmp=$(diff --unchanged-line-format= --old-line-format= --new-line-format='%L' "$2" "$1" | wc -l)

if [ "$first_cmp" -eq "0" -o "$second_cmp" -eq "0" ]
then
    echo "Subset"
    exit 0
else
    echo "Not subset"
    exit 1
fi

Se um é um subconjunto do outro, o script retorna 0para o Truecontrário 1.

gc5
fonte
O que% L ​​faz? Este script não parece trabalho, e eu estou tentando depurá-lo ...
Alex
Na verdade, não me lembro do significado %Ldisso, foi há três anos. De man diff(versão atual) %Lsignifica "conteúdo da linha".
gc5 24/05
% L imprime o conteúdo da "nova" linha. IOW, não imprima nada para linhas inalteradas ou antigas, mas imprima o conteúdo da linha para novas linhas.
PLG 26/09
Este script funciona para mim, pronto para uso!
PLG 26/09
2

Se f1 é um subconjunto de f2, então f1 - f2 é um conjunto vazio. Com base nisso, podemos escrever uma função is_subset e uma função derivada dela. Conforme definido Diferença entre 2 arquivos de texto


sort_files () {
  f1_sorted = "$ 1. sortido"
  f2_sorted = "$ 2. sortido"

  E se [ ! -f $ f1_sorted]; então
    gato $ 1 | classificar | uniq> $ f1_sorted
  fi

  E se [ ! -f $ f2_sorted]; então
    gato $ 2 | classificar | uniq> $ f2_sorted
  fi
}

remove_sorted_files () {
  f1_sorted = "$ 1. sortido"
  f2_sorted = "$ 2. sortido"
  rm -f $ f1_sorted
  rm -f $ f2_sorted
}

set_union () {
  sort_files $ 1 $ 2
  gato "$ 1. sortido" "$ 2. sortido" | classificar | uniq
  remove_sorted_files $ 1 $ 2
}

set_diff () {
  sort_files $ 1 $ 2
  gato "$ 1. sortido" "$ 2. sortido" "$ 2. sortido" | classificar | uniq -u
  remove_sorted_files $ 1 $ 2
}

rset_diff () {
  sort_files $ 1 $ 2
  gato "$ 1. sortido" "$ 2. sortido" "$ 1. sortido" | classificar | uniq -u
  remove_sorted_files $ 1 $ 2
}

is_subset () {
  sort_files $ 1 $ 2
  saída = $ (set_diff $ 1 $ 2)
  remove_sorted_files $ 1 $ 2

  se [-z $ output]; então
    retornar 0
  outro
    retorno 1
  fi

}

Saurabh Hirani
fonte
Esse script deve começar com #!/bin/bash?
24417 Alex
2

Em http://www.catonmat.net/blog/set-operations-in-unix-shell/ :

Comm compara dois arquivos classificados linha por linha. Pode ser executado de forma que produza linhas que aparecem apenas no primeiro arquivo especificado. Se o primeiro arquivo for um subconjunto do segundo, todas as linhas no primeiro arquivo também aparecerão no segundo, portanto, nenhuma saída será produzida:

$ comm -23 <(sort subset | uniq) <(sort set | uniq) | head -1
# comm returns no output if subset ⊆ set
# comm outputs something if subset ⊊ set
Alec
fonte