BASH - Safe check before running "rm -fr $FOLDER" - bash

I have a script which really needs an rm -fr on a specific folder
I'd like to make this as safe as possible. I started this script below but I was wondering if there's anything else I missed.
folder=""
if [[ ! -d "$folder" ]]; then
echo "Error: is not a folder"
elif [[ "$folder" == "/" ]]; then
echo "Error: folder points to root"
elif [[ "$folder" == "../"* ]]; then
echo "Error: folder start with ../"
elif [[ "$folder" == *"/.."* ]]; then
echo "Error: folder contains /.."
elif [[ "$folder" == *"/*"* ]]; then
echo "Error: folder ends with /*"
else
rm -fr "$folder"
fi
Update: added the check for "/"

If you want to be as safe as possible, you could perhaps...
Make sure any globbing is done first :
shopt -s nullglob
declare -a folders=(folder_or_glob)
Iterate over each element of the array, one at a time, and operate on the canonical path.
for f in "${folders[#]-}"
do
[[ $f ]] || continue
candidate="$(realpath -e -s "$f")" || continue
ok_to_delete "$candidate" || continue
rm -rf "$candidate"
done
Use function ok_to_delete to test :
ok_to_delete()
{
[[ -d $1 ]] || continue # Is directory
[[ $1 != / ]] || continue # Not root
[[ "${1%/*}" ]] || continue # At least two levels deep
(... add any test you want ...)
}
There is a bit of redundancy here (e.g. not root + 2 levels deep), but this is just to give you ideas.

Instead of checking the path name of folder, I would rather to check the contents in that folder, file's size/user/timestamp/keywork/extension, etc, or whatever you care most about. This is a more safe method for you, this's just my two cents.

Related

default value for a variable in bash script

I want to give my variable in bash script a default value.
Here is the script :
unamestr=`uname`
if [[ "$unamestr" == 'Linux' ]] || [[ "$unamestr" == 'Darwin' ]]; then
DEFAULT_LOCATION="/home/$USER/.kaggle/competitions/$1"
elif [[ "$unamestr" == 'CYGWIN' ]] || [[ "$unamestr" == 'MINGW' ]]; then
DEFAULT_LOCATION="C:\\Users\\$USER\\.kaggle\\competitions\\$1"
fi
kaggle competitions download -c $1
KAGGLE_LOCATION=${2:-DEFAULT_LOCATION}
mv KAGGLE_LOCATION .
mkdir data
mv $1/*.zip data/
mv $1/*.csv data/
cd data/
unzip *.zip
rm *.zip
When I do echo $DEFAULT_LOCATION, the correct value comes up. But when I do $KAGGLE_LOCATION and dont enter any cmd argument, I get DEFAULT_LOCATION as output instead of the actual value. What is wrong with my code?
PS: I have never used a Mac, so I am not sure if location is same for unix as Mac. If its different, please comment.
You are not expanding the variable, try expanding the variable like "$KAGGLE_LOCATION"
unamestr=`uname`
if [[ "$unamestr" == 'Linux' ]] || [[ "$unamestr" == 'Darwin' ]]; then
DEFAULT_LOCATION="/home/$USER/.kaggle/competitions/$1"
elif [[ "$unamestr" == 'CYGWIN' ]] || [[ "$unamestr" == 'MINGW' ]];then
DEFAULT_LOCATION="C:\\Users\\$USER\\.kaggle\\competitions\\$1"
fi
kaggle competitions download -c $1
KAGGLE_LOCATION=${2:-DEFAULT_LOCATION}
mv "$KAGGLE_LOCATION" .
mkdir data
mv $1/*.zip data/
mv $1/*.csv data/
cd data/
unzip *.zip
rm *.zip

Bash double condition: "Yy" or free string but not "Nn"

I'm trying for my update script to find a way to get a double if with conditions: I can type "Yy" to download the latest version, or type anything else (another version number) but which is not "Nn" (basically cancelling the task).
Tried that, it's working for the "Yy" and the free text but I still can type "Nn" and it downloads me the n.zip file (it should stop the task)
if [[ "$latest_confirm" =~ ^[Yy]$ ]] || [[ ! "$latest_confirm" =~ ^[Nn]$ ]]; then
I think you want something like this:
latest_version=4.9
read latest_confirm
if [[ $latest_confirm =~ ^[Yy]$ ]]; then
version=$latest_version
elif [[ $latest_confim =~ ^[Nn]$ ]]; then
...
else
version=$latest_confirm
fi
...
Instead of the else, you might want another reg-ex comparison to make sure the value is a valid version number.
Finally worked this way if anyone wanting to know about the solution to update my installed softwares:
upgrade_pma() {
old="pma-old"
new="pma-new"
header "PHPMYADMIN UPGRADE"
latest_version=$(check_version pma)
ask_latest "PHPMyAdmin" $pma_version $latest_version true
# Detect version to install
if [[ "$install_version" =~ ^[Yy]$ ]]; then
install_version_nb=$latest_version
version_url="https://files.phpmyadmin.net/phpMyAdmin/$latest_version/phpMyAdmin-$latest_version-all-languages.zip"
else
install_version_nb=$install_version
version_url="https://files.phpmyadmin.net/phpMyAdmin/$install_version/phpMyAdmin-$install_version-all-languages.zip"
fi
# Download file and process install
if [[ ! "$install_version" =~ ^[Nn]$ ]]; then
cd $tools/phpmyadmin
if wget -O pma.zip "$version_url"; then
unzip pma.zip && rm $_
mkdir $new && mv phpMyAdmin-$install_version_nb-all-languages/* $new
cp -R www/config.inc.php $new/
mv www $old && mv $new www
chowwwn . && cmf644 . && cmd755 .
# Check if everything works, then delete the old version
section "PHPMyAdmin upgraded successfully"
ask_deleteold
if [[ "$deleteold" =~ ^[Yy]$ ]]; then
rm -rf $old phpMyAdmin-$install_version-all-languages/
fi
fi
fi
exit 0
}

Getting stuck in a logic statement

I'm writing a BASH script to purge the cache from a web server. The script is designed to take arguments from positional parameters. "ShellCheck.net" is telling me that my script is functionally correct, but when I test it I'm getting error where I shouldn't ... so I thought I'd ask for some folks to put fresh eyes on it. Take a look, I'll continue below and describe my problem:
#!/bin/bash
#
# Verify the user running is root, if not, fail.
if [[ "$UID" -ne "0" ]]; #added-1438279711
then
echo 'Only ROOT may run this script.';
exit 1;
fi
#
# Set the variables
BASE="/path/to/folder/foo/bar/" #added-1438279711
DOMAINOPT="$1" #added 1438451428
PATHOPT="$2" #added 1438451428
#
# Define Functions
function usage() { #added-1438382631
echo -en "Proper Usage:\n\n"
echo -en "\tSpecify the domain to be used\n"
echo -en "\tUsage: \"cleancache.sh abc.com\"\n"
echo -en "\t\tNote: This option will search for files and folders, recursively, within the domain folder, and remove them.\n\n"
echo -en "\tSpecify the URI you'd like to act upon within the domain\n"
echo -en "\tUsage: \"cleancache.sh abc.com /path/to/folder/\"\n"
echo -en "\t\tNote: This option will search for files and folders, recursively,\n\t\twithin the specified path, and remove them. Removing a single file is not currently supported with this script.\n\n"
}
#
# Validate the input
if [[ ! -z "$DOMAINOPT" ]] && [[ "$DOMAINOPT" != "^[A-Za-z0-9-]*[\.][a-z]*$" ]] #added-1438462778
then
clear
echo -en "Please follow the proper format for the DOMAIN option\n\n"
usage
exit 1
elif [[ ! -z "$DOMAINOPT" ]] && [[ "$DOMAINOPT" = "^[A-Za-z0-9-]*[\.][a-z]*$" ]]
then
DOMAINOPT="$DOMAINOPT"
else
clear
echo -en "Please enter a domain!\n\n"
usage
exit 1
fi
if [[ ! -z "$PATHOPT" ]] && [[ "$PATHOPT" != "^[\/][\S]*[\/]$" ]] #added-1438456371
then
clear
echo "Please follow the proper format for the PATH option"
usage
exit 1
elif [[ ! -z "$PATHOPT" ]] && [[ "$PATHOPT" = "^[\/][\S]*[\/]$" ]]
then
PATHOPT="$PATHOPT"
else
echo ""
fi
#
# Doing Stuff
if [[ "$#" -gt "2" ]]
then
echo -en "Too many arguments!\n\n"
usage
exit 1
elif [[ "$#" -eq "2" ]]
then
echo "Purging Cache in \"$BASE$DOMAINOPT$PATHOPT\""
find "$BASE""$DOMAINOPT""$PATHOPT" -type d -exec rm -rf {} \;
find "$BASE""$DOMAINOPT""$PATHOPT" -type f -exec rm -f {} \;
echo "Purging Complete"
exit 0
else
echo "Purging Cache in \"$BASE$DOMAINOPT\""
find "$BASE" -type d -name "$DOMAINOPT" -exec rm -rf {} \;
mkdir -p "$BASE$DOMAINOPT" && chown apache:apache "$BASE$DOMAINOPT" && chmod 755 "$BASE$DOMAINOPT"
echo "Purging Complete!"
echo "Creating \".stat\" file"
echo "" > "$BASE""$DOMAINOPT""/.stat"
if [[ -f "$BASE""$DOMAINOPT""/.stat" ]] #added-1438387045
then
echo "$BASE$DOMAINOPT/.stat file created!"
fi
fi
echo "All Operations Complete, exiting now!"
Everything responds normally if you run the script without any arguments (Please enter a domain), It responds normally if you try to enter a path before a domain ... but when I do it correctly, when I type: "cleancache.sh abc.com", I get an error like i haven't met the required pattern ("Please follow the proper format for the DOMAIN option") ... when that is exactly write! ... I don't understand what I'm missing, been banging my head all day, no joy.
PLEASE HELP!
Use this to match a regex:
[[ "$DOMAINOPT" =~ ^[A-Za-z0-9-]*[\.][a-z]*$ ]]
or this:
[[ ! "$DOMAINOPT" =~ ^[A-Za-z0-9-]*[\.][a-z]*$ ]]
Don't quote the regex.

Why am I getting unbound variable i bash?

I have the following script which works for the most part till it hits a specific line:
#!/usr/bin/env bash
set -eux
# Go Home.
cd /vagrant/Freya/
CLEANED_ASSETS=false
## Clean up time!
## Remove all vendor and composer.lock folders - just because.
for f in *; do
if [[ -d $f ]]; then
if [[ $f != ".git" ]] && [[ $f != "bin" ]] && [[ $f != "docs" ]]; then
if [[ $f == "Loader" ]] && [[ $CLEANED_ASSETS == false ]]; then
cd "$f/"
if [[ -d "Assets" ]]; then
cd Assets/
rm -rf vendor composer.lock docs
let $CLEANED_ASSETS=true
cd ../../
fi
fi
cd "$f/"
rm -rf vendor composer.lock docs
cd ../
fi
fi
done
The issue is when it hits let $CLEANED_ASSETS=true I am not sure the proper way to set this variable to true, so it never enters this loop again. I keep getting:
+ let false=true
bin/clean-directories: line 21: true: unbound variable
CLEANED_ASSETS=true
No let, no $.
In particular, the let causes true to be treated as a variable name (searched for a numeric value), and referring to variable names that don't exist gets you flagged by set -u.

Bash script to copy files from cd to directory

I have no bash scripting knowledge unforunately. I need a script that reads a cd copied ONE file from the cd to a destination and renames it. Here is my code
#!/bin/bash
mount /dev/cd0 /mnt/
for file in /mnt/*
do
if($file == SO_CV*)
cp SO_CV* /usr/castle/np_new/CVFULLPC.BIN
else if($file == SO_PC*)
cp SO_PC* /usr/castle/np_new/PCMAP.BIN
else if($file == MS_PC*)
cp MS_PC* /usr/castle/np_new/FULLPC.BIN
else if($file == MS_MC*)
cp MS_MC* /usr/castle/np_new/MBFULLPC.BIN
done
umount /mnt/
Could someone tell me if this is even valid bash scripting, or what mistakes I might have made.
Thanks
Jim
Syntax problems. Try this code:
#!/bin/bash
mount /dev/cd0 /mnt/
for file in /mnt/*; do
if [[ "$file" == SO_CV* ]]; then
cp SO_CV* /usr/castle/np_new/CVFULLPC.BIN
elif [[ "$file" == SO_PC* ]]; then
cp SO_PC* /usr/castle/np_new/PCMAP.BIN
elif [[ "$file" == MS_PC* ]]; then
cp MS_PC* /usr/castle/np_new/FULLPC.BIN
elif [[ "$file" == MS_MC* ]]; then
cp MS_MC* /usr/castle/np_new/MBFULLPC.BIN
fi
done
umount /mnt/
an alternative:
#!/bin/bash
error_in_cp () {
{ printf "An ERROR occured while trying to copy: '\s' to its dest file.\n" "$#"
printf "Maybe there were more than 1 file ? or you didn't have the rights necessary to write the destination?"
printf "Exiting..."
} >&2 #to have it on STDERR
exit 1
}
mount /dev/cd0 /mnt/ &&
for file in /mnt/*; do
case "$file" in
SO_CV*) cp -p SO_CV* /usr/castle/np_new/CVFULLPC.BIN || error_in_cp "$file" ;;
SO_PC*) cp -p SO_PC* /usr/castle/np_new/PCMAP.BIN || error_in_cp "$file" ;;
MS_PC*) cp -p MS_PC* /usr/castle/np_new/FULLPC.BIN || error_in_cp "$file" ;;
MS_MC*) cp -p MS_MC* /usr/castle/np_new/MBFULLPC.BIN || error_in_cp "$file" ;;
*) echo "oops, forgot to handle that case: '$file' . ABORTING. "
exit 1
;;
esac
done # no "&&" here so you always umount /mnt/ even if you aborted the copy or the latest command went wrong
umount /mnt/
note: I changed the "cp" to "cp -p" to prevere rights & times... adjust if needed.
note that "&&" at the end of the line is ok
(no need to :
command && \
something
)
You may need to add { and } around each part if there is more than 1 element (here, "case ... esac" is one element, so it's fine)

Resources