2
\$\begingroup\$

I've started bash scripting recently and currently I'm working on a program that simplifies backuping using rsync . I'd appreciate any feedback on what I've done wrong / what can be improved.

#!/bin/bash

# Copyright (C) 2020 ##########, e-mail: ##################

# saver comes with NO WARRANTY. This program is completely free and you can
# redistribute it under the GNU General Public License conditions.
# See https://www.gnu.org/licenses/gpl-3.0.txt for more information

# saver was made to simplify the process of backuping using rsync.

# version 2.2-1

if [[ ! $(id -u) == 0 ]]; then # making sure the progream is executed as root
    exec sudo $0 "$@"
fi

for ((i=1; i=i; i++));  do # here input variables of the script are read one by another using the for loop
    [[ -z ${!i} ]] && break
    case ${!i} in
        \-*) # this is where all the '-' options are
            case ${!i} in
                *s*) option=$option"sync" ;;&  
                *d*) rsyncoptions+=(-n) ;;& 
                *r*) rsyncoptions+=(--delete) ;;&
                *p*) rsyncoptions+=(--progress) ;;&
                *v*) rsyncoptions+=(-v) ;;&
                *h*) option=$option"help" ;;&
                *i*) option=$option"diskinfo" ;;
            esac
        ;;

        *) # here the paths and disks are set
            if [[ -b ${!i} ]]; then
                if [[ -z $sp ]]; then
                    sdp="${!i}"
                else
                    tdp="${!i}"
                fi
            else
                if [[ -b /dev/${!i} ]]; then
                    if [[ -z $sp ]]; then
                        sdp="/dev/${!i}" # sdp = sync disk path
                    else
                        tdp="/dev/${!i}" # tdp = target disk path
                    fi
                else
                    if [[ ${!i} == /* ]]; then    
                        if [[ -z $sp ]]; then
                            sp="${!i}" # sp = sync path
                        else
                            tp="${!i}" # tp = target path
                        fi
                    fi
                fi
            fi
        ;;
    esac
done

case $option in
    sync) # the -s option
        if [[ ! -e $sp ]]; then # checking if the path to sync from exists
            echo -e "The path to copy wasn't provided or doesn't exist\nType in 'saver -h' to see the list of commands"
            logger "saver: The path to copy wasn't provided or doesn't exist"
            exit
        fi

        if [[ -z $tp ]]; then # checking if the target path isn't empty
            echo -e "The target path wasn't provided\nType in 'saver -h' to see the list of commands"
            logger "saver: The target path wasn't provided"
            exit
        fi

        fsp=$sp # these stand for the final paths that will be put to the rsync command (final sync path / final target path)
        ftp=$tp

        if [[ $sdp ]]; then 
            echo "Unmounting the disk to copy"
            umount -q $sdp # mounting the sync external disk to a folder that's made
            
            echo "Creating /mnt/saverbd"
            mkdir /mnt/saverbd | grep -q a
            
            echo -e "Mounting the disk to copy to /mnt/saverbd\n"
            mount $sdp /mnt/saverbd 
            
            fsp=/mnt/saverbd${sp} # updates the final path if it's from an external disk
        fi

        if [[ $tdp ]]; then
            echo "Unmounting the target disk"
            umount -q $tdp # mounting the target external disk to a folder that's made

            echo "Creating /mnt/savertd"
            mkdir /mnt/savertd | grep -q a

            echo -e "Mounting the target disk to /mnt/savertd\n"
            mount $tdp /mnt/savertd

            ftp=/mnt/savertd${tp} # updates the final path if it's from an external disk
        fi

        if [[ ! -e $tp ]]; then
            echo "Creating ${ftp}"
            mkdir -p $ftp | grep -q a 
        fi

        echo

        rsync -aAX "${rsyncoptions[@]}" $fsp --exclude={"/dev/*","/proc/*","/sys/*","/tmp/*","/run/*","/mnt/*","/media/*","/lost+found"} $ftp

        if [[ $sdp ]]; then # unmounting the sync external disk and deleting the mount folder
            echo -e "\nUnmounting the copied disk"
            umount -q $sdp

            echo -e "Deleting /mnt/saverbd\n"
            rm -rf /mnt/saverbd
        fi

        if [[ $tdp ]]; then # unmounting the target external disk and deleting the mount folder
            echo -e "\nUnmounting the target disk"
            umount -q $tdp
            
            echo -e "Deleting /mnt/savertd\n"
            rm -rf /mnt/savertd 
            
        fi
    ;;

    diskinfo)
        lsblk -o NAME,SIZE,MOUNTPOINT,FSTYPE # shows the available disks and partitions
    ;;

    help) # the help page
        echo -e "\n Copyright (C) 2020 ###########, e-mail: ##################
 Version 2.2-1
\n saver comes with NO WARRANTY. This program is completely free and you\n can redistribute it under the GNU General Public License conditions.\n See https://www.gnu.org/licenses/gpl-3.0.txt for more information
\n saver was made to simplify the process of backuping using rsync.
\n This program will automaticly exclude these directories from syncing:\n /dev ; /proc ; /sys ; /tmp ; /run ; /mnt ; /media ; /lost+found.
 When typing in the disk name, you can use the full '/dev/(disk)' or just\n provide the name of the disk.
 Any disk you mention will be unmounted at the end of the program.
\n Usage:
\n -s [disk to copy (empty for current disk)] [copied path]\n    [target disk (empty for current disk)] [target path]
\n Options:
\n -s     Sync folders
 -r     Delete any other files in the target folder
 -d     Preform a 'dry run', no changes will be made
 -p     Display progress for individual files (useful for large files)
 -v     Display files that are being processed
 -i     Show available disks/partitions
 -h     Show this help\n"
    ;;

    *) # in case no valid option is provided
        echo -e "Invalid option provided\nType in 'saver -h' to see the list of commands"
        logger saver: "Invalid option provided" 
    ;;
esac
\$\endgroup\$
  • 1
    \$\begingroup\$ It does, what I mean by that is if the code I've written can be somehow optimized, if there can be any compatibility issues. I'm new to bash scripting so I tend to make mistakes. \$\endgroup\$ – Arthur Jul 1 '20 at 11:12
  • 2
    \$\begingroup\$ Welcome to Code Review. To increase the odds of receiving answers about your script, it could be better to modify the title of you post to include a brief description of the task implemented by the script. \$\endgroup\$ – dariosicily Jul 1 '20 at 12:41
  • 1
    \$\begingroup\$ As @dariosicily indicated a better title might be bash backup script. \$\endgroup\$ – pacmaninbw Jul 1 '20 at 13:09
  • \$\begingroup\$ I changed the title, but "bash backup script" was taken. \$\endgroup\$ – Arthur Jul 1 '20 at 13:22
2
\$\begingroup\$

loop

for ((i=1; i=i; i++));  do 
    [[ -z ${!i} ]] && break

you know that $# hold number of argument from command line ?

you can use

for ((i=1; i <= $# ; i++))    

test

if [[ ! $(id -u) == 0 ]]

you know about != ? use

if [[ $(id -u) != 0 ]]

mkdir

what is the purpose of .. ?

mkdir /mnt/savertd | grep -q a

Do you want to get rid of "mkdir: cannot create directory ‘/mnt/savertd’: File exists" message ?

this won't work (message is printed on stderr, while | will collect stdout), use either

test -d /mnt/savertd || mkdir /mnt/savertd

or

mkdir -p /mnt/savertd
  • second form is better, it will create all directory along the path, first one will fail if /mnt do not exists.
  • mkdir -p will also not complain if directory exists.

echo -e

you have some

echo -e "Mounting the target disk to /mnt/savertd\n"

why use both -e (no new line) and a trailling \n (new line) ?

actually :

  • -e enable interpretation of backslash escapes

long echo -e in help section can be replaced by a here documentation

cat <<EOF
 Copyright (C) 2020 ###########, e-mail: ##################
 Version 2.2-1

 saver comes with NO WARRANTY. This program is completely free and you
 can redistribute it under the GNU General Public License conditions.
 See https://www.gnu.org/licenses/gpl-3.0.txt for more information
 saver was made to simplify the process of backuping using rsync.
 This program will automaticly exclude these directories from syncing:
   /dev ; /proc ; /sys ; /tmp ; /run ; /mnt ; /media ; /lost+found.
 When typing in the disk name, you can use the full '/dev/(disk)' or just provide the name of the disk.
 Any disk you mention will be unmounted at the end of the program.

 Usage:
 -s [disk to copy (empty for current disk)] [copied path]
    [target disk (empty for current disk)] [target path]
 Options:
 -s     Sync folders
 -r     Delete any other files in the target folder
 -d     Preform a 'dry run', no changes will be made
 -p     Display progress for individual files (useful for large files)
 -v     Display files that are being processed
 -i     Show available disks/partitions
 -h     Show this help
EOF
\$\endgroup\$
  • \$\begingroup\$ Thanks a lot I've heard about $#, but after I wrote that code so I haven't thought about that. All the other tips are really helpful too. I also wasn't sure how to use a here document, so I used a long echo, but this is much cleaner. Thanks again. \$\endgroup\$ – Arthur Jul 1 '20 at 12:49
  • \$\begingroup\$ I'm not sure about what you said of echo -e with \n . echo -e doesn't block a new line. echo -n does that. I used 'echo -e' with '\n' to have two new lines. \$\endgroup\$ – Arthur Jul 1 '20 at 13:06
  • \$\begingroup\$ yes my fault, -e enable interpretation of backslash escapes \$\endgroup\$ – Archemar Jul 1 '20 at 13:08

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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