Precisamos de uma lista “C” que contenha todos os elementos de “A”, mas não tenha nenhum de “B”

6

Nós temos duas listas.

Um "A" maior:

A='echo -e '1\n2\n3\n4\n5''
echo "$A"
1
2
3
4
5

e um menor "B":

B='echo -e '1\n2\n3''
echo "$B"
1
2
3

P: Mas precisamos de uma terceira lista que contenha todos os elementos de "A", mas não tenha nenhum de "B", como faço isso no bash?

echo "$C"
4
5

Os números podem ser qualquer coisa, de "foo" a 99, etc.

ATUALIZAÇÃO:

Está trabalhando no shell manualmente, mas é estranho porque, se eu colocá-lo em um script, ele não funciona!

cat a.txt 
A=$(seq 5)
B=$(seq 3)
comm -23 <(sort <<< "$A") <(sort <<< "$B")
sh a.txt 
a.txt: line 3: syntax error near unexpected token '('
a.txt: line 3: 'comm -23 <(sort <<< "$A") <(sort <<< "$B")'

fazendo isso manualmente, funciona ...:

A=$(seq 5)
B=$(seq 3)
comm -23 <(sort <<< "$A") <(sort <<< "$B")
4
5

Por que? atualize na atualização: precisa usar bash ao invés de "sh": D

    
por evachristine 28.04.2014 / 17:04

4 respostas

12

O comando comm é o que você precisa:

$ A=$(seq 5)
$ B=$(seq 3)
$ comm -23 <(sort <<< "$A") <(sort <<< "$B")
4
5

Aqui está um método que não requer que a entrada seja classificada. Este é um idioma comum no awk que lê o primeiro arquivo na memória e, em seguida, faz alguma filtragem no segundo arquivo com base no primeiro. Vamos tentar com dados randomizados

$ A=$(seq 5 | sort -R); echo "$A"
3
5
1
2
4
$ B=$(seq 3 | sort -R); echo "$B"
2
1
3

Esperamos que a saída seja 5 e depois 4:

$ awk 'NR==FNR {b[$1]=1; next} !($1 in b) {print}' <(echo "$B") <(echo "$A")
5
4
    
por 28.04.2014 / 17:13
3

Como glenn jackman forneceu, o utilitário comm é a maneira mais simples de fazer isso. No entanto, esse método destrói a ordem de classificação.

Existe outra maneira de realizar isso que preserva a ordem de classificação original (embora ambas as listas devam ser pré-classificadas na mesma ordem):

diff --unchanged-line-format '' --old-line-format '' file_a file_b

Isso retornará todas as linhas exclusivas para file_b em seu pedido original.

Acredito que isso também seria mais eficiente se o conjunto de dados fosse muito grande também. Como uma operação de classificação pode ser cara. Mas isso é apenas um palpite.

    
por 28.04.2014 / 17:37
2
sort a b b | uniq -u

Mais antigos que os montes (UNIX 7), mas ainda funciona.

    
por 29.04.2014 / 06:17
0

Ou, você sabe, Perl:

#!/usr/bin/perl -s
if($#ARGS == 0) {print "Usage: $0 -exclude=fileWithLinesToExclude [inputFile]\n"; exit(0)}
open(EXCL, $exclude);
%excluded = map { $_ => 1 } <EXCL>;
while(<>) {
   print $_ unless $excluded{$_};
} 

Coisas

  • perl -s permite que os switches se tornem valores de variáveis
  • Não há chomping acontecendo; se a linha de exclusão for "foobar_" e a linha processada for "foobar", ela não será excluída.
  • Nenhuma classificação está em andamento, exceto concebivelmente a inserção de hash, então o arquivo a ser processado pode ser tão grande quanto você desejar, ou um fluxo de coisas, qualquer que seja.
  • Transmita o nome do arquivo de entrada após o switch de exclusão ou apenas insira o material.
por 29.04.2014 / 13:24