1

I'm trying to write a code where an organization is organizing an virtual event with a raffle and is identifying a subset of the attendees that born in a certain month. Therefore, the event organizer needs to list only eligible members. The members’ information is given in a CSV file, and the record format in this data file is as follow:

firstName,lastName,YYYYMMDD

However, the year can either be in a Gregorian calendar, where the current year is 2022, or a Buddhist calendar, where the current year is 2565.

Deezel
  • 111
  • 6
  • 4
    Use `set -x` to enable debug tracing of the script. Or add more print statements, but either way you should do some debugging at this stage. – Useless May 12 '23 at 15:00
  • 2
    please update the question with the expected output (corresponding to the provided sample input) – markp-fuso May 12 '23 at 15:07
  • 2
    [Shellcheck](https://www.shellcheck.net/) identifies several problems with the code. The report includes links to more information about the problems and how to fix them. It's a good idea to run [Shellcheck](https://www.shellcheck.net/) on all new and modified shell code. – pjh May 12 '23 at 15:24
  • 3
    Try `awk` which is the tool for this job – Diego Torres Milano May 12 '23 at 16:35
  • Don't use a shell read loop for this, see [why-is-using-a-shell-loop-to-process-text-considered-bad-practice](https://unix.stackexchange.com/questions/169716/why-is-using-a-shell-loop-to-process-text-considered-bad-practice), just use an awk script as already suggested. – Ed Morton May 13 '23 at 14:15

2 Answers2

2

There are several issues with the code in the question, including syntax errors, logic errors, and some stylistic issues (e.g. use of magic numbers, unnecessary repetition). This Shellcheck-clean code attempts to fix most of them:

#! /bin/bash -p

month=$1
csvfile=$2

gregorian_year=$(date '+%Y')
buddhist_year=$((gregorian_year + 542))

months=( '' January February March April May June
            July August September October November December )

while IFS=, read -r forename surname yyyymmdd ; do
    birth_year=${yyyymmdd:0:4}
    birth_month=${months[10#${yyyymmdd:4:2}]}

    age=$(( (birth_year > gregorian_year)
            ? (buddhist_year - birth_year)
            : (gregorian_year - birth_year) ))

    if [[ $birth_month == "$month" ]] && (( age > 18 && age < 100 )); then
        printf '%s,%s,%s\n' "$forename" "$surname" "$yyyymmdd"
    fi
done < "$csvfile"
  • The 10# in birth_month=${months[10#${yyyymmdd:4:2}]} forces the month number to be treated as a decimal (base 10) number. Otherwise 08 and 09 will cause errors as invalid octal numbers. See Value too great for base (error token is "08").
  • Note that the code does not produce any output given the arguments May guest.csv and the guest.csv contents given in the question because the only person born in May is only 7 years old.
pjh
  • 6,388
  • 2
  • 16
  • 17
1

There are (at least) two reasons why the script doesn't show anything with the example data.

  1. There is a bug in the date extraction d="${line##,}, as this just tries to remove a leading comma. The line should be:
d="${line##*,}"
  1. There is a logic error when computing age: you always subtract the birth year from the current Gregorian year. This way, the only May-born person in your data file is 2023-2566 = -535 years old, which is not between 18 and 100. You need a way to tell which calendar is being used and compute the age accordingly.

Finally, I'm not sure that echo "%s\n" "$line" is going to give the intended output. You probably confused echo and printf.

David
  • 166
  • 6