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.
Related
I'm attempting to run a bash script in a Jenkins freestyle job, but getting a strange inclusion of extra quotes that's throwing errors in the script.
I finally found the extra quotes by adding what ought to be unnecessary amounts of debugging, so I'm still unsure if it's actually what's causing the issue or not.
Jenkins script (the label parameter in the job is either empty or populated from upstream):
#!/usr/bin/env bash
set -ex
if [[ -z $label ]]; then
echo "label not provided"
labelSend=" "
else
echo "label provided"
labelSend="--label \"$label\" "
fi
./label-script.sh --stack-id $stack_id --update $update "$labelSend"
and the label-script script has a parameter parser like so:
#!/usr/bin/env bash
set -e
print_usage () {
echo
echo "Usage: label-script.sh [OPTIONS]"
echo
echo "Does stuff."
echo
echo " Options:"
echo " --stack-id ID"
echo " --update Use 'true' if this is an update"
echo " --label Optional. Overrides the label"
echo
}
parse_args () {
if [[ $# -eq 0 ]]; then
print_usage
exit 0
fi
while [[ $# -gt 0 ]]
do
key="$1"
case ${key} in
--stack-id) stack_id=$2; shift;;
--update) update=$2; shift;;
--label) label=$2; shift;;
*) print_usage; exit 1;;
esac
shift
done
}
parse_args "$#"
# further script things
Now if I run the Jenkins job, I'm seeing this output:
+ [[ -z testing woo ]]
+ echo 'label provided'
label provided
+ labelSend='--label "testing woo" '
+ ./label-script.sh --stack-id 1 --update false '--label "testing woo" '
and the script prints the help menu rather than continuing with the code.
Note specifically the extra ' around the label parameter when calling the script. I'm thinking this is what's causing my script to fail, as it's not able to parse the given parameter. I have to include label in some form of quotes because it could be a multi-word string that needs to be appropriately quoted for the script to parse.
I've tried every variation of the labelSend=--label $label line that I can think of - nested quotes, no quotes, escaped quotes, etc, with no luck.
Has anyone run into something similar? Is there any quoting method that will get me past this?
I have a bash script with an if condition and continuous lines of script. However, after if condition nothing in the script seems to run. For example, here are the lines of script (The lines after continue in if condition are not reacting).
dir=/usr/path/tofiles
for file in $(find $dir -name \*txt)
do
fbase={file%.txt}
if [ "$step" == "1" -a ! -f "${fbase}.header" ]
then
continue
fi
### From here nothing in the script runs #####
if [ -f "${fbase}merged" ]
then
echo "$file already merged"
fi
files="$files ${fbase}g.txt.gz"
cnt=$(($cnt + 1))
if [ "$cnt" -eq "$chunksize" ]
then
outid=$(uuidgen)
logfile=${outdir}/${outid}.cluster.log
echo "sh $(pwd)/mysecript.sh $outid $files"
fi
done
After the first if condition nothing in the script is running, I tried printing using echo nothing is showing up. Precisely, the issue is after the continue statement within the if condition. Any help/suggestions are much appreciated.
Thanking you
You seem to have a wrong interpretation of the continue statements.
The continue statement skips the lines below it and starts with the next iteration.
while (true) {
print(1);
if (true) {
continue;
}
print(2);
}
In the above print(2) will never get executed as it skips it everytime and starts with the next iteration.
For deeper insight please read Nested-If statements in Shell-scripting
For your scenario please try this
dir=/usr/path/tofiles
for file in $(find $dir -name \*txt)
do
fbase={file%.txt}
if ! [ "$step" == "1" -a ! -f "${fbase}.header" ]
then
if [ -f "${fbase}merged" ]
then
echo "$file already merged"
fi
files="$files ${fbase}g.txt.gz"
cnt=$(($cnt + 1))
if [ "$cnt" -eq "$chunksize" ]
then
outid=$(uuidgen)
logfile=${outdir}/${outid}.cluster.log
echo "sh $(pwd)/mysecript.sh $outid $files"
fi
fi
done
The problem with your script is, that in case your first if statement evaluates always to true you'll always skip the rest of the loop, due to the continue, so nothing else will be executed. This behavior is the same as in all other programming languages.
continue, like break, is a keyword to control the loop behavior. This means that using continueit is possible to skip the rest of the current loop iteration. And using break it is possible to exit the loop.
In case you need to go further to the next if statement, you need to nest your ifs, so that both are checked/evaluated.
More background information regarding this issue can be found here.
According to your comments, your code should include nested ifs, like:
dir=/usr/path/tofiles
for file in $(find $dir -name \*txt)
do
fbase={file%.txt}
if [ "$step" == "1" -a ! -f "${fbase}.header" ]
then
if [ -f "${fbase}merged" ]
then
echo "$file already merged"
fi
files="$files ${fbase}g.txt.gz"
cnt=$(($cnt + 1))
if [ "$cnt" -eq "$chunksize" ]
then
outid=$(uuidgen)
logfile=${outdir}/${outid}.cluster.log
echo "sh $(pwd)/mysecript.sh $outid $files"
fi
fi
done
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
Using sed I'm able to delete a line of code inside the command prompt. When i go to use it inside a bash script and use a variable it doesn't delete it.
function remove_user {
echo "Input user you would like to delete"
read rui
if grep -q $rui database.txt ;
then
echo "Are you sure yu want to delete this user?"
echo "(Y/N)"
read input3
if [ $input3 == "Y" ] || [ $input3 == "y" ] ;
then
sed -i '/"$rui"/d' ./databse.txt
echo "User Deleted"
echo "returning to the main menu..."
echo "$rui"
#sleep 1 ; clear
elif [ $input3 == "N" ] || [$input3 == "n" ] ;
then
echo "returning to main menu..."
sleep 1 ; clear
else
echo "input invalid"
echo "returning to main menu..."
sleep 1 ; clear
fi
else
echo " user not found"
echo "returning to main menu..."
sleep 1 ; clear
fi
My database looks like this
Larry:Larry#hotmail.com:Larry Bob:ATC:4.0
Not sure what the issue could be because the code works just not with a variable
You need to make the variable visible to the shell by breaking back out of the single-quote delimited sed script briefly. You do that by adding a terminating single quote before the variable, and a starting one again after it:
sed -i '/'"$rui"'/d' ./databse.txt
The reason you don't just use double quotes around the whole script is that sets you up for surprises later when the shell interprets you're whole script instead of just the variable you want it to expand. e.g.:
$ echo "money is nice" | sed 's/money/$$/'
$$ is nice
$ foo="money"; echo "money is nice" | sed 's/'"$foo"'/$$/'
$$ is nice
$ foo="money"; echo "money is nice" | sed "s/$foo/$$/"
6756 is nice
That last one happens because the double quotes expose your whole script to the shell before sed sees it and the shell interprets $$ as the current PID.
In my program, I have to check whether a command given as a input by a user exists or not and if it exists, program needs to check if the parameters of that command are correct.
For example:
ls ( is correct)
-al (is correct)
do the watch
and if I do this:
ls (is correct)
-kala (not correct)
don't do the watch.
How I can do this? Here is my script:
while true
do
echo "Insert the command"
read comm
if [ "$(type -t $comm)" != "" ]; then
echo "Insert the parameters of the command ";
read par;
echo "Insert the time of watch";
read time;
if [ $t -le 0 ]; then
echo "Value not correct";
else
clear;
while true
do
echo "$comm"
date
echo ""
$comm $par
sleep $((time))
clear
done
fi;
else
echo "Command not found, retry.";
echo "";
fi
done
You can replace the command invocation with this:
if ! $comm $par; then
exit 1
fi
to make it stop after an error. Also there is already a tool called watch but I think you already know this.