Como comparar a mesma palavra entre vários arquivos?

4

Eu gostaria de contar as mesmas palavras em vários arquivos e mostrar em qual arquivo elas são.

Arquivo1:

This is so beautiful

Arquivo2:

There are so beautiful

File3:

so beautiful

A saída desejada 1:

so:3
beautiful:3

A saída desejada 2:

so:
file1:1
file2:1
file3:1

beautiful:
file1:1
file2:1
file3:1
    
por Yu Quan 14.09.2018 / 07:27

7 respostas

2

Tente isso,

# Declare the files you want to include
files=( file* )

# Function to find common words in any number of files
wcomm() {
    # If no files provided, exit the function.
    [ $# -lt 1 ] && return 1
    # Extract words from first file
    local common_words=$(grep -o "\w*" "$1" | sort -u)
    while [ $# -gt 1 ]; do
        # shift $1 to next file
        shift
        # Extract words from next file
        local next_words=$(grep -o "\w*" "$1" | sort -u)
        # Get only words in common from $common_words and $next_words
        common_words=$(comm -12 <(echo "${common_words,,}") <(echo "${next_words,,}"))
    done
    # Output the words common to all input files
    echo "$common_words"
}

# Output number of matches for each of the common words in total and per file
for w in $(wcomm "${files[@]}"); do
    echo $w:$(grep -oiw "$w" "${files[@]}" | wc -l);
    for f in "${files[@]}"; do
        echo $f:$(grep -oiw "$w" "$f" | wc -l);
    done;
    echo;
done

Saída:

beautiful:3
file1:1
file2:1
file3:1

so:3
file1:1
file2:1
file3:1

Explicação :

Incluído como comentários dentro do script.

Recursos :

  • Tantos arquivos quanto seu ARG_MAX permite
  • Localiza todas as palavras separadas por qualquer coisa grep entende como separador de palavras.
  • Ignora maiúsculas, portanto, "bonito" e "Bonito" é a mesma palavra.
por 14.09.2018 / 12:57
0

Tente este código. se necessário, faça ajustes

bash-4.1$ cat test.sh
#!/bin/bash

OUTPUT_FILE=/tmp/output.txt

awk '{
for(i=1;i<=NF;i++)
{
        Arr[$i]++
}
}
END{
for (i in Arr){
        if(Arr[i]>1)
        {
                print i":"Arr[i]
        }
}
}' file* > ${OUTPUT_FILE}

cat ${OUTPUT_FILE}
echo ""

IFS=":"
while read WORD TOTAL_COUNT
do
        echo "${WORD}:"
        for FILE_NAME in file*
        do
                COUNT=$(tr ' ' '\n' < ${FILE_NAME} | grep -c "${WORD}")
                if [ "${COUNT}" -gt "0" ]
                then
                        echo "${FILE_NAME}:${COUNT}"
                fi
        done
done < ${OUTPUT_FILE}


bash-4.1$ bash test.sh
beautiful:3
so:3

beautiful:
file1:1
file2:1
file3:1
so:
file1:1
file2:1
file3:1
    
por 14.09.2018 / 09:31
0

Usando grep para alimentar o nome das palavras e arquivos e, em seguida, awk para reformatar a saída para obter o resultado desejado:

grep -Ho '\w\+' file* |
awk -F':' '{ words[$1 FS $2]++; seen[$2]++ }
END{ for (x in seen) {
         print x":" seen[x];
         for (y in words) {
            if (y ~ "\<" x "\>")print substr(y, 1, length(y)-length(x)), words[y]
         }
     }
}'

Isso lhe dará uma boa saída como segue (ambas as saídas desejadas de uma só vez):

so:3
file1: 1
file2: 1
file3: 1
This:1
file1: 1
beautiful:3
file3: 1
file1: 1
file2: 1
There:1
file2: 1
are:1
file2: 1
is:1
file1: 1
    
por 14.09.2018 / 11:03
0

Tente tudo isso awk approach:

awk '
        {for (i=1; i<=NF; i++)  {WC[$i]++
                                 FC[$i,FILENAME]++
                                }
        }
END     {for (w in WC) if (WC[w] > 1) print w, WC[w]

         print ""

         for (f in FC)  {split (f, T, SUBSEP)
                         w = T[1]
                         if (WC[w] > 1) {if (!D[w]) print w, ""
                                         print T[2], FC[f]
                                         D[w] = 1
                                        }
                        }
        }

' OFS=":" file[1-3]
so:3
beautiful:3

beautiful:
file3:1
file2:1
file1:1
so:
file1:1
file2:1
file3:1

Ele coleta os dados respectivos (contagem de palavras e palavras por arquivo) para cada arquivo e, na seção END, produz as saídas desejadas 1 e 2, com base na contagem de palavras > 1.

    
por 14.09.2018 / 12:59
0

Se você não quer escrever um código, apenas usando uma maneira rápida de saber os resultados, você pode usar este comando:

cat list_of_words | while read line; do echo $line; grep -riE '$line'-c where_to_look_or_folder; done

-r :read into files
-i: no casesensitive
-E: regexp is useable if you want something more complicated to search
-c: counter

Saída:

word1
path:filename:count

Exemplo:

cat text | while read line; do echo $line; grep -riE '$line'-c somwhwere/nowhere; done
    
por 14.09.2018 / 13:21
0

perl, conta todas as palavras nos arquivos especificados e, para palavras vistas mais de uma vez, imprime a contagem total e a contagem por arquivo

$ cat file1
This is so beautiful
foo
foo
foo
$ cat file2
There are so beautiful
foo
bar
bar
$ cat file3
so beautiful
bar
baz

então

perl -lane '
    for (@F) {$count{$_}++; $filecount{$_}{$ARGV}++}
    END {
        for $word (sort keys %count) {
            if ($count{$word} > 1) {
                print "$word:$count{$word}";
                print "$_:$filecount{$word}{$_}" for sort keys %{ $filecount{$word} };
                print "";
            }
        }
    }
' file{1,2,3}
bar:3
file2:2
file3:1

beautiful:3
file1:1
file2:1
file3:1

foo:4
file1:3
file2:1

so:3
file1:1
file2:1
file3:1

Para classificar os resultados por ordem decrescente de "contagem", você usaria essa linha no bloco END:

for $word (reverse sort {$count{$a} <=> $count{$b}} keys %count) {
    
por 14.09.2018 / 17:27
0

Este processo exibe o número de ocorrências de palavras incluídas em arquivos de texto localizados em um diretório ou subdiretórios, para cada arquivo e em todos os arquivos.

  1. Concatenar todos os arquivos de texto.
  2. Suprimir palavras duplicadas.

O resultado é uma lista de palavras e cada palavra na lista é contada em cada arquivo sucessivamente, bem como em arquivos concatenados.

  1. Pesquise o número de ocorrências para cada palavra na lista.

1. Concatenar todos os arquivos de texto

find . -type f -exec cat {} \;

find pesquisa todos os arquivos de texto no diretório ou nos subdiretórios atuais e invoca cat para concatenar todos os arquivos correspondentes.

2. Suprimir palavras duplicadas

As palavras podem ser colocadas separadamente em cada linha para criar uma lista de palavras: cada caractere, exceto uma letra, é substituído por uma nova linha e, em seguida, uma sequência de novas linhas é substituída por uma nova linha.

tr -cs '[:alpha:]' '[\n*]'

Finalmente, as palavras duplicadas devem ser suprimidas para obter uma lista adequada de palavras. uniq pode filtrar linhas repetidas de texto, mas as linhas devem ser classificadas, portanto, sort pode ser usado para classificar linhas de texto.

sort | uniq

ou

sort -u

Observação: uma pesquisa sem distinção entre maiúsculas e minúsculas pode ser realizada adicionando o seguinte comando no pipeline.

tr '[:upper:]' '[:lower:]'

Problemas potenciais

Um arquivo de script existente não deve ser concatenado com outros arquivos, mesmo se estiver localizado no diretório atual ou em subdiretórios (c.f. man find ).

find . -type f \( \! -name "*${0##*/}" \) -exec cat {} \;

Observação: $0 expande para o nome do shell. ${PARAMETER##WORD} expande para o parâmetro com o padrão de correspondência mais longo excluído (c.f. expansões de parâmetros do shell).

Por exemplo, /usr/local/bin/myscript se torna myscript .

Resultado

list=$(find . -type f \( \! -name "*${0##*/}" \) -exec cat {} \; | 
           tr -cs '[:alpha:]' '[\n*]' | tr '[:upper:]' '[:lower:]' | sort -u)

A substituição de comandos permite salvar a saída do pipeline em uma variável.

3. Pesquise o número de ocorrências ...

a) Pesquisa todas as linhas contendo a palavra dada, cada ocorrência em uma linha diferente ...

grep --exclude="*${0##*/}" -Rowi $word .

... e conta o número de linhas

grep --exclude="*${0##*/}" -Rowi $word . | wc -l

b) Conta as linhas correspondentes para cada arquivo de entrada.

grep --exclude="*${0##*/}" -Rowi $word . | tr '[:upper:]' '[:lower:]' |
    uniq -c | sed -E "$sed_script"

Pesquisa todas as linhas que contêm a palavra em questão (cada ocorrência está em uma linha diferente), imprime o número de ocorrências, bem como o arquivo de origem, e reformata os dados.

Observação: $0 expande para o nome do shell. ${PARAMETER##WORD} expande para o parâmetro com o padrão de correspondência mais longo excluído (c.f. expansões de parâmetros do shell).

Por exemplo, /usr/local/bin/myscript se torna myscript .

Nota: --exclude é uma extensão GNU, não especificada para POSIX grep . Se isso for um problema, remova a opção e adicione a seguinte instrução na parte superior do script de shell.

# assign first positional parameter to "dirname"
# and move to this directory

dirname=${1:?first positional parameter missing\!}
cd "$dirname"

Script da shell

#!/bin/sh

list=$(find . -type f \( \! -name "*${0##*/}" \) -exec cat {} \; | 
           tr -cs '[:alpha:]' '[\n*]' | tr '[:upper:]' '[:lower:]' | sort -u)

# extract data and print formatted data
sed_script='s/([[:digit:]]+) (.*):[[:alpha:]]+/:/;s/[[:blank:]]+//'

for word in $list; do
    echo $word:$(grep --exclude="*${0##*/}" -Rowi $word . | wc -l)
    grep --exclude="*${0##*/}" -Rowi $word . | tr '[:upper:]' '[:lower:]' | 
        uniq -c | sed -E "$sed_script"
    echo
done | sed -e "/^[[:alpha:]]\+:1/{N;N;d;}"

Entrada

prompt% cat file1
Lorem ipsum dolor sit amet. cat cat
prompt% cat file2
Lorem ipsum dolor sit amet, consectetur adipiscing elit. cat ut
prompt% cat file3
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Sed tristique egestas massa sed facilisis. Duis hendrerit ut. cat
tristique tristique tristique tristique

Saída

adipiscing:2
./file2:1
./file3:1

amet:3
./file1:1
./file2:1
./file3:1

cat:4
./file1:2
./file2:1
./file3:1

consectetur:2
./file2:1
./file3:1

dolor:3
./file1:1
./file2:1
./file3:1

elit:2
./file2:1
./file3:1

ipsum:3
./file1:1
./file2:1
./file3:1

lorem:3
./file1:1
./file2:1
./file3:1

sed:2
./file3:2

sit:3
./file1:1
./file2:1
./file3:1

tristique:5
./file3:5

ut:2
./file2:1
./file3:1
    
por 16.09.2018 / 11:11