텍스트 파일의 고유 모음 수를 계산하는 쉘 스크립트

텍스트 파일의 고유 모음 수를 계산하는 쉘 스크립트

저는 주어진 텍스트 파일을 가져와 문자 수, 모음 수, 각 모음 수를 계산하는 스크립트를 작성 중입니다. 첫 번째 부분은 쉽지만 루프에 문제가 있습니다. 내 이해는 myString 개수가 루프 개수가 된다는 것입니다. 문자를 읽을 때마다 if/elif 문을 거쳐 모음과 일치하면 해당 모음에 해당하는 변수의 값을 1씩 증가시킵니다.

Shellcheck.net은 내 라인을 좋아하지 않지만 그 이유를 이해할 수 없습니다.

#!/bin/bash

myString=$(cat sampletext.txt | tr A-Z a-z)   #this works
count=$(echo -n "$myString" |tr -d '[.]'| wc -c)    #this works
vowels=$(echo -n $myString | tr -cd 'aeiou'| wc -c) #this works

va=0
ve=0
vi=0
vo=0
vu=0
i=0
while (( i++ < ${#myString} )); do
char=$(expr substr "$myString" "$i" 1)
if   [ "$char" -eq "a" ]; then
((va=++))
elif [ "$char" -eq "e" ]; then
((ve=++))
elif [ "$char" -eq "i" ]; then
((vi=++))
elif [ "$char" -eq "o" ]; then
((vo=++))
elif [ "$char" -eq "u" ]; then
((vu=++))
fi
done
echo $vi

쉘 검사 출력:

((va=++))
^-- SC1105 (error): Shells disambiguate (( differently or not at all. For subshell, add spaces around ( . For ((, fix parsing errors.
  ^-- SC2030 (info): Modification of va is local (to subshell caused by (..) group).

*안타깝지만, 안되는 부분만 넣었습니다. Shebang을 포함한 전체 내용을 표시하도록 편집했습니다 =)

답변1

awk기본적인 솔루션을 제공할 예정입니다 .당신이 언급한당신이 원해요"내가 만들고 싶은 보고서를 작성하기 위해 루프를 사용하는 방법을 배우고 이해합니다."awk, 이는 일반적으로 순수보다 이 경우 더 효율적입니다.bash

#!/bin/sh
#
grep -o -- . "$1" |
    awk '
        /[[:alpha:]]/ { letters[tolower($1)]++ }
        /[aeiou]/ { vowels++ }

        END {
            printf "%d\tvowels\n", vowels;
            for (letter in letters) {
                printf "%d\t%s\n", letters[letter], letter | "sort -k2,3"
            }
        }
    '

파일을 호출 letters하고 실행 가능하게 만듭니다( chmod a+x letters). 입력 파일이 sampletext.txt다음과 같이 실행될 수 있습니다

./letters sampletext.txt

노트

  • grep -o -- . {file}(GNU grep또는 호환 가능하다고 가정) 파일을 한 줄에 하나씩 개별 문자로 분할합니다. awk내부에서 이 작업을 수행 할 수도 있지만 이는 빠르고 게으른 방법입니다.
  • [[:alpha:]]알파벳 문자와 일치합니다. [[:alnum:]]영숫자 또는 .임의의 문자를 사용할 수 있습니다 .
  • printf | "sort"구성은 서식이 지정된 모든 출력을 명령(단일 인스턴스)에 공급하고 sort, 명령은 현재 로케일을 기준으로 열 2를 기준으로 정렬합니다.

답변2

Bash에는 디렉토리 목록과 여러 색인(부록 D 참조)이 포함된 190페이지 분량의 매뉴얼이 함께 제공됩니다. (다소 모호하고 무서운) 구문은 많은 기능을 숨깁니다.

여기에 있는 대부분의 답변/튜토리얼은 이러한 부분에 따라 달라집니다.

www.gnu.org/software/bash/manual/bash.html#Arrays

www.gnu.org/software/bash/manual/bash.html#Shell-Parameter-Expansion

배열에는 특별한 것이 없습니다. 배열은 변수 집합에 대해 유사한 이름을 많이 만들어내는 것을 피하는 게으른 사람의 방법일 뿐입니다. 인덱스 값을 이름의 마지막 부분으로 처리합니다. 그러나 인덱스 자체는 변수이기 때문에 루프를 위한 훌륭한 도구입니다.

다음은 의도를 나타내는 충분한 설명이 포함된 스크립트입니다.

#! /bin/bash

myString="The quick brown FOX jumps over the lazy dog."

Vowel=( a e i o u )     #.. Declare a list of what we want to output. 

myString="${myString,,}"    #.. Shell substitution to lowercase a string. 

#.. Declares an associative array to store character frequencies.
#.. Typical values would be: Freq[e]="3", Freq[h]="2", Freq[q]="1".
#.. We store counts for all characters, to avoid multiple tests.

declare -A Freq 

#.. Iterate the string, indexing via a substring expansion,
#.. and counting the frequencies of each ASCII character.

for (( j = 0; j < ${#myString}; j++ )); do
    (( Freq[\${myString:j:1}]++ ))
done

declare -p Freq     #.. Debug of the frequency array.

#.. Iterate over the vowel list to report the frequencies.

for v in "${Vowel[@]}"; do
    printf 'Vowel %s occurs %2d times.\n' "${v}" "${Freq["${v}"]}"
done

출력은 다음과 같습니다.

$ time ./Calhoun.sh
declare -A Freq=([" "]="9" [.]="1" 
    [a]="1" [b]="1" [c]="1" [d]="1" 
    [e]="3" [f]="1" [g]="1" [h]="2" [i]="1" 
    [j]="1" [k]="1" [l]="1" [m]="1" [n]="1" 
    [o]="4" [p]="1" [q]="1" [r]="2" [s]="1" 
    [t]="2" [u]="2" [v]="1" [w]="1" [x]="1" 
    [y]="1" [z]="1" )
Vowel a occurs  1 times.
Vowel e occurs  3 times.
Vowel i occurs  1 times.
Vowel o occurs  4 times.
Vowel u occurs  2 times.

real    0m0.013s
user    0m0.012s
sys 0m0.000s

답변3

myString="Hello WORLD"
declare -A vowel=()      # an associative array
declare -l char          # value is lowercased upon assignment

for ((i=0; i<${#myString}; i++)); do 
    char=${myString:i:1}

    # inside [[...]], the == operator does _pattern matching_
    [[ $char == [aeiou] ]] && ((vowel[$char]++))
done

declare -p vowel   # => ([o]="2" [e]="1" )

문자열의 문자를 반복하는 더 효율적인 방법(특히 문자열이 긴 경우)은 다음과 같습니다.

while IFS= read -r -d '' -n1 char; do 
    [[ $char == [aeiou] ]] && ((vowel[$char]++))
done < <(
    printf '%s' "$myString"
)

개수가 0인 모음을 포함하려면 다음을 수행하세요.

myString="Hello WORLD"
declare -A vowel=([a]=0 [e]=0 [i]=0 [o]=0 [u]=0)
declare -l char

while IFS= read -r -d '' -n1 char; do 
    [[ -v "vowel[$char]" ]] && ((vowel[$char]++))
done < <(printf '%s' "$myString")

for char in "${!vowel[@]}"; do
    printf '%s\t%d\n' "$char" "${vowel[$char]}"
done | sort
a   0
e   1
i   0
o   2
u   0```

답변4

awk각 모음에 대한 순수 솔루션은 개별적으로 제공됩니다.

#define array vowels with one vowel for each index
BEGIN{ split("aeiou",vowels,"") }

#in each line, make line lowercase
{$0=tolower($0)
#for each vowel occurence (loops through array vowels),
#replace vowel with empty string.
#gsub returns the number of replacements is has made,
#this value is added to the counter array element for each vowel
for (letter in vowels) { count[letter]+=gsub(vowels[letter],"") } }

#in the end, loop through array vowels and return vowel and counter value
END { for (letter in vowels) {print vowels[letter],count[letter]} }

예제로 저장 count_vowels.awk하고 다음을 통해 실행하세요.

awk -f count_vowels.awk inputfile.txt

#-lines는 주석이므로 생략 가능합니다.

관련 정보