Reputation: 415
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 :
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
Reputation: 70967
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/
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
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
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;-)
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
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
This script was built for bash v4.4.12 and tested under bash v5.0.
You could download bigger demo script: scholl-averages-demo.sh (view in browser as text .txt).
Always pure bash without forks, but with
.
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
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