0

This script is incomplete since I will want to do error testing later, but the idea is that ARG is a script and ARG2 is a directory, and ARG should mark all of the files in ARG2. How would I do this in such a way that bash knows the first argument has to be a script and argument 2 is a directory?

ARG=$1
ARG2=$2
CHECK=0
aCount=0
bCount=0
cCount=0
dCount=0
fCount=0

if [ $CHECK -e 0 ]; then
    for files in $ARG2; do
        if [ sh $1 $2 -eq A]; then
            aCount=$((aCount+1))
        elif [ sh $1 $2 -eq B]; 
            bCount=$((bCount+1))
        elif [ sh $1 $2 -eq C]; 
            cCount=$((cCount+1))
        elif [ sh $1 $2 -eq D ];
            dCount=$((dCount+1))
        else;
            fCount=$((fCount+1))
        fi
    done
fi

echo A: $aCount
echo B: $bCount       
echo C: $cCount          
echo D: $dCount
echo F: $fCount
0

2 Answers 2

4

There are a variety of errors you could catch by running your script through shellcheck.net.

Corrections:

  • To loop over the files in a directory, write for file in dir/* not for file in dir. The latter just loops once with $file set to the string "dir", rather than iterating over the contents of the directory dir/.

  • [ sh $1 $2 -eq A] is a jumble of shell constructs. You want to capture the output of the script, so you need $(...). You're doing a string check, so you should use == not -eq. Correcting both yields:

    [ $(sh $1 $2) == A ]
    
  • I'm guessing $2 should be $files, though. The loop variable, yes?

    [ $(sh $1 $files) == A ]
    
  • There are other miscellaneous mistakes, such as missing thens and not always having a space before ].

Improvements:

  • You should quote everything properly to prevent inadvertent word splitting and glob expansion.

    [ "$(sh "$1" "$files")" == A ]
    
  • Let's replace $1 with $script and $files with singular $file.

    [ "$(sh "$script" "$file")" == A ]
    
  • If the script has a proper shebang line like #!/bin/bash at the top then there's no need to explicitly invoke sh.

    [ "$("$script" "$file")" == A ]
    
  • That's all great. Now you have something like this:

    if [ "$("$script" "$file")" == A ]; then
        aCount=$((aCount+1))
    elif [ "$("$script" "$file")" == B ]; then
        bCount=$((bCount+1))
    elif [ "$("$script" "$file")" == C ]; then
        cCount=$((cCount+1))
    elif [ "$("$script" "$file")" == D ]; then
        dCount=$((dCount+1))
    else
        fCount=$((fCount+1))
    fi
    

    Awfully repetitious, no? Let's try a case statement instead.

    case "$("$script" "$file")" in
        A) aCount=$((aCount+1));;
        B) bCount=$((bCount+1));;
        C) cCount=$((cCount+1));;
        D) dCount=$((dCount+1));;
        *) fCount=$((fCount+1));;
    esac
    
  • That case statement is still quite complex. Let's break it up to make it easier to parse.

    grade=$("$script" "$file")
    
    case $grade in
        ...
    esac
    
  • Variable names ought to be lowercase. UPPERCASE names are reserved for the shell, so best not to use those. Change COUNT to count.

  • Let's rename ARG and ARG2 to script and dir, respectively. Meaningful names make everything easier to read.

  • var=$((var+1)) can be simplified to ((var += 1)) or ((var++)).

End result:

script=$1
dir=$2

check=0
aCount=0
bCount=0
cCount=0
dCount=0
fCount=0

if ((check == 0)); then
    for file in "$dir"/*; do
        grade=$("$script" "$file")

        case $grade in
            A) ((aCount++));;
            B) ((bCount++));;
            C) ((cCount++));;
            D) ((dCount++));;
            *) ((fCount++));;
        esac
    done
fi

echo "A: $aCount"
echo "B: $bCount"
echo "C: $cCount"
echo "D: $dCount"
echo "F: $fCount"
Sign up to request clarification or add additional context in comments.

Comments

2

@John Kugelman did a great job above. For an alternate take -

declare -A count                   # count is an array
for file in "$dir"/*               # skipping assignments, and $check
do grade=$("$script" "$file")      # grab the output as $grade
   case $grade in                  # look up its value
   [A-D]) (( count[$grade]++ ));;  # use as-is for a-d
       *) (( count['F']++    ));;  # throw everything else in f
   esac
done

for g in A B C D F                 # then for a-f (known values)
do echo "$g: "${count[$g]}         # pull the counts
done

3 Comments

An array is a great idea. I ran out of space in my answer. ;)
You'll want declare -A for an associative array.
I had -A, and started testing a couple options, and ended up not putting it back. Thanks, edited/corrected.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.