In one of my scripts, I wanted to create a log file that was dynamic based on how the script was running. For example, if the script was doing something like waiting on another script or generated an error for whatever reason, I wanted the log file name to change to reflect that.

I started with this. I used the $STATUS as the switch and called a function (setstatus) to change that based on what was passed (by another function or whatever etc..):

  1. In a conf file (script.conf):

    ... LOG="$NAME-$STATUS-$FILENAME.log" ...
    
  2. In the actual script:

    RUNNING="Running"  ## Doing this so I can easily define what the text looks like for each case
    WAITING="Waiting"
    ERROR="ERROR"
    FINISHED="Finished"
    STATUS="$RUNNING"
    
    source script.conf
    ...
    ...
    setstatus () {
    # This function changes the status of the script and changes the log file appropriately (e.g. $NAME-$STATUS-$FILENAME.log)
    # It takes in the new status to set. Don't call this unless you actually want to change the file while mid/end-script.
    
    # Available status'es are:
    #1# Running - the script is currently running
    #2# Waiting - the script is waiting on another script to continue
    #3# ERROR - the script has finished but with an error.
    #4# Finished - the script has completely finished successfully
    
    if [ "$LOG" ]; then
    OLDLOG="$LOG"
    case "$1" in
    1)  [ "$STATUS" != "$RUNNING" ] && STATUS="$RUNNING" && mv "$OLDLOG" "$LOG"
    ;;
    2)  [ "$STATUS" != "$WAITING" ] && STATUS="$WAITING" && mv "$OLDLOG" "$LOG"
    ;;
    3)  [ "$STATUS" != "$ERROR" ] && STATUS="$ERROR" && mv "$OLDLOG" "$LOG"
    ;;
    4)  [ "$STATUS" != "$FINISHED" ] && STATUS="$FINISHED" &&  mv "$OLDLOG" "$LOG"
    ;;
    esac
    fi
    }
    
  3. What actually came from that was a non-working script. The mv command would get mad at me because I was trying to rename a file the same name it already had. Hence the $STATUS as shown in the filename wouldn’t ever change from *“Running”*…wonder why?
    Turns out, just because you use a variable later on, doesn’t mean variables defined in it are re-eval’ed. Once its defined, the definition of said variables used in a variable are expanded to what they exist as at the time the variable is set. In my case, this is as soon as the script starts hence the “*Running*”.

  4. How can we fix this…hmm. Maybe an eval “LOG=$LOG” will re-eval for us? Nope. Even if you throw a ‘\’ before the variable $STATUS, this doesn’t work. Why? Because the variable was already defined at the start of our script (as soon as we source the conf file).

  5. What I did. I created a variable named LOGTEMPLATE, escaped out all variables in it, and then instead of an eval “LOG=$LOG”, I did a eval “LOG=$LOGTEMPLATE”.
    When you run this, the LOG variable is set to the expanded $LOGTEMPLATE which then in turn expands the $STATUS and other inside variables. Hence whatever $STATUS is set to at that moment is what gets loaded into the $LOG variable…clever huh?

  6. So now, my script looks like:

script.conf:

 ... LOGTEMPLATE="$NAME-\$STATUS-$FILENAME.log" ...

The script:

RUNNING="Running"  ## Doing this so I can easily define what the text looks like for each case
WAITING="Waiting"
ERROR="ERROR"
FINISHED="Finished"
STATUS="$RUNNING"

source script.conf
[ "$LOGTEMPLATE" ] && eval "LOG=$LOGTEMPLATE"
...
...
setstatus () {
# This function changes the status of the script and changes the log file appropriately (e.g. $NAME-$STATUS-$FILENAME.log)
# It takes in the new status to set. Don't call this unless you actually want to change the file while mid/end-script.

# Available status'es are:
#1# Running - the script is currently running
#2# Waiting - the script is waiting on another script to continue
#3# ERROR - the script has finished but with an error.
#4# Finished - the script has completely finished successfully

if [ "$LOG" ]; then
    OLDLOG="$LOG"
    case "$1" in
        1)  [ "$STATUS" != "$RUNNING" ] && STATUS="$RUNNING" && eval "LOG=$LOGTEMPLATE" && mv "$OLDLOG" "$LOG"
            ;;
        2)  [ "$STATUS" != "$WAITING" ] && STATUS="$WAITING" && eval "LOG=$LOGTEMPLATE" && mv "$OLDLOG" "$LOG"
            ;;
        3)  [ "$STATUS" != "$ERROR" ] && STATUS="$ERROR" && eval "LOG=$LOGTEMPLATE" && mv "$OLDLOG" "$LOG"
            ;;
        4)  [ "$STATUS" != "$FINISHED" ] && STATUS="$FINISHED" && eval "LOG=$LOGTEMPLATE" &&  mv "$OLDLOG" "$LOG"
            ;;
    esac
fi
}

Thanks linuxquestions.

Mario Loria is a builder of diverse infrastructure with modern workloads on both bare-metal and cloud platforms. He's traversed roles in system administration, network engineering, and DevOps. You can learn more about him here.