Como usar grep, sort e uniq para criar três campos de saída

3

Estou usando dois arquivos no meu comando, o primeiro arquivo ( file1 ) é apenas um arquivo com todas as letras do alfabeto em linhas individuais. O segundo arquivo ( $w no meu comando) é uma lista gigante de palavras. Eu tenho que comparar a lista do alfabeto para a lista de palavras para encontrar palavras que contenham uma letra do alfabeto exatamente duas vezes, mostrar quantas dessas palavras existem para cada letra e uma palavra de exemplo. A saída seria algo assim, mas para todo o alfabeto

v 94 bivalve
w 94 awkward
x 3 executrix
y 196 abysmally
z 58 bedazzle

Abaixo está o meu comando e sua saída

 for i in 'cat file1'; do grep $i.*$i $w | sort | uniq -c | head -1; done
  1 aardvark    
  1 abba
  1 acacia
  1 abandoned
  1 abalienate
  1 affability
  1 ageing
  1 aforethought
  1 abalienation
  1 hajj
  1 backstroke
  1 abnormally
  1 accommodate
  1 abalienation
  1 abdominous
  1 agitprop
  1 quinqevalent
  1 aardvark
  1 abbess
  1 abatement
  1 absquatulate
  1 bivalve
  1 awkward
  1 executrix
  1 abysmally
  1 bedazzle
    
por James Obrien 08.11.2016 / 22:10

3 respostas

3

Supondo que você esteja usando bash e uma versão relativamente nova dele, você deve ser capaz de fazer algo assim.

for CHAR in {a..z}
do
    WORD_LIST=( $(grep "$CHAR.*$CHAR" $w) )
    echo $CHAR ${#WORD_LIST[@]} ${WORD_LIST[0]}
done

Estamos fazendo uso de matrizes bash que podem fornecer uma contagem do tamanho ${#WORD_LIST[@]} e obteremos o primeiro elemento da matriz ${WORD_LIST[0]} .

O motivo pelo qual seu exemplo não funciona é porque uniq -c contará apenas as instâncias uniq, então ele lhe dará uma contagem de cada palavra, em vez de uma contagem de todas as palavras passadas para ele. .

    
por 08.11.2016 / 22:41
2

Sarting da versão de Zachary Brady:

for i in {a..z} 
 do 
   ( echo $i ;
     grep -c    "^[^$i]*$i[^$i]*$i[^$i]*$" file1; 
     grep -m 1  "^[^$i]*$i[^$i]*$i[^$i]*$" file1
   ) | paste - - - 
 done
  • "^[^$i]*$i[^$i]*$i[^$i]*$" é para garantir que recebamos exatamente duas ocorrências de $i (exemplo ^[^a]*a[^a]*a[^a]*$ )
  • grep -c ... conta o número de palavras correspondentes
  • grep -m 1 ... obtém a primeira palavra correspondente
  • colar - - - ... une as 3 linhas de saída em uma única linha

Se você preferir um exemplo de palavra aleatória, substitua o segundo grep por

grep "^[^$i]*$i[^$i]*$i[^$i]*$" file1 | shuf | head -1

Outra alternativa para "garantir exatamente dois" é encontrar 2 aa e rejeitar se aaa:

grep 'a.*a' file1  | grep -vc 'a.*a.*a' 
    
por 09.11.2016 / 00:10
2

Aqui estão duas maneiras de fazer isso, mais uma orientada a shell (usando principalmente o grep) e outra com o awk.

w=/usr/share/dict/words
sort file1 | uniq | while read letter
do
  count=$(grep -ic "^[^$letter]*$letter[^$letter]*$letter[^$letter]*$" "$w")
  r=$(( (RANDOM % count) + 1 ))
  printf "%s %d %s\n" "$letter" $count \
    $(grep -i "^[^$letter]*$letter[^$letter]*$letter[^$letter]*$" "$w" | \
        sed -n ${r}p )
done

A classificação inicial e o uniq são desnecessários se o arquivo1 foi preparado conforme indicado (uma letra por linha), mas eu os adicionei gratuitamente para me aproximar do requisito "use grep sort and uniq".

A solução do awk:

BEGIN {
  split("abcdefghijklmnopqrstuvwxyz", alphabet, "");
  srand();
}
{
  for (i in alphabet) {
    letter=alphabet[i]
    if (match(tolower($1), "^[^"letter"]*"letter"[^"letter"]*"letter"[^"letter"]*$")) {
      counts[letter]++
      if (wordfor[letter]) {
        if (rand() * counts[letter] >= counts[letter] - 1)
          wordfor[letter]=$1
      } else
        wordfor[letter]=$1
    }
  }
}
END {
  for (i in alphabet)
    print alphabet[i], counts[alphabet[i]], wordfor[alphabet[i]]
}

Salve isso em um arquivo e use algo como:

w=/usr/share/dict/words ## or whatever
awk -f theabove.awk "$w" | sort
    
por 09.11.2016 / 03:55

Tags