Finalmix6
Finalmix6

Reputation: 415

How to replace a not existing value by 0?

So I have a subject folder, located in /home/subject, which contains subjects such as :

These subjects are files. And each one of them contain, the name of a student with his mark.


So for example it will be,

For Geography

And Math :

I also have a student folder, located in /home/student, which is empty for now. And the purpose of this folder is to put inside of it :

  1. The name of the student as the name of the file;
  2. The marks of all subjets this student received.

So here is my code :

rm /home/student/*
for subjectFile in /home/subject/*; do
awk -v subject="$(basename "$subjectFile")" '{ print subject, $2 >>"/home/student/" $1 }' "$subjectFile"
done

This loop iterates over all the subject files, inside of the subject folder. The $subjectFile value is something like :


I then, get the basename of each of these subject files :

And then print the result of the second column, the mark number, inside of my student name that I get through the first column of the subject file : so for this example, for the subject Geography, I'll get Matthew.

I also didn't want to simply infinite append the result, but overwrite the previous results each time I run this script, so I typed : rm /home/student/* , to erase any student files before to proceed to the append.

This works great.


But then I have a request,

How can I make it to replace by 0 the subject mark of a student, if this one is undefined? A student that did not received any mark for a specific subject, while others did?


As for example :

For Geography

And Math :

So for Elena, This shall create an Elena file, inside of the student folder with :

Upvotes: 2

Views: 91

Answers (2)

F. Hauri  - Give Up GitHub
F. Hauri - Give Up GitHub

Reputation: 70967

Simple distribution using pure

Let's try: This will create a little tree in /tmp:

cd /tmp && tar -zxvf <(base64 -d <<eof
H4sIAFUFUFwAA+3XXU6EMBQF4D6ziu7A/l2uLsBHE7eAYwUMghlKnNm9EKfEEHUyBpg4nu+lJJDQ
5HBK226KpqmuxJJUj4mGUTOpz2MktHXG9RdEWiitrWUhadFZHXRtyLZSiidflbsfnjt2/49qP/Jv
u4dnvwnLfAcn5K9JuT5/w0zIfw2T/F+yUMz+jiHg1Lnv87c09j9l0+dvU3JCqtln8oV/nv9dFkLh
36RWyW3l60zqm+S+KCspNSfnnhwsbtJ/X+dV2c68BBzvfzr2nw33/XeWUvR/DWP/WcYFgOICcI0F
4OJN+p/7Jt9mr8V+znec8P8/7P8cK4v+r2HsP8X6u1h/Qv0vX+x/6B59ff7znyFNw/5fGZz/AQAA
AAAAAAAAAAB+7R1PsalnACgAAA==
eof
)

This will create (and print in terminal, because of -v flag in tar command):

school/
school/subject/
school/subject/math
school/subject/english
school/subject/geography
school/student/

Quick overview:

cd /tmp/school/subject/
grep . * | sort -t: -k2

will render:

geography:Elena 14
english:Elena 15
math:Elena 19
math:Matthew 10
geography:Matthew 15
english:Matthew 17 
geography:Phil  15
math:Phil  17
english:Phil  18

Building student stat files:

cd /tmp/school
rm student/*
for file in subject/*;do
    subj=${file##*/}
    while read student note ;do
        echo >>student/${student,,} ${subj^} $note
    done < $file
done

Nota: This use ${VARNAME^} to upper first char and ${VARNAME,,} to lower all string. So filenames are lowercaps and subject become capitalized in student files.

Then now:

ls -l student
total 12
-rw-r--r-- 1 user user 32 jan 29 08:57 elena
-rw-r--r-- 1 user user 32 jan 29 08:57 matthew
-rw-r--r-- 1 user user 32 jan 29 08:57 phil

and

cat student/phil
English 18
Geography 15
Math 17

Then now: searching for missing notation

for file in student/*;do
    for subj in subject/*;do
        subj=${subj##*/}
        grep -q ^${subj^}\  $file || echo ${subj^} 0 >> $file
      done
  done

This could be tested (This will randomely drop 0 or 1 mark in all files):

for file in subject/*;do
    ((val=1+(RANDOM%4)))
    ((val<4)) && sed ${val}d -i $file
  done

Then run:

cd /tmp/school
rm student/*
for file in subject/*;do
    subj=${file##*/}
    while read student note ;do
        echo >>student/${student,,} ${subj^} $note
    done < $file
done
for file in student/*;do
    for subj in subject/*;do
        subj=${subj##*/}
        grep -q ^${subj^}\  $file || echo ${subj^} 0 >> $file
      done
  done

Ok, now:

grep ' 0$' student/*
student/matthew:Geography 0

Nota: As I'v been used $RANDOM, result may differ in your tests;-)

Another aproach: two steps again but

First step: building student list, then student files imediately with 0 notation:

cd /tmp/school
rm student/*
declare -A students
for file in subject/* ;do
    while read student mark ;do
        [ "$student" ] && students[$student]=
      done <$file
  done
for file in subject/*;do
    class=(${!students[@]})
    while read student mark ;do
        subj=${file##*/}
        echo >> student/${student,,} ${subj^} $mark
        class=(${class[@]/$student})
      done <$file
    for student in ${class[@]};do
        echo >>  student/${student,,} ${subj^} 0
      done
  done

Statistic tool

For fun, with a lot of bashisms and without file creation, there is a pretty dump tool:

#!/bin/bash

declare -A students
declare subjects=() sublen=0 stdlen=0
for file in subject/* ;do                               # read all subject files
    subj=${file##*/}
    subjects+=($subj)                                     # Add subject to array
    sublen=$(( ${#subj} > sublen ? ${#subj} : sublen )) # Max subject string len
    declare -A mark_$subj                   # Create subject's associative array
    while read student mark ;do
    stdlen=$(( ${#student} > $stdlen ? ${#student} : stdlen ))
        [ "$student" ] && {                                   # Skip empty lines
        ((students[$student]++))                     # Count student's marks
        printf -v mark_$subj[$student] "%d" $mark     # Store student's mark
    }
    done <$file
  done

printf -v formatstr %${#subjects[@]}s;  # prepare format string for all subjects
formatstr="%-${stdlen}s %2s ${formatstr// / %${sublen}s}"

printf -v headline "$formatstr" Student Qt "${subjects[@]}"
echo "$headline"                                               # print head line
echo "${headline//[^ ]/-}"                                # underscore head line

for student in ${!students[@]};do                   # Now one line by student...
    marks=()                                                       # Clear marks
    for subject in ${subjects[@]};do    
        eval "marks+=(\${mark_$subject[\$student]:-0})"  # Add subject mark or 0
    done
    printf "$formatstr\n" $student ${students[$student]} ${marks[@]}
done

This may print out something like:

Student Qt    english geography      math
------- --    ------- ---------      ----
Phil     2         18        15         0
Matthew  3         17        15        10
Elena    2          0        14        19

Nota

This script was built for bash v4.4.12 and tested under bash v5.0.

More

You could download bigger demo script: scholl-averages-demo.sh (view in browser as text .txt).

Always pure bash without forks, but with

  • average by student, average by subject and overall average, in pseudo float
  • subject and student sorted alphabeticaly
  • support UTF-8 in student names

.

Student Qt        art   biology   english geography   history      math  Average
------- --        ---   -------   ------- ---------   -------      ----  -------
Elena    5         12         0        15        14        17        19    12.83
Iñacio   6         12        15        19        18        12        14    15.00
Matthew  5         19        18        17        15        17         0    14.33
Phil     5         15        19        18         0        13        17    13.67
Renée    6         14        19        18        17        18        15    16.83
Theresa  5         17        14         0        12        17        18    13.00
William  6         17        17        15        15        13        14    15.17
------- --        ---   -------   ------- ---------   -------      ----  -------
Avgs     7      15.14     14.57     14.57     13.00     15.28     13.86    14.40

Upvotes: 2

Bob Shaffer
Bob Shaffer

Reputation: 655

I would do it something like this. First make empty files for every student:

cat /home/subject/* | cut -d' ' -f1 | sort -u | while read student_name; do > /home/students/$student ; done

Then I would go through each one and add the marks:

for student in `ls /home/students` ; do
    for file in /home/subjects/* ; do
        subject="`basename $file`"
        mark="`egrep "^$student [0-9]+" $file | cut -d' ' -f2`"
        if [ -z "$mark" ]; then
            echo "$subject 0" >> /home/students/$student
        else
            echo "$subject $mark" >> /home/students/$student
        fi
    done
done

something like that anyway

Upvotes: 1

Related Questions