2

I have created a bash script that creates incremental backups every hour, it is called via crontab. This script works flawlessly and only takes about a few minutes to run every time.

The issue is that I have in the beginning of the script a conditional that lets me disable backups temporarily except for the one at 5 am, simply by creating an empty file 'backup.disabled'.

Nothing else can create these directories here except this backup script, and of course the backup script does not backup it's own backups, and I confirmed that they only take about 1-5 minutes to run every time, from one NVME drive to another.

The conditional does not seem to work correctly, although it is pretty basic.

# Do not run the backup if the file 'backup.disabled' exists and it is not currently 5 am.
if [[ -e backup.disabled && "`date +%H`" -ne "05" ]] ; then
    exit
fi

# Start the backup
BACKUP_DIR_NAME="`date +%F_%H%M`"
#.... everything after this works perfectly, no need to show it

Here are the resulting backup directories after creating 'backup.disabled' on Oct 13 around 21:30 :

#...
drwxr-xr-x 1 root root  34 Oct 13 07:05 2020-10-13_0700
drwxr-xr-x 1 root root  34 Oct 13 08:03 2020-10-13_0800
drwxr-xr-x 1 root root  34 Oct 13 09:02 2020-10-13_0900
drwxr-xr-x 1 root root  34 Oct 13 10:02 2020-10-13_1000
drwxr-xr-x 1 root root  34 Oct 13 11:01 2020-10-13_1100
drwxr-xr-x 1 root root  34 Oct 13 12:01 2020-10-13_1200
drwxr-xr-x 1 root root  34 Oct 13 13:02 2020-10-13_1300
drwxr-xr-x 1 root root  34 Oct 13 14:04 2020-10-13_1400
drwxr-xr-x 1 root root  34 Oct 13 15:01 2020-10-13_1500
drwxr-xr-x 1 root root  34 Oct 13 16:01 2020-10-13_1600
drwxr-xr-x 1 root root  34 Oct 13 17:04 2020-10-13_1700
drwxr-xr-x 1 root root  34 Oct 13 18:06 2020-10-13_1800
drwxr-xr-x 1 root root  34 Oct 13 19:03 2020-10-13_1900
drwxr-xr-x 1 root root  34 Oct 13 20:01 2020-10-13_2000
drwxr-xr-x 1 root root  34 Oct 13 21:01 2020-10-13_2100
# at this point I created 'backup.disabled'
drwxr-xr-x 1 root root  34 Oct 14 05:05 2020-10-14_0500
drwxr-xr-x 1 root root  34 Oct 14 08:01 2020-10-14_0800
drwxr-xr-x 1 root root  34 Oct 14 09:02 2020-10-14_0900
drwxr-xr-x 1 root root  34 Oct 15 05:01 2020-10-15_0500
drwxr-xr-x 1 root root  34 Oct 15 08:00 2020-10-15_0800
drwxr-xr-x 1 root root  34 Oct 15 09:00 2020-10-15_0900

The issue: It turns out that it creates the backup at 5 am, 8 am and 9 am... but it's only supposed to create the one at 5 am when the file 'backup.disabled' exists, unless I am missing something ???

The backups at 8 and 9 am all seem to be very normal backups...

Either way I find this issue very weird and would like to understand what is happening.

Thank you.

ZOlivier
  • 224
  • 1
  • 11
  • 4
    With `-ne "05"` you do a numerical comparison. The date command will give `08` and `09`; that are invalid numbers (preceding `0` means octal.), Replace `-ne` by `!=` for a string comparison. – Wiimm Oct 15 '20 at 16:27
  • Incidentally, the irc.freenode.org #bash factoid for this issue is [`!august`](http://wooledge.org/~greybot/meta/august); to quote its current contents: *August is the month when all your scripts break because you placed `$(date +%m)` in a variable and tried to do arithmetic with it, without removing the leading zeros. 08 is considered octal. Use `$((10#$month))` to force decimal, or strip the zero.* – Charles Duffy Oct 15 '20 at 19:32

2 Answers2

2

The issue arises when comparing strings (of numbers in this case) with numeric test operands (eg, -eq).

Consider the following:

$ for i in {01..12}
do
   echo "++++++++++++++ ${i}"

   if [[ "${i}" -ne "05" ]]
   then
       echo "not equal"
       continue
   fi

   echo "here i am"
done

++++++++++++++ 01
not equal
++++++++++++++ 02
not equal
++++++++++++++ 03
not equal
++++++++++++++ 04
not equal
++++++++++++++ 05
here i am
++++++++++++++ 06
not equal
++++++++++++++ 07
not equal
++++++++++++++ 08
./testdt: line 7: [[: 08: value too great for base (error token is "08")
here i am
++++++++++++++ 09
./testdt: line 7: [[: 09: value too great for base (error token is "09")
here i am
++++++++++++++ 10
not equal
++++++++++++++ 11
not equal
++++++++++++++ 12
not equal

Notice that for i = 08 / 09 we get conversion errors because the shell is trying to treat the strings '08' and '09' and base-8 (octal) numbers. [see these Q&As covering this same issue: here and here]

Also notice that after the error the scripting continues and prints the 'here i am' message, which in your script would equate to performing the backup (@ 08:00 and 09:00).

To address this issue you could either test the values as numerics (using -ne) or use != if you wish to continue comparing strings, eg:

$ for i in {01..12}
do
   echo "++++++++++++++ ${i}"

   if [[ "${i}" != "05" ]]                    # use '!=' to compare strings
   then
       echo "not equal"
       continue
   fi

   echo "here i am"
done

++++++++++++++ 01
not equal
++++++++++++++ 02
not equal
++++++++++++++ 03
not equal
++++++++++++++ 04
not equal
++++++++++++++ 05
here i am
++++++++++++++ 06
not equal
++++++++++++++ 07
not equal
++++++++++++++ 08               # no error; 'here i am' message not printed
not equal
++++++++++++++ 09               # no error; 'here i am' message not printed
not equal
++++++++++++++ 10
not equal
++++++++++++++ 11
not equal
++++++++++++++ 12
not equal
markp-fuso
  • 28,790
  • 4
  • 16
  • 36
0

Done with Bash's built-in only, no external date command and no sub-shell spawn.

#!/usr/bin/env bash

# Declare now_hour as an int type variable
declare -i now_hour

# Format and convert current hour to an integer base 10 regardless of leading zero
printf -v now_hour '10#%(%H)T'

# Do not run the backup if the file 'backup.disabled' exists
# and it is not currently 5 am.
if [[ -e backup.disabled && $now_hour -ne 5 ]]; then
  exit
fi

# Start the backup
printf -v BACKUP_DIR_NAME '%(%F_%H%M)T'
#.... everything after this works perfectly, no need to show it

Now with POSIX shell grammar only (no bashism) sub-shell and external date command are absolutely needed:

#!/usr/bin/env sh

# Capture current hour
now_hour=$(date +%H)

# Strip-out leading 0 for a valid shell base 10 integer
now_hour=${now_hour#0}

# Do not run the backup if the file 'backup.disabled' exists
# and it is not currently 5 am.
if [ -e backup.disabled ] && [ "$now_hour" -ne 5 ]; then
  exit
fi

# Start the backup
BACKUP_DIR_NAME="$(date +%F_%H%M)"
#.... everything after this works perfectly, no need to show it
Léa Gris
  • 17,497
  • 4
  • 32
  • 41
  • Might be worth mentioning the version requirement for this feature. (Off the top of my head I want to say it was introduced in 4.3, but I'm not sure if that's right). – Charles Duffy Oct 15 '20 at 19:31