Как разбить и подсчитать количество вхождений слова в Bash?

каждый.

У меня есть файл с именами в формате:

Name Name    Surname Surname

Это файл с именами

Nikola  KAZIKOVA
Pavel   MILAN GAZDIK
Nikolas Martin  STRUP
Nikola  GAZDIK
Nikola ČERNÁ
Nikola Martina ČERNÁ

Я пытаюсь создать сценарий, который печатает ряд вхождений рядом с каждым именем. Однако я не могу понять, как их считать.

Это мой код, я могу загрузить текст в массив, но не могу понять, как считать имена.

#!/bin/bash

file=$1

if [[ -z $1 ]]
    then echo "ERROR: FILE NOT FOUND"
    exit
fi


# Read the file in parameter and fill the array named "array"
getArray() {
    array=() # Create array
    while IFS= read -r line # Read a line
    do
        array+=("$line") # Append line to the array
    done < "$1"
}

# Print the file (print each element of the array)
getArray $file
for e in "${array[@]}"
do
    IFS='   ' read -ra arr <<< "$e"
    echo "${arr[0]}" | grep -o "${arr[0]}"

done

Это результат, которого я пытаюсь достичь

   [4] Nikola  KAZIKOVA
   [1] Pavel   MILAN GAZDIK
   [1] Nikolas Martin  STRUP
   [4] Nikola  GAZDIK
   [4] Nikola ČERNÁ
   [4] Nikola Martina ČERNÁ

🤔 А знаете ли вы, что...
Bash поддерживает создание функций для группировки команд и повторного использования кода.


4
114
4

Ответы:

Решено

Один вариант:

while read -r f l ; do echo "[$(grep -w -c $f d.dat)] $f $l" ; done <d.dat

Выход:

[4] Nikola KAZIKOVA
[1] Pavel MILAN GAZDIK
[1] Nikolas Martin  STRUP
[4] Nikola GAZDIK
[4] Nikola ČERNÁ
[4] Nikola Martina ČERNÁ

Обратите внимание, что это не очень эффективно из-за grep внутри цикла. Если ваш файл большой, вы можете использовать альтернативные инструменты, такие как. awk или python.


awk альтернатива;

awk 'NR==FNR{A[$1]++}NR>FNR{for(i in A) {if (i==$1){printf "[%s] %s\n", A[i], $0}}}' d.dat d.dat

Выход:

[4] Nikola  KAZIKOVA
[1] Pavel   MILAN GAZDIK
[1] Nikolas Martin  STRUP
[4] Nikola  GAZDIK
[4] Nikola ČERNÁ
[4] Nikola Martina ČERNÁ

с вашими показанными образцами, пожалуйста, попробуйте следовать коду awk.

awk '
{
  value[FNR]=$0
  occur[$1]++
}
END{
  for(i=1;i<=FNR;i++){
    split(value[i],arr,FS)
    print "["occur[arr[1]] "] " value[i]
  }
}
'  Input_file

Объяснение: добавлено подробное объяснение приведенного выше кода.

awk '                                       ##Starting awk program from here.
{
  value[FNR]=$0                             ##Creating array named value which has index as current line number and value as currnet line value.
  occur[$1]++                               ##Creating array named occur with index of 1st field and keep adding same index element here.
}
END{                                        ##Starting END block of this program from here.
  for(i=1;i<=FNR;i++){                      ##Using for loop till value of FNR here.
    split(value[i],arr,FS)                  ##Splitting value[i] into arr with separator as space.
    print "["occur[arr[1]] "] " value[i]    ##Printing values as per requirement here.
  }
}
'  Input_file                               ##Mentioning Input_file name here.

Это может сработать для вас (GNU sed, sort, uniq):

sed 's/ .*//' file |
sort |
uniq -c | 
sed -nE 's/^\s*(\S+) (\S+)/s#^\2\\b#[\1] \&#/p' |
sed -f - file

Удалите все, кроме имени, из каждой строки.

Рассортируйте имена.

Используя uniq, подсчитайте количество вхождений каждого имени.

Превратите приведенный выше результат в сценарий sed, который добавляет к каждому имени префикс в формате [n].

Примените приведенный выше сценарий sed к исходному файлу.


Если ввод/данные не слишком большие/большие, чистое решение bash будет выглядеть примерно так:

#!/usr/bin/env bash

declare -A Name
declare -a full_name names

while read -ra names; do
  full_name+=("${names[*]}")
  ((Name[${names[0]}]++))
done < file.txt

for name in "${!full_name[@]}"; do
  first_name = "${full_name[$name]%% *}"
  for key in "${!Name[@]}"; do
    [[ "$first_name" == "$key" ]] &&
    printf '[%d] %s\n' "${Name[$key]}" "${full_name[$name]}" &&
    break
  done
done