bash script to search server for specific /users/ and then delete - linux

I am kind of new to bash scripting and know some of the basics. I need help creating a bash script that I can run that will search a server (or multiple servers) for a list of specific /home/users and then if it finds a /user from the list it deletes that user's directory using sudo rm -rf /user. If it doesn't find a listed user, it does nothing. this script should be adaptable to run on multiple servers that may or may not have any users on them. Any help would be appreciated.
I posted what I have been thinking. I would start by creating a file called userList containing all the home directories I want to delete, one per-line:
/Users/user1
/Users/user30
#!/usr/bin/bash
do
if [[ -d $dir ]]
then
rm -R $dir
echo “Directory $dir found and deleted.”
else
echo “Directory $dir not found.”
fi
done < userList

you're close:
#!/bin/bash
while read dir; do # <-- there's a change here
if [[ -d $dir ]]
then
sudo rm -rf $dir # <-- and here
echo “Directory $dir found and deleted.”
else
echo “Directory $dir not found.”
fi
done < userList

Related

remove a subfolder with bash if it exsits

I have a subfolder of something like:
USERS = /opt/users/
I want to delete a subfolder of /opt/users without defining a new variable like:
if [ -d $USERS/phones ] && [-d $USERS/emails]; then
rm -Rf $USERS/phones
rm -RF $USERS/emails
else
echo "Folder does not exist, continuing the process"
fi
The question is can you suggest some smarter way or is good enough? I don't know how to handle if one of the folders is not existing or if I have another && condition? and the two commands rm -Rf I am not sure of the way it looks kinda ugly. Any recommendations?
First of all, some general bash tips:
Consider quoting strings
When to wrap quotes around a shell variable?
No spaces around =
Command not found error in Bash variable assignment
That said, lets take a closer look at rm -f:
-f, --force
ignore nonexistent files and arguments, never prompt
So there's no need to add extra checks if the folder exists.
You can change the code to:
USERS="/opt/users/"
rm -Rf "$USERS/phones"
rm -RF "$USERS/emails"
If you want to add a echo for each file that does not exist, we'll need to check if it exist:
How can I check if a directory exists in a Bash shell script?
Using a short if/else, the code could look something like this:
USERS="/opt/users/"
[[ -d "$USERS/phones" ]] && rm -Rf "$USERS/phones" || echo '/phones does not exists'
[[ -d "$USERS/emails" ]] && rm -Rf "$USERS/emails" || echo '/emails does not exists'
The above code can be simplified by using array's:
declare -a folders=("phones" "emails")
for i in "${arr[#]}"
do
[[ -d "$USERS/$i" ]] && rm -Rf "$USERS/$i" || echo "/$i does not exists"
done
A variation on OP's code assuming there could be a variable number of subdirectories to remove:
users=/opt/users/ # refrain from using ALL CAPS for user-defined variables
rmlist=(phones emails 'other schtuff') # array of subdirectory names
for subdir in "${rmlist[#]}"
do
[[ -d "${users}/${subdir}" ]] && rm -Rf "${users}/${subdir}" && continue
echo "Folder ${users}/${subdir} does not exist, continuing the process"
fi

linux zip and exclude dir via bash/shell script

I am trying to write a bash/shell script to zip up a specific folder and ignore certain sub-dirs in that folder.
This is the folder I am trying to zip "sync_test5":
My bash script generates an ignore list (based on) and calls the zip function like this:
#!/bin/bash
SYNC_WEB_ROOT_BASE_DIR="/home/www-data/public_html"
SYNC_WEB_ROOT_BACKUP_DIR="sync_test5"
SYNC_WEB_ROOT_IGNORE_DIR="dir_to_ignore dir2_to_ignore"
ignorelist=""
if [ "$SYNC_WEB_ROOT_IGNORE_DIR" != "" ];
then
for ignoredir in $SYNC_WEB_ROOT_IGNORE_DIR
do
ignorelist="$ignorelist $SYNC_WEB_ROOT_BACKUP_DIR/$ignoredir/**\*"
done
fi
FILE="$SYNC_BACKUP_DIR/$DATETIMENOW.website.zip"
cd $SYNC_WEB_ROOT_BASE_DIR;
zip -r $FILE $SYNC_WEB_ROOT_BACKUP_DIR -x $ignorelist >/dev/null
echo "Done"
Now this script runs without error, however it is not ignoring/excluding the dirs I've specified.
So, I had the shell script output the command it tried to run, which was:
zip -r 12-08-2014_072810.website.zip sync_test5 -x sync_test5/dir_to_ignore/**\* sync_test5/dir2_to_ignore/**\*
Now If I run the above command directly in putty like this, it works:
So, why doesn't my shell script exclude working as intended? the command that is being executed is identical (in shell and putty directly).
Because backslash quotings in a variable after word splitting are not evaluated.
If you have a='123\4', echo $a would give
123\4
But if you do it directly like echo 123\4, you'd get
1234
Clearly the arguments you pass with the variable and without the variables are different.
You probably just meant to not quote your argument with backslash:
ignorelist="$ignorelist $SYNC_WEB_ROOT_BACKUP_DIR/$ignoredir/***"
Btw, what actual works is a non-evaluated glob pattern:
zip -r 12-08-2014_072810.website.zip sync_test5 -x 'sync_test5/dir_to_ignore/***' 'sync_test5/dir2_to_ignore/***'
You can verify this with
echo zip -r 12-08-2014_072810.website.zip sync_test5 -x sync_test5/dir_to_ignore/**\* sync_test5/dir2_to_ignore/**\*
And this is my suggestion:
#!/bin/bash
SYNC_WEB_ROOT_BASE_DIR="/home/www-data/public_html"
SYNC_WEB_ROOT_BACKUP_DIR="sync_test5"
SYNC_WEB_ROOT_IGNORE_DIR=("dir_to_ignore" "dir2_to_ignore")
IGNORE_LIST=()
if [[ -n $SYNC_WEB_ROOT_IGNORE_DIR ]]; then
for IGNORE_DIR in "${SYNC_WEB_ROOT_IGNORE_DIR[#]}"; do
IGNORE_LIST+=("$SYNC_WEB_ROOT_BACKUP_DIR/$IGNORE_DIR/***") ## "$SYNC_WEB_ROOT_BACKUP_DIR/$IGNORE_DIR/*" perhaps is enough?
done
fi
FILE="$SYNC_BACKUP_DIR/$DATETIMENOW.website.zip" ## Where is $SYNC_BACKUP_DIR set?
cd "$SYNC_WEB_ROOT_BASE_DIR";
zip -r "$FILE" "$SYNC_WEB_ROOT_BACKUP_DIR" -x "${IGNORE_LIST[#]}" >/dev/null
echo "Done"
This is what I ended up with:
#!/bin/bash
# This script zips a directory, excluding specified files, types and subdirectories.
# while zipping the directory it excludes hidden directories and certain file types
[[ "`/usr/bin/tty`" == "not a tty" ]] && . ~/.bash_profile
DIRECTORY=$(cd `dirname $0` && pwd)
if [[ -z $1 ]]; then
echo "Usage: managed_directory_compressor /your-directory/ zip-file-name"
else
DIRECTORY_TO_COMPRESS=${1%/}
ZIPPED_FILE="$2.zip"
COMPRESS_IGNORE_FILE=("\.git" "*.zip" "*.csv" "*.json" "gulpfile.js" "*.rb" "*.bak" "*.swp" "*.back" "*.merge" "*.txt" "*.sh" "bower_components" "node_modules")
COMPRESS_IGNORE_DIR=("bower_components" "node_modules")
IGNORE_LIST=("*/\.*" "\.* "\/\.*"")
if [[ -n $COMPRESS_IGNORE_FILE ]]; then
for IGNORE_FILES in "${COMPRESS_IGNORE_FILE[#]}"; do
IGNORE_LIST+=("$DIRECTORY_TO_COMPRESS/$IGNORE_FILES/*")
done
for IGNORE_DIR in "${COMPRESS_IGNORE_DIR[#]}"; do
IGNORE_LIST+=("$DIRECTORY_TO_COMPRESS/$IGNORE_DIR/")
done
fi
zip -r "$ZIPPED_FILE" "$DIRECTORY_TO_COMPRESS" -x "${IGNORE_LIST[#]}" # >/dev/null
# echo zip -r "$ZIPPED_FILE" "$DIRECTORY_TO_COMPRESS" -x "${IGNORE_LIST[#]}" # >/dev/null
echo $DIRECTORY_TO_COMPRESS "compressed as" $ZIPPED_FILE.
fi
After a few trial and error, I have managed to fix this problem by changing this line:
ignorelist="$ignorelist $SYNC_WEB_ROOT_BACKUP_DIR/$ignoredir/**\*"
to:
ignorelist="$ignorelist $SYNC_WEB_ROOT_BACKUP_DIR/$ignoredir/***"
Not sure why this worked, but it does :)

Shell Scripting: Print directory names and files with specifics

In my script, I am asking the user to input a directory and then list all the files in that specific directory. What I want to do with that is to make the display a little better in which I would be able to display a "/" if the item in the directory is another directory and if it is an executable file (not an executable directory), print with a **".
This is what I have:
echo “Directory: “
read thing
for var123 in $thing*
do
echo $var123
done
In a directory I have a few folders and a few scripts that have the execute permission. when I run the script I want to say
/folder1/subfolder1/
/folder1/subfolder2/
/folder1/file1*
/folder1/file2*
I am new to this and have no clue what I am doing. Any help will be greatly appreciated.
You might want to check and make sure the user inputs something that ends in a / first.
e.g.
[[ $thing =~ '/'$ ]] || thing="$thing/"
Also check if it exists
e.g.
[[ -d $thing ]] || exit 1
Then for checking if it's a directory use the -d test as above. To check if executable file use -x. So putting that all together, try:
#!/bin/bash
echo “Directory: “
read thing
[[ $thing =~ '/'$ ]] || thing="$thing/"
[[ -d $thing ]] || exit 1
for var123 in "$thing"*
do
if [[ -f $var123 && -x $var123 ]]; then
echo "$var123**"
elif [[ -d $var123 ]]; then
echo "$var123/"
else
echo "$var123"
fi
done
ls -F is your friend here - if you want to do it for the current directory:
ls -F
If you want to do it for all files & subfolders of the current directory:
find * -exec ls -Fd {} \;
... and for a given directory:
echo "Directory: "
read DIR
find $DIR/* -exec ls -Fd {} \;
Edit: ls -F will append a / to directories and a * to executables. If you want ** instead, just use sed to replace them:
find $DIR/* -exec ls -Fd {} \; | sed 's/\*$/&&/'
And this approach works in all shells, not just bash.

Bash is symlinking to working directory instead of specified directory

I am working on a bash script that I am working on for a universal Linux dotfile install script. I am attempting to get the symlinking working but I have been bashing (no pun intended) my head against the wall trying to figure out why the symlinks will not work and the copying will not work. I currently have this separated into multiple files so I don't have if statements three miles long.
ultimate-install.sh
#! /bin/bash
#
# The ultimate install script for all dotfiles.
if [[ -z "$1" ]]; then
echo "Please specify the directory where all of you dotfiles are located."
exit 1
fi
# Makes sure that the directory does NOT have a trailing slash!
if [[ ${1:(-1)} == "/" ]]; then
DOTFILE_DIR=${1:0:${#1} - 1}
else
DOTFILE_DIR="$1"
fi
# TODO: Clean this mess up and make it more concise.
if [[ -z "$2" ]]; then
if [[ ! -d $HOME/.config/old_dotfiles ]]; then
mkdir "$HOME/.config/old_dotfiles"
fi
BACKUP_DIR="$HOME/.config/old_dotfiles"
else
if [[ -d "$2" ]]; then
BACKUP_DIR="$2"
else
mkdir "$2"
BACKUP_DIR="$2"
fi
fi
read DECISION
if [ $DECISION == "N" -o $DECISION == "n" ]; then
echo "Aborting installation!"
exit
fi
read DECISION
echo
if [ $DECISION == "N" -o $DECISION == "n" ]; then
source src/no-prompts.sh "$DOTFILE_DIR" "$BACKUP_DIR"
else
source src/prompts.sh "$DOTFILE_DIR" "$BACKUP_DIR"
fi
echo "Installation complete. Old dotfiles are backed up to $BACKUP_DIR."
src/no-prompts.sh
#! /bin/bash
#
# Maintained by Daniel Seymour
DOTFILE_DIR="$1"
BACKUP_DIR="$2"
TEST_DIR="/home/daniel/dotfile-test"
function no_prompt_install(){
FILE_NAME="$1"
if [ "${FILE_NAME:0:1}" == "." ]; then
ln -s "$FILE_NAME $TEST_DIR/$FILE_NAME"
else
ln -s ".$FILE_NAME $TEST_DIR/$FILE_NAME"
fi
}
# TODO: implement a check for file type and deal with unknown files.
for FILE in $DOTFILE_DIR/*; do
cp $FILE $BACKUP_DIR
no_prompt_install $FILE
done
src/prompts.sh
#! /bin/bash
#
# Maintained by Daniel Seymour
DOTFILE_DIR="$1"
BACKUP_DIR="$2"
TEST_DIR="/home/daniel/dotfile-test"
function prompt_install {
FILE_PATH=$1
FILE_NAME=${FILE_PATH##*/}
echo "Would you like to install $FILE_NAME? [Y, n]"
read DECISION
if [ $DECISION == "n" -o $DECISION == "N" ]; then
echo "Not installing."
return
else
# TODO: Clean this up into one statement.
if [ ${FILE_NAME:0:1} == "." ]; then
rm -rf "$TEST_DIR/$FILE_NAME"
ln -sn "$FILE_PATH $TEST_DIR/$FILE_NAME"
else
FILE_NAME="."$FILE_NAME
rm -rf "$TEST_DIR/$FILE_NAME"
ln -sn "$FILE_PATH $TEST_DIR/$FILE_NAME"
fi
fi
}
# TODO: implement a check for file type and deal with unknown files.
for FILE in $DOTFILE_DIR/*; do
cp $FILE $BACKUP_DIR
prompt_install $FILE
done
The above is trimmed for long echo statements that do a lot of explaining.
The basic idea of this script is to take as many as two arguments (the dotfile directory to install and if specified, the custom backup directory, $1 and $2 respectively). The script should then copy all of the files in the target directory to BACKUP_DIR and symlink all of the dotfiles in the DOTFILE_DIR to TEST_DIR. (TEST_DIR will be $HOME in the production scripts.) Great in theory, right?
The complication comes when I run the script. None of the files are copied or symlinked as they should be. Instead, I end up with NO copy (probably due to the same issue as the symlink not working) and a broken symlink in the current directory.
One last piece of information. I am executing the file from the directory that contains ultimate-install.sh (/home/daniel/Projects/Git-Repos/Ultimate-Dotfile-Install-Scripts).
So where did I go wrong?
PS Please don't comment on the TODOs. :)
Short answer
Your quoting is wrong.
ln -sn -- "$FILE_PATH" "$TEST_DIR/$FILE_NAME"
Longer answer
This does not really relate to your problem, but I want to point it out.
Do not use "" inside [[ ]], so instead of this if [[ -z "$1" ]]; then use this if [[ -z $1 ]]; then
What is the point of making sure that directory does not have a trailing slash? It has no effect! /usr/bin/ is the same directory as /usr/bin or /usr////bin or /usr////////bin//////
Do not check if a directory exists when creating directories. Use -p option! Example: mkdir -p "$HOME/.config/old_dotfiles"
Instead of if [ $DECISION == "N" -o $DECISION == "n" ]; use if [[ ${DECISION^^} == N]];
I have another great answer about bash code style HERE. Please check it out! Also read the comments, since I was explaining there exactly your issue.

How to avoid cd to a directory without access permission

I have an array variable tht contains all directories in a folder.
I need to cd to each directory in the $array. bt when it reach to a non accessible directory , program halts . how can I avoid this so that my loop simply go to the next directory. code is
foreach dir ($array)
cd $dir
echo "directory is $dir"
cd - end
please help !!
Just test that the cd worked.
( if cd $dir 2> /dev/null; then
echo in directory $dir
# Do other things
fi )
Also, note the parentheses. These cause the entire clause to run in a subshell, so there's no need to cd back to the original location.
If iam not mistaken you can use something like
if [ -r ${dir} ]
then
echo "Have read access!"
fi
You can use -w for testing write access and -x for testing for the exceution bit.

Resources