1

I'm making birthday tracker for only a few people and was wondering how to combine all if statements into one since date and name are the only different things in every if statement, rest is same format.

date=`date +%b-%d`
echo $date

if [ $date  == "Sep-09" ]; then
    echo "Happy Birthday abc!"
fi

if [ $date  == "Oct-11" ]; then
    echo "Happy Birthday xyz!"
fi
.
.
.

I think I can use positional arguments ($1 for date, $2 for name..) but can't figure out how to use it for multiple dates and names. Or if there's any better way, that would be good to know as well.

Learner
  • 51
  • 8

4 Answers4

5

Using case:

...
case "$date" in
    "Sep-09") name=abc;;
    "Oct-11") name=xyz;;
       ...
           *) exit;; # if no condition met
esac

echo "Happy Birthday $name!"
Ivan
  • 6,188
  • 1
  • 16
  • 23
2

You can use map/dictionary data structure here. You can check example here: How to define hash tables in Bash?

Vadim Beskrovnov
  • 941
  • 6
  • 18
  • 2
    Good idea! Actually you can also use simple indexed arrays if set data with only digits, like so: `date=$(date +%m%d); name[0908]=abc; name[1011]=xyz; echo "Happy Birthday ${name[$date]}!"` – Ivan Sep 08 '22 at 12:25
1

You could store your birthdays in a text file and read them with while and read.

read will let you read into one or more variables, and if there's more words than variables it stuffs all remaining words into the last variable, so it's perfect to use here when the person's name could be multiple words:

help read

The line is split into fields as with word splitting, and the first word is assigned to the first NAME, the second word to the second NAME, and so on, with any leftover words assigned to the last NAME. Only the characters found in $IFS are recognized as word delimiters.

I also suggest using the date format +%m-%d rather than +%b-%d as it means the dates in your file will be in order (e.g. you can run sort birthdays.txt to see everyone in birthday order, which you couldn't do if the month was represented by 3 letters).

birthdays.txt:

09-08 John Smith
10-11 Alice

checkbirthdays.sh:

#!/bin/bash
nowdate="$(date +%m-%d)"
(
  while read checkdate name ; do
    if [ "$nowdate" = "$checkdate" ] ; then
      echo "Happy Birthday $name!"
    fi
  done
) < birthdays.txt

output of ./checkbirthdays.sh on September 8th:

Happy Birthday John Smith!
Keiji
  • 880
  • 5
  • 12
  • 2
    You can add `break` or `exit` into `if` statement to stop the loop when condition met. – Ivan Sep 08 '22 at 12:30
  • 2
    Or use `grep` to get the needed string without loop like so: `read checkdate name < <(grep "$(date +%m-%d)" birthdays.txt)` – Ivan Sep 08 '22 at 12:35
  • @Keiji using .txt file is a pretty good and clean approach too. Thanks for this. Super helpful. – Learner Sep 08 '22 at 12:58
  • @Ivan Not using a `break`/`exit` was intentional in case you have multiple people in the file with the same birthday :) Yes, a `grep` would be a much better solution, but this solution is a good illustration of how one can use `while`/`read`, I think. – Keiji Sep 09 '22 at 12:29
1

Assuming you have bash 4.0 or newer and, names don't contain whitespace or glob characters, use of an associative array would be appropriate for this task:

#!/bin/bash

declare -A birthday_to_names=(
    ['Sep-09']='abc def'
    ['Oct-11']='xyz'
)

names=${birthday_to_names[$(date +%b-%d)]}
[[ $names ]] && printf 'Happy Birthday %s!\n' $names

Note that this version congratulates all birthday people if there are multiple names associated with the given birthday.

M. Nejat Aydin
  • 9,597
  • 1
  • 7
  • 17