Как увеличить строковую переменную в цикле for

#bash #for-loop #grep

#bash #for-цикл #grep

Вопрос:

Мне нужен цикл, который может находить букву, которой чаще всего заканчиваются слова на нескольких языках, и выводить данные в столбцах. Пока у меня есть

 count="./wordlist/french/fr.txt ./wordlist/spanish/es.txt ./wordlist/german/de.$
lang="French Spanish German Portuguese Italian"
(
echo -e "Language Letter Count"
for i in $count
do
    (for j in {a..z}
        do
            echo -e "LANG" $j $(grep -c $j> $i)
        done
    ) | sort -k3 -rn | head -1
done
) | column -t  

Я хочу, чтобы она выводилась, как показано:

 
Language  Letter  Count
French     e       196195
Spanish    a       357193
German     e       251892
Portuguese a       217178
Italian    a       216125
  

Вместо этого я получаю:

 
Language  Letter  Count
LANG      z       0
LANG      z       0
LANG      z       0
LANG      z       0
LANG      z       0
  

Файлы слов имеют формат:
Word Freq(#) где слово и его частота разделены пробелом.

Это означает, что у меня есть 2 проблемы; Во-первых, grep команда не обрабатывает аргумент $j> , чтобы найти символ в конце слова. Я пробовал использовать grep -E $j> и grep '$j>' , но ни то, ни другое не сработало.

Вторая проблема заключается в том, что я не знаю, как вывести название языка (в переменной lang ). Вложение другого for цикла не сработало, когда я попробовал это так (или с i и k в обратном порядке):

 
(
for i in $count
do
    for k in $lang
    do
        for j in {a..z}
        do
             echo -e $k $j $(grep -c $j> $i)
        done
        ) | sort -k3 -rn | head -1
done
done
) | column -t
  

Поскольку это выводит кратные названия языка « $k » в местах, где ему не принадлежит.

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

Комментарии:

1. Можете ли вы вставить пару строк, скажем, из двух файлов списка слов для проверки?

2. даже если бы это сработало, разве это не вывело бы неправильные числа? например, если ваш файл с подсчетом слов содержит три записи: is 1000; xertz 1; showbiz 1; результат был бы z 2 (а не s 1000 )

3. Да, Умляут, это было бы z 2 что я и хочу, так как я хочу подсчитать частоту и отобразить символ, которым чаще всего заканчивается слово в самом файле. И, рулофс, пример файла показан здесь: de 1622928 je 1622619 est 1348809 pas 1128894 le 1093232 таким образом, в самом этом файле e чаще всего заканчивается словом. Извините за неправильное представление.

Ответ №1:

grep границы слов

Чтобы заставить специальные разделители (например, > для окончания слова) работать с egrep при вызове из командной строки, вы должны поместить их в " кавычки " .

  count=$(egrep -c "${char}>" "${file}")
  

Кстати, вам действительно следует использовать двойные кавычки ( " ), потому что одинарные кавычки предотвратят расширение переменной. (например, в j="foo"; k='$j>' первый символ k значения будет $ вместо f )

Отображение названия языка

Получение правильной языковой строки немного сложнее; вот несколько предложений:

  • Выводите отображаемый язык из пути к списку слов:

     lang=${file%/*}
    lang=${lang##*/}
      

    С помощью bash (хотя и не с помощью dash и некоторых других оболочек) вы могли бы даже сделать lang=${lang^} заглавную строку.

  • Найдите правильное название языка в словаре. Bash-4 имеет встроенные словари, но вы также можете использовать файлы dicts:

     $ cat languagues.txt
    ./wordlist/french/fr.txt Français 
    ./wordlist/english/en.txt English
    ./wordlist/german/de.txt Deutsch
    
    $ file=./wordlist/french/fr.txt
    $ lang=$(egrep "^${file}/>" languages.txt | awk '{print $2}')
      
  • Вы также можете выполнять итерации по file,lang парам, например

     languages="french/fr,French spanish/es,Español german/de,Deutsch"
    for l in $languages; do
       file=./wordlist/${l%,*}.txt
       lang=${l#*,}
       # ...
    done
      

Принимая во внимание частоты слов

Третья проблема, которую я вижу (хотя я могу неправильно понять проблему), заключается в том, что вы не принимаете во внимание частоту слов. например, слово A, которое используется в 1000 раз чаще, чем слово B, будет учитываться только один раз (точно так же, как B).

Вы можете использовать awk для суммирования частот совпадающих слов:

 count=$(egrep "${char}>" "${file}" | awk '{s =$2} END {print s}')
  

Теперь все вместе

Таким образом, полное решение проблемы может выглядеть как:

 languages="french/fr,French spanish/es,Español german/de,Deutsch"

(
echo -e "Language Letter Count"
for l in ${languages}; do
  file=./wordlist/${l%,*}.txt
  lang=${l#*,}

  for char in {a..z}; do
     #count=$(egrep -c "${char}>" "${file}")
     count=$(egrep "${char}>" "${file}" | awk '{s =$2} END {print s}')
     echo ${file} ${char} ${count}
  done | sort -k3 -rn | head -1
done
) | column -t
  

Комментарии:

1. Это сработало чудесно, и я узнал кое-что новое, спасибо Umlaute!

2. У меня есть вопрос, если вы не возражаете, можете ли вы рассказать мне, как вы использовали ${l%,*} и ${l#*,} ? Я все еще в замешательстве по поводу использования % и # в скрипте, что именно они означают?

3. @Angelo man bash и search for ## должны дать вам объяснение, которое лучше всего, что я мог бы сказать.