5
\$\begingroup\$

A few days ago I wrote my first own Bash script - did some really small thing a few years ago - to keep my costs in check and get an overview over them. Therefore, I want it to include cost adding, viewing and changing. It works the way I wrote it, but I am not really sure if it's the "best" way to do it. Also I wanted to have an overview of the spent money for the current month, but don't have an idea how to do it.

If you have a few minutes to take a look over the code, I would really appreciate it.

#!/bin/bash

##declaration of Variables

#path to saving files
SavingFile=/home/tobias/TestSkripte/SpeicherBuchhaltung

Budget=$(cut -d= -f2 $SavingFile)
CostsHousehold=$(cut -d= -f4 $SavingFile)
CostsCar=$(cut -d= -f6 $SavingFile)
CostsLuxury=$(cut -d= -f8 $SavingFile)
CostsStudy=$(cut -d= -f10 $SavingFile)
CostsFood=$(cut -d= -f12 $SavingFile)
CostsHygiene=$(cut -d= -f14 $SavingFile)
CostsEntertainment=$(cut -d= -f16 $SavingFile)
CostsOther=$(cut -d= -f18 $SavingFile)
CostsMonthly=$(cut -d= -f20 $SavingFile)
CostsAll=$(cut -d= -f22 $SavingFile)
CostsMonthlyOld=

# check if script is called with parameters - if not show current budget
if [ $# -gt 0 ]
then
        case $1 in
                # add amount to *Costs and subtract it from the given budget
                "-a")
                        case $2 in
                               #asigning variables for chosen *Costs
                                #for adding *Costs only change or append lines in case statement
                                "-h") CostsTemp=$CostsHousehold; CostsName=CostsHousehold;;
                                "-c") CostsTemp=$CostsCar; CostsName=CostsCar;;
                                "-l") CostsTemp=$CostsLuxury; CostsName=CostsLuxury;;
                                "-s") CostsTemp=$CostsStudy; CostsName=CostsStudy;;
                                "-f") CostsTemp=$CostsFood; CostsName=CostsFood;;
                                "-y") CostsTemp=$CostsHygiene; CostsName=CostsHygiene;;
                                "-e") CostsTemp=$CostsEntertainment; CostsName=CostsEntertainment;;
                                "-o") CostsTemp=$CostsOther; CostsName=CostsOther;;


                        esac;
                        #add given amount to chosen *Costs
                        CostsNew=$((CostsTemp+$3));
                         #save new Costs
                        sed -i "s/$CostsName=$CostsTemp/$CostsName=$CostsNew/g" "$SavingFile";
                        #save new calculated Budget to file
                        BudgetNew=$((Budget-$3));
                        sed -i "s/Budget=$Budget/Budget=$BudgetNew/g" "$SavingFile";;

                #change the value of Budget or *Costs
                "--change")
                        #check if there are three options given --> if not you cant add costs
                        if [ $# -eq 3 ] ; then

                                #check input what shall be changed, if wanted only change/add options here
                                case $2 in
                                        "-b") nameTemp=Budget; changeTemp=$Budget;;
                                        "-h") nameTemp=CostsHousehold; changeTemp=$CostsHousehold;;
                                        "-c") nameTemp=CostsCar; changeTemp=$CostsCar;;
                                        "-l") nameTemp=CostsLuxury; changeTemp=$CostsLuxury;;
                                        "-s") nameTemp=CostsStudy; changeTemp=$CostsStudy;;
                                        "-f") nameTemp=CostsFood; changeTemp=$CostsFood;;
                                        "-y") nameTemp=CostsHygiene; changeTemp=$CostsHygiene;;
                                        "-e") nameTemp=CostsEntertainment; changeTemp=$CostsEntertainment;;
                                        "-o") nameTemp=CostsOther; changeTemp=$CostsOther;;
                                esac;

                                #changing the values in the saving File
                                sed -i "s/$nameTemp=$changeTemp/$nameTemp=$3/g" "$SavingFile"
                        #print error if there are not three options given
                        else
                                printf "%-s \n"  "no value given, please run again with value"
                        fi;;


                #print wanted costs
                "--print")

                        #assigning variables for case statement so they can be printed wiht -t otherwise they could be printed in case statement directly without variables
                        printB="Amount of Budget: $Budget"
                        printH="Household costs: $CostsHousehold"
                        printC="Car costs: $CostsCar"
                        printL="Luxury costs: $CostsLuxury"
                        printS="Sutdy costs: $CostsStudy"
                        printF="Food costs: $CostsFood"
                        printY="Hygine costs: $CostsHygiene"
                        printE="Enternainment costs: $CostsEntertainment"
                        printO="Other costs: $CostsOther"
                        printA="All costs added together are: $((CostsHousehold + CostsCar + CostsLuxury + CostsStudy + CostsFood + CostsHygiene + CostsEntertainment + CostsOther))"



                        case $2 in
                                "-b") printf "%s \n" "$printB";;
                                "-h") printf "%s \n" "$printH";;
                                "-c") printf "%s \n" "$printC";;
                                "-l") printf "%s \n" "$printL";;
                                "-s") printf "%s \n" "$printS";;
                                "-f") printf "%s \n" "$printF";;
                                "-y") printf "%s \n" "$printY";;
                                "-e") printf "%s \n" "$printE";;
                                "-o") printf "%s \n" "$printO";;
                                "-a") printf "%s \n" "$printA";;
                                "-t") printf "%s \t \t" "$printH"; printf "%-s \n" "$printC";
                                      printf "%s \t \t" "$printL"; printf "%-s \n" "$printS";
                                      printf "%s \t \t" "$printF";  printf "%-s \n" "$printY";
                                      printf "%s \t " "$printE";  printf "%-s \n" "$printO";
                                      printf "%s \n" "$printA";
                                      printf "%s \n" "$printB";;

                        esac;;



                #shows help; if new *Cost is changed/added, change help option too so it stays up to date
                "--help")
                        #adding costs
                        printf "%-s \n \t" "-a: add following costs:";
                        printf "%-s \n \t" "-h: Household related costs";
                        printf "%-s \n \t" "-c: car related costs";
                        printf "%-s \n \t" "-l: luxury related costs";
                        printf "%-s \n \t" "-s: stutdy related costs";
                        printf "%-s \n \t" "-f: food related costs";
                        printf "%-s \n \t" "-y: hygiene article related costs";
                        printf "%-s \n \t" "-e: entertainment related costs";
                        printf "%-s \n \n" "-o: other related costs";
                        #change costs
                        printf "%-s \n \t" "--change: change the value of the following costs:";
                        printf "%-s \n \t" "-b:  budget";
                        printf "%-s \n \t" "-h: household";
                        printf "%-s \n \t" "-c: car";
                        printf "%-s \n \t" "-l: luxury";
                        printf "%-s \n \t" "-s: study";
                        printf "%-s \n \t" "-y: hygiene";
                        printf "%-s \n \t" "-e: entertainment";
                        printf "%-s \n \n"   "-o: other";
                        #show costs
                        printf "%-s \n \t" "--print: prints following costs:";
                        printf "%-s \n \t" "-b:  budget";
                        printf "%-s \n \t" "-h: household";
                        printf "%-s \n \t" "-c: car";
                        printf "%-s \n \t" "-l: luxury";
                        printf "%-s \n \t" "-s: study";
                        printf "%-s \n \t" "-y: hygiene";
                        printf "%-s \n \t" "-e: entertainment";
                        printf "%-s \n \t" "-o: other";
                        printf "%-s \n \t" "-a: all costs added together";
                        printf "%-s \n \t" "-t: print all costs and budget";;



        esac

else
       printf "%-s \n" $Budget
fi

Stay healthy and have a good time.

\$\endgroup\$

1 Answer 1

2
\$\begingroup\$

Welcome to Code Review. I enjoyed reading your script. Thanks for submitting it to code review.

Good things

  • Your variable names are good.
  • The comments are very good.
  • The 8 space indentation warms the heart of someone who has been stuck with 4 space python code for too long. I like the way your inner case statements have the whole thing on one line, but if this was for code that was going to be maintained by other people it would be better to spread it out to more lines.
  • You have quoted most things well, but shellcheck would ding you for not quoting every variable substitution, on the off chance it would have space in it and cause problems. In most cases you are setting it not far above, so there's little chance of space creeping in, but it is a good habit to get into.

Suggestions

  • Move each option into a function.
  • Use the [[ form of conditionals to avoid some unpleasant edge cases.
  • Add at least one space after # comment delimiter.
  • Try shellcheck.
  • Consider switching your sh-bang line to #!/usr/bin/env bash to make your script portable to environments that have bash in weird places.

The data store

It took me a minute of reading through this to figure out how the data store worked and I was very impressed with the whole thing. It is a pretty unusual setup, but before talking about more traditional ways of doing this, let's look at how the existing design could be improved.

  • The first thing about a custom data format is to document it. I would add this as a series of comments at the top of the file. Including an example of how it is supposed to look would do wonders for somebody that has never seen this before.
  • I'm pretty happy with the way writing to the file works. sed should do this fine most of the time and hopefully you have backups. One potential flaw with your implementation is that you could have two keys that are ambiguously named. If you had Foo=1 and BarFoo=2, updating Foo=3 would change both. This is easy to fix by adding a ^ to the beginning of the match part of the regex.
  • The reading of the file is a bit more problematic. Your code hinges on the position of the fields in the file, but the code would be easier to read and resilient to new fields being added in the middle if you use the same variable scheme you use for writing to the file.
  • Wouldn't the file be more human-readable if the key-value pairs were one per line instead of one long line?

All of this really leads to the question of: why not use a database? Making a table in a SQL database and putting each cost in a record would make it easy to total up also. You can access PostgreSQL or SQLite from the command line. Either would be more resilient and scalable than writing your own stuff to mess with a text file.

Your current data store is essentially a key-value store. There are lots of those these days. The dbm family has been in UNIX for decades. More Internet-era alternatives include riak, cassandra, scylladb, and many others. These will fit with your current data model more easily than moving to SQL, but SQL will give you "reporting" abilities for free where you have to build that yourself with the key-value stores.

\$\endgroup\$
1
  • 1
    \$\begingroup\$ Thank you really much, I'll implement your ideas. To be honest I forgot about a database - it's a good idea and the longer I think about it - the dumber I feel not have had the idea myself - I will defenitly implement it. Thank you for your time and helping a newbie out :) \$\endgroup\$
    – tdonot
    Commented Sep 16, 2020 at 13:16

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.