0

I have an issue with the echo in the for loop, as I want to count string chars especailly for "*" it but it prints all the files in current directory.

clearvar() {
int=0
str=0
uniqchar=0
}

countstring(){
for c in $(echo "${1}" | fold -w1); do

                echo "$c"
                if [[ $c == [0-9] ]];then
                        int=$(( $int + 1 ))

                elif [[ $c == [a-Z] ]];then
                        str=$(( $str + 1 ))

                else
                        uniqchar=$(( $uniqchar + 1 ))
                fi
        done
}

while [ $# -gt 0 ];do

        echo "Argument input: $1"
        read -p "Input: " string
        rmws=$(echo $string | tr -d " ")

        mashed=$rmws$1
        countstring $mashed
        echo -e "int: $int\nstr: $str\nuniquechar: $uniqchar\nWhole string: $mashed"
        clearvar
        shift
done

Example output:

Argument input: io1
Input: fj^*23                
f
j
^
file1
file2
file3
2
3
i
o
1
int: 3
str: 4
uniquechar: 4
Whole string: fj^*2wio1

it interprets as echo * instead of echo "*". so I expect it to not print out the file names.

J Wu
  • 3
  • 2
  • Welcome to SO could you please post input sample and expected sample output in your question and let us complete requirement's picture here. – RavinderSingh13 Dec 15 '19 at 04:26
  • [When to wrap quotes around a shell variable?](https://stackoverflow.com/questions/10067266/when-to-wrap-quotes-around-a-shell-variable) – KamilCuk Dec 15 '19 at 04:37
  • im still confused on when to wrap the quotes xd – J Wu Dec 15 '19 at 04:46

1 Answers1

0
rmws=$(echo $string | tr -d " ")

If string=* this just executes echo * and expands the *.

The same happens in:

countstring $mashed

Both these expansions are unquoted. Quote them in double quotes. As a rule of a thumb - always use double quotes.

Also the same happens in the for loop:

for c in $(echo "${1}" | fold -w1)

the expansion, as elsewhere, is unquoted, so * expands. You have to quote. That's why the for i in $(..) is considered bad style - because such bugs happen. You can't do for i in "$(...)" because then you would iterate over one element. To iterate over lines or elements in a stream use a while IFS= read -r loop. You can print every character on each separate line with ex. sed 's/./&\n/g' and iterate over lines, or use bash extension read -n1 to read one character.

while IFS= read -r -n1 c; do
   ..
done <<<"$1"

The <<<"$1" is a bash's "here string".

You don't need $ in arithmetic expansion. Just:

int=$(( int + 1 ))

str=$(( str + 1 ))

uniqchar=$(( uniqchar + 1 ))

or in bash you can even do:

(( int++ ))
# and so on

Your script could become:

clearvar() {
    int=0
    str=0
    uniqchar=0
}

countstring(){
       while IFS= read -r -n1 c; do
                echo "$c"
                if [[ $c == [0-9] ]];then
                        (( int++ ))

                elif [[ $c == [a-Z] ]];then
                        (( str++ ))

                else
                        (( uniqchar++ ))
                fi
        done <<<"$1"
}

while [ $# -gt 0 ]; do
        echo "Argument input: $1"
        read -p "Input: " string
        rmws="$(echo "$string" | tr -d " ")"
        mashed="$rmws$1"
        countstring "$mashed"
        echo "int: $int"
        echo "str: $str"
        echo "uniquechar: $uniqchar"
        echo "Whole string: $mashed"
        clearvar
        shift
done

Notes:

  • echo has portability issues. Prefer to use printf instead.
  • I prefer while (($#)); do in place of while [ $# -eq 0 ]; do.

PS. I would use tr:

countstring() {
       printf "%s" "$1" | tr -cd '[0-9]' | wc -c
       printf "%s" "$1" | tr -cd '[a-zA-Z]' | wc -c
       printf "%s" "$1" | tr -d '[0-9a-zA-Z]' | wc -c
}
KamilCuk
  • 120,984
  • 8
  • 59
  • 111
  • thanks, but it still seems to print out directory files, even with every variable value quoted. – J Wu Dec 15 '19 at 04:57
  • Hm... printf comes from C language - the `%s` is substituted for the argument passed. So ex. `printf "%s" "the string"` just prints `the string`. Ex. `printf "The %s fox %s over %s\n" "brown" "jumps" "dog"` would print `The brown fox jumps over dog` followed by a newline. You can find many examples over the net. – KamilCuk Dec 15 '19 at 05:08
  • thanks, it makes more sense to use printf, tr and wc than for loops for counting characters. – J Wu Dec 15 '19 at 05:17