Handling files with spaces in a bash selection menu - bash

I am trying to make this script handle file with spaces in them. it is supposed show and execute the content of files in a directory. when I select a file with a space in it, bash fails with bash: foo: no such file or directory, What am I missing to make this handle files correctly
# /etc/skel/.bashrc
#Interactive shell detection
if [[ $- != *i* ]] ; then
# Shell is non-interactive. Be done now!
return
fi
#kv-bash (easy) var database & setup of info
echo "type 'menu' for a bash menu"
#done####################
#to easily launch crouton enviroments
addentry() {
cd ~/.sslm
echo "Name your menu entry."
read entry
sleep 1s
if [ -e "$entry " ]
then
echo "Error, Menu entry already exists"
addentry
else
echo "what do you want the entry to do?"
read entryexec
echo "$entryexec && menu"> ~/.sslm/"$entry"
echo "done"
cd ~/
fi
sleep 1s
}
###################
delentry() {
cd ~/.sslm
ls -x
echo "what entry do you want to delete?"
read del
rm "$del"
echo "the work has been done, he is dead"
}
###################
menu() {
clear
cd ~/.sslm
echo "-- simple shell launcher menu v1.o --"
# set the prompt used by select, replacing "#?"
PS3="Use number to select a file or 'exit' to leave: "
# allow the user to choose a file
select filename in *
do
# leave the loop if the user says 'stop'
if [[ "$REPLY" == exit ]]; then
cd ~/
break
fi
# complain if no file was selected, and loop to ask again
if [[ "$filename" == "" ]]
then
echo "'$REPLY' is not a valid number"
sleep 1s
continue
fi
# now we can use the selected file, trying to get it to run the shell
script
. $filename
# it'll ask for another unless we leave the loop
break
done
}
menu
also, this is on a chromebook, so there is no apt.

At this part:
script
. $filename
I just needed to change to . "$filename"
thx #PesaThe

Related

Repeat through bash script based on user input

#sets dir name
echo "What is the name of the target?"
read targetName
#changes dir to desktop
mkdir -p ../Desktop/Notes
cd ../Desktop/Notes
#make working directory
mkdir $targetName
cd $targetName
mkdir "IPs" "SubDomains" "Screenshots" "NmapScans" "Notes"
I have been trying to wrap my brain around simple loops in bash. I have the following script I would like to ask a user for "targetName" to create some directories. After the directories are created I would like the script to ask the user if they would like to create another target, If Y/Yes loop back, if no then exit. I realize this is a fairly simple issue, new to bash and programming in general and I work best if I create the problem myself. Im 99% sure I need a if loop for this. Im just not sure how to break it up correctly. Thanks in advance!
See if that helps you:
#!/bin/bash
while true
do
IFS= read -p 'What is the name of the target? ' -r targetName
# sanity checks:
# * no empty input
# * no '/' in input
if [ ${#targetName} -eq 0 ] || [[ ${targetName} == */* ]]
then
echo 'error: invalid target name' 1>&2
continue
fi
# for now, just print what you'll be doing
printf 'mkdir -p \\\n'
printf ' %q \\\n' ~/Desktop/Notes/"$targetName"/{IPs,SubDomains,Screenshots,NmapScans,Notes}
read -p 'Do you wish to create an other target?[y/n] ' -n 1 yesno
echo
case "$yesno" in
[Yy]) continue;;
*) break;;
esac
done

chmod command is not working in shell script

I am writing a script that needs to create a file if an argument is passed to he script.
If no argument is passed then it will ask for fileName but it needs to have default permission as -rwx- --- - ---.
I am using this command chmod a=-rwx to remove all the default permissions and then i am using chmod u=+rwx to get the desired permission as stated but it is not working.
Can anyone help please?
#!bin\bash
if [ $#==0 ]; then
echo "Please enter a file name?"
read fileName
if [ -f $fileName ]; then
echo "File already exist! Opening for Editing"
sleep 3
nano $fileName
else
echo "File created with the name $fileName"
echo "Opening $fileName for editing "
sleep 3
echo "#!bin\bash" >$fileName
nano $fileName
fi
elif [ -f $1 ]; then
echo "File already exists with the name $1"
echo "Opening for editing"
sleep 3
nano $1
else
fileName="$1"
chmod a=-rwx $fileName
chmod u=+rwx $fileName
echo "File created with the name $filename"
echo "Opening $fileName for editing "
echo "#!bin\bash" >$fileName
sleep 3
nano $1
fi
Your chmod syntax is incorrect. The operation is either = to set the mode to a specific value, + to add modes, or - to remove modes; you can't combine them with =+ and =-.
You can perform multiple operations on a single file by separating them with ,.
So it should be:
chmod go-rwx,u+rwx "$fileName"
Another problem:
if [ $#==0 ]
should be
if [ $# -eq 0 ]
Use -eq for numeric comparisons, and spaces are needed around operators in shell conditions.
Third problem: You're doing the chmod before you create the file. Put it after
echo "#!/bin/bash" >"$fileName"
Fourth problem: #!bin\bash should be #!/bin/bash.
Finally, remember to quote your variables, in case they contain spaces.

Copy contents of multiple directories inside current directory into new directory

Brand new to .sh and I'm trying to backup images from an old iPhone where images were placed into a new directory for date. There are hundreds of directories where I need to take the pictures from each and dump them all into one directory.
My best attempt:
#!/bin/bash
function process () {
a=1
for i in *
do
cp -r i ${dir}
let a=a+1
done
}
#Interview
echo "This script will take the contents of each directory in the current directory and move it's contents into a new directory that you will specify"
echo "Name your new directory"
read dir
#Confirm
read -p "Your new directory will be ${dir}. Continue?" -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]
then
process
fi
Errors recieved:
massmove.sh: line 1: $'\r': command not found
massmove.sh: line 2: $'\r': command not found
massmove.sh: line 3: $'\r': command not found
massmove.sh: line 4: syntax error near unexpected token `$'{\r''
'assmove.sh: line 4: `function process () {
Update: Improved via deefff's answer:
function process () {
for i in */*.*
do
cp -r $i ${dir}
done
}
echo "This script will take the contents of each directory in the current directory and mv it's contents into a new directory that you will specify"
echo "Name your new directory"
read dir
mkdir ${dir}
echo process
Still is throwing these errors:
massmove.sh: line 2: syntax error near unexpected token `$'{\r''
'assmove.sh: line 2: `function process () {
Could this be a WSL bug?
I understand that
cp */*.* ${dir}
is a quick and powerful way to accomplish my task but i'm also very interested in what is causing the errors.
* will match every entry, including directories, so in its current form, it will copy the whole directory structure, which I guess, is not what you want. Also, you should refer to the variable i as $i in the cp command. Also, the a variable seems pointless. Try it like this:
function process () {
for i in */*.*
do
cp $i ${dir}
done
}
In fact, I just realized that this should do the trick as well:
cp */*.* ${dir}
Please see my script below.
It basically has 3 functions: menu, prnt (for printing lines with date) and process (to process files). It has a menu, which works with options. Hope you like it.
Cheers !!
Gaurav
#!/bin/bash
curdir=`pwd` # Take the current directory
# To print out logs on the screen, with date and time !!!
prnt ()
{
d=`date +"%d-%m-%y "%T` # Take the date string using bash date function.
echo "$d|$1" # Whenever prnt is called, it will echo back, date plus the line passed to it, in nice date|line format.
}
# Menu for the whole operation. This is just the definition. Its being called from the bottom of the script.
menu()
{
# Specially made for you my friend !!
echo ; echo ; prnt " <<!!*** .. params_noob's file copier .. ***!!>>" ; echo ; echo
echo ; prnt "Currently we are in $curdir" ; echo
prnt "Enter the directory, where you would like to move files (Please enter full path and system will create it): " ; echo
read dir ; mkdir $dir 2>/dev/null # Read directory and make it. I am putting all errors to /dev/null. If directory is there, its there, don't want blabber. If not, then create it.
echo ;prnt "Entered directory is \"$dir\"" ; echo
prnt "Type y to go ahead OR n to start over again.." ; echo
read ans # I have read the answer here
# A simple menu using case function of bash.
case $ans in
y)
echo ; prnt "You have entered yes, to move ahead" ; echo
prnt "processing files now...."
process $dir # Here i am calling the process function.
;;
n)
echo ; prnt "Starting over.. " ; echo
menu # Here i am calling the menu itself, again.
;;
0)
echo ; prnt "Exiting now.. " ; echo
exit # Now exiting the script.
;;
*)
# This one is just to let you know, if you enter anything else than y,n or 0.
echo ; prnt "Invalid input. Please enter y or n or 0 !!" ; echo
;;
esac # Menu using case function ends with esac here.
}
# Function to process and move the files to the desired location.
process()
{
# Took the argument passed to this function into variable "td" [target dirctory].
td="$1"
# Using find command here to find all files. You an replace '-type f' with '-name "*.pic"' or '-name "*.jpg"' -- you get the picture - right?
find $curdir -type f | while read file # Feeding output of find to a while loop.
do
cp $file $td/
done
a=`ls -lrt $td/* |wc -l` # Now taking word count of all the files copied.
# Some more echo and priting.. just for aesthetics.. :)
echo ; echo ; prnt " **** COPIED $a FILES from \[$curdir\] to \[$td\] ****" ; echo ; echo
echo ; echo ; ls -lrtha $td|head -10 ; echo ; echo
exit # Script exits after processing the files. You can replace exit with menu, to go back to menu again.
}
#
##
###
####
#####
#################################################################
clear ; menu ### The main menu is being called from this line. I clear the screen first. Looks more professional :)
#################################################################
# FINISH # All done !!
enter code here
Here is the output in a picture format
Try something like this:
function process () {
for file in *; do
if [[ ${file} != ${dir} ]]; then
cp -r ${file} ${dir}
fi
done
}
Also, remember to create the directory before you call the 'process' function:
echo "Name your new directory: "
read dir
mkdir ${dir}

Shell Script not reading input

I have written a script that backs up and restores files. I have a problem in that when the user enters '2' for a restore the program says that this is an invalid input, all other options work fine. I feel it is something small that I have missed but I cant fix it
Update and Restore Script
#!/bin/bash
ROOT="/Users/Rory/Documents"
ROOT_EXCLUDE="--exclude=/dev --exclude=/proc --exclude=/sys --exclude=/temp --exclude=/run --exlucde=/mnt --exlcude=/media --exlude=/backup2.tgz"
DESTIN="/Users/Rory/test/"
BACKUP="backup2.tgz"
CREATE="/dev /proc /sys /temp /run /mnt /media "
if [ "$USER" != "root" ]; then
echo "You are not the root user"
echo "To use backup please use: sudo backup"
exit
fi
clear
echo "************************************************"
echo "********* Backup Menu **************************"
echo "************************************************"
OPTIONS="BACKUP RESTORE DESTINATION EXIT"
LIST="1)BACKUP 2)RESTORE 3)DESTINATION 4)EXIT"
select opt in $OPTIONS; do
if [ "$opt" = "EXIT" ]; then
echo "GOODBYE!"
sleep 3
clear
exit
elif [ "$opt" = "BACKUP" ]; then
echo "BACKING UP FILES..."
sleep 2
tar cvpfz $DESTIN/backup.`date +%d%m%y_%k:%M`.tgz $ROOT $ROOT_EXCLUDE_DIRS
echo "BACKUP COMPLETE"
sleep 2
exit
elif [ "$opt" = "RESTORE" ]; then
echo "RESTOTING FILES..."
sleep 2
tar xvpfz $BACKUP_FILE -C /
sleep2
echo "RESTORE COMPLETE..."
if [[ -e "/proc" ]]; then
echo "$CREATE_DIRS allready exists! "
else
mkdir $CREATE_DIRS
echo "$CREATE_DIRS are created! "
fi
exit
elif [ "$opt" = "DESTINATION" ]; then
echo "CURRENT DESTINATION: $DEST_DIR/backup.`date +%d/%m/%y_%k:%M`.tgz "
echo "TO CHANGE ENTER THE NEW DESTINATION..."
echo "TO LEAVE IT AS IS JUST PRESS ENTER..."
read NEW_DESTIN
#IF GREATER THEN 0 ASSIGN NEW DESTINATION
if [ ${#NEW_DESTIN} -gt 0 ]; then
DESTIN = "$NEW_DESTIN"
fi
clear
echo $BANNER1
echo $BANNER2
echo $BANNER3
echo $LIST
else
clear
echo "BAD INPUT!"
echo "ENTER 1 , 2, 3 or 4.."
echo $LIST
fi
done
Except where you missed the ending quote where you set ROOT_EXCLUDE (line #4), it looks okay to me. I take it the missing quote is a transcription error or your program wouldn't really work at all.
I've tried out the program and it seems to work.
A debugging trick is to put set -xv to turn on debugging in your script and set +xv to turn it off. The -x means to print out the line before executing, and the -v means to print out the line once the shell interpolates the line.
I'm sure that you'll immediately see the issue once you have set -xv in your program.
As part of this, you can set PS4 to the line prompt to print when the debugging information is printed. I like setting PS4 like this:
export PS4="[\$LINENO]> "
This way, the line prompt prints out the line it's executing which is nice.
In your case, I would put set -xv right before you set OPTIONS and then at the very end of the program. This way, you can see the if comparisons and maybe spot your issue.
export PS4="[\$LINENO]> "
set -xv
OPTIONS="BACKUP RESTORE DESTINATION EXIT"
LIST="1)BACKUP 2)RESTORE 3)DESTINATION 4)EXIT"
select opt in $OPTIONS; do
if [ "$opt" = "EXIT" ]; then
echo "GOODBYE!"
set +xv
By the way, it's better to use double square brackets like [[ ... ]] for testing rather than the single square brackets like [ ... ]. This has to do with the way the shell interpolates the values in the test.
The [ ... ] is an alias to the built in test command. The shell interpolates the line as is and the entire line is executed.
The [[ ... ]] are a compound statement where the shell will interpolate variables, but not the entire line. The line is kept as whole:
foo="one thing"
bar="another thing"
This will work:
if [ "$foo" = "$bar" ]
then
echo "Foo and bar are the same"
fi
This won't:
if [ $foo = $bar ]
then
echo "Foo and bar are the same"
fi
The shell interpolates the line as is:
if [ one thing = another thing ]
And this is the same as:
if test one thing = another thing
The test command looks at the first item to see if it's a standard test, or assumes three items and the second item is a comparison. In this case, neither is true.
However, this will work:
if [[ $foo = $bar ]] # Quotes aren't needed
then
echo "Foo and bar are the same"
fi
With the [[ ... ]] being a compound command, the $foo and $bar are replaced with their values, but their positions are kept. Thus, the = is recognized as a comparison operator.
Using [[ ... ]] instead of [ ... ] has solved a lot of hard to find shell scripting bugs I have.

Shell Directory Backup and Restore

I have been making script that will back up and restore a directory. I want to make it better but I need some help.
At the moment the I have the file being saved as just backup.tgz I did have the date added onto the end but when I ran the restore function the I could only have it look for the backup.tgz and not the backup with the date extension. Is there any way to have it look for the most recent backup? Or even look for the backup given by user input?
I have also tried to add a progress bar and make incremental back ups but had no luck there either if someone could help?
Tar Code
#!/bin/bash
ROOT="/Users/Rory/Documents"
ROOT_EXCLUDE="--exclude=/dev --exclude=/proc --exclude=/sys --exclude=/temp --exclude=/run --exlucde=/mnt --exlcude=/media --exlude=$
DESTIN="/Users/Rory/BackUps"
BACKUP="backup.tgz"
CREATE="/dev /proc /sys /temp /run /mnt /media "
if [ "$USER" != "root" ]; then
echo "You are not the root user"
echo "To use backup please use: sudo backup"
exit
fi
clear
echo "************************************************"
echo "********* Backup Menu **************************"
echo "************************************************"
OPTIONS="BACKUP RESTORE DESTINATION EXIT"
LIST="1)BACKUP 2)RESTORE 3)DESTINATION 4)EXIT"
select opt in $OPTIONS; do
if [ "$opt" = "EXIT" ]; then
echo "GOODBYE!"
sleep 3
clear
exit
elif [ "$opt" = "BACKUP" ]; then
echo "BACKING UP FILES..."
sleep 2
tar cvpfz $DESTIN/backup.tgz $ROOT $ROOT_EXCLUDE
echo "BACKUP COMPLETE"
sleep 2
clear
echo $LIST
elif [ "$opt" = "RESTORE" ]; then
echo "RESTOTING FILES..."
sleep 2
tar xvpfz $DESTIN/$BACKUP -C /Users/Rory/BackUps
sleep 2
if [[ -e "/proc" ]]; then
echo "$CREATE already exists! "
else
mkdir $CREATE
echo "$CREATE are created! "
fi
echo "RESTORE COMPLETE..."
clear
exit
elif [ "$opt" = "DESTINATION" ]; then
echo "CURRENT DESTINATION: $DESTIN/backup.tgz "
echo "TO CHANGE ENTER THE NEW DESTINATION..."
echo "TO LEAVE IT AS IS JUST PRESS ENTER..."
read NEW_DESTIN
#IF GREATER THEN 0 ASSIGN NEW DESTINATION
if [ ${#NEW_DESTIN} -gt 0 ]; then
DESTIN = "$NEW_DESTIN"
fi
clear
else
clear
echo "BAD INPUT!"
echo "ENTER 1 , 2, 3 or 4.."
echo $LIST
fi
done
Well, in the code snippet you posted, you are only looking for backup.tgz.
If you wanted to pick a specific one, you could modify your script to accept an argument and pick one based on a string you input. Or...if you wanted to do based on "how old", you could sort the backup files by date and allow the user to pick 0th, 1st, 2nd, etc.
One thing you may want to check out is rsync. Rsync can only copy files that have changed.
Plus, you can also enable a progress bar with rsync = )
rsync -avP /source/path/ /dest/path/
Check out the man page for more details man rsync
To enable progress bar on restore, you can untar the file and use rsync in reverse, and then you have progress updates = )
If you want to make this a custom numeric progress bar you'll probably need to do something more complicated than simply taking output from rsync.

Resources