bash: Can't get simple 'if' to work - bash

I don't understand why this simple read doesn't work. Mind you, I am VERY new to bash. :)
#!/bin/bash
echo -n "Project Name: "
read PROJECT_NAME
if [ -n "$PROJECT_NAME" ]; then
echo "You must provide a project name."
exit 2
fi
-- snip --
When this executes, it asks for the project name. After I press enter, I get "You must provide a project name." and then the scripts exists instead of continuing.
What am I doing wrong?
Thanks
Eric

You want [ -z "$PROJECT_NAME" ], not -n:
From man test:
-n STRING
the length of STRING is nonzero
...
-z STRING
the length of STRING is zero

to avoid confusion from -n or -z , you can just use case/esac to compare strings
case "$PROJECT_NAME" in
"" ) echo "No";;
*) echo "Have";;
esac

Related

Shell script with absolute path and control errors

I was doing this little script in which the first argument must be a path to an existing directory and the second any other thing.
Each object in the path indicated in the first argument must be renamed so that the new
name is the original that was added as a prefix to the character string passed as the second argument. Example, for the string "hello", the object OBJECT1 is renamed hello.OBJECT1 and so on
Additionally, if an object with the new name is already present, a message is shown by a standard error output and the operation is not carried out continuing with the next object.
I have the following done:
#! /bin/bash
if [ "$#" != 2 ]; then
exit 1
else
echo "$2"
if [ -d "$1" ]; then
echo "directory"
for i in $(ls "$1")
do
for j in $(ls "$1")
do
echo "$i"
if [ "$j" = "$2"."$i" ]; then
exit 1
else
mv -v "$i" "$2"."$i"
echo "$2"."$i"
fi
done
done
else
echo "no"
fi
fi
I am having problems if I run the script from another file other than the one I want to do it, for example if I am in /home/pp and I want the changes to be made in /home/pp/rr, since that is the only way It does in the current.
I tried to change the ls to catch the whole route with
ls -R | sed "s;^;pwd;" but the route catches me badly.
Using find you can't because it puts me in front of the path and doesn't leave the file
Then another question, to verify that that object that is going to create new is not inside, when doing it with two for I get bash errors for all files and not just for coincidences
I'm starting with this scripting, so it has to be a very simple solution thing
An obvious answer to your question would be to put a cd "$2 in the script to make it work. However, there are some opportunities in this script for improvement.
#! /bin/bash
if [ "$#" != 2 ]; then
You might put an error message here, for example, echo "Usage: $0 dir prefix" or even a more elaborate help text.
exit 1
else
echo $2
Please quote, as in echo "$2".
if [ -d $1 ]; then
Here, the quotes are important. Suppose that your directory name has a space in it; then this if would fail with bash: [: a: binary operator expected. So, put quotes around the $1: if [ -d "$1" ]; then
echo "directory"
This is where you could insert the cd "$1".
for i in $(ls $1)
do
It is almost always a bad idea to parse the output of ls. Once again, this for-loop will fail if a file name has a space in it. A possible improvement would be for i in "$1"/* ; do.
for j in $(ls $1)
do
echo $i
if [ $j = $2.$i ]; then
exit 1
else
The logic of this section seems to be: if a file with the prefix exists, then exit instead of overwriting. It is always a good idea to tell why the script fails; an echo before the exit 1 will be helpful.
The question is why you use the second loop? a simple if [ -f "$2.$i ] ; then would do the same, but without the second loop. And it will therefore be faster.
mv -v $i $2.$i
echo $2.$i
Once again: use quotes!
fi
done
done
else
echo "no"
fi
fi
So, with all the remarks, you should be able to improve your script. As tripleee said in his comment, running shellcheck would have provided you with most of the comment above. But he also mentioned basename, which would be useful here.
With all that, this is how I would do it. Some changes you will probably only appreciate in a few months time when you need some changes to the script and try to remember what the logic was that you had in the past.
#!/bin/bash
if [ "$#" != 2 ]; then
echo "Usage: $0 directory prefix" >&2
echo "Put a prefix to all the files in a directory." >&2
exit 1
else
directory="$1"
prefix="$2"
if [ -d "$directory" ]; then
for f in "$directory"/* ; do
base=$(basename "$f")
if [ -f "Sdirectory/$prefix.$base" ] ; then
echo "This would overwrite $prefix.$base; exiting" >&2
exit 1
else
mv -v "$directory/$base" "$directory/$prefix.$base"
fi
done
else
echo "$directory is not a directory" >&2
fi
fi

Validate bash script arguments

I am trying to do something like this to make a script to perform backups if they have failed. I am taking in the environment as argument to the script.
The one thing i am unsure on how to do is that i want to verify $1 to only include some predefined values. The predefined values should be something like tst, prd, qa, rpt. Anyone?
#!/bin/bash
ENVIRONMENT=$1
BACKUPDATE=$(date +"%d_%m_%Y")
BACKUPFILE="$ENVIRONMENT".backup."$BACKUPDATE".tar.gz
if [ $1 == "" ]
then
echo "No environment specified"
exit
elif [ -f "$BACKUPFILE" ]; then
echo "The file '$BACKUPFILE' exists."
else
echo "The file '$BACKUPFILE' in not found."
exec touch "$BACKUPFILE"
fi
You can use case:
case "$1" in
tst) echo "Backing up Test style" ;;
prd)
echo "Production backup"
/etc/init.d/myservice stop
tar czf ...
/etc/init.d/myservice start
;;
qa) echo "Quality skipped" ;;
rpt)
echo "Different type of backup"
echo "This could be another processing"
...
;;
*)
echo "Unknown backup type"
exit 2
;;
esac
Note the double ;; to end each case, and the convenient use of pattern matching.
Edit: following your comment and #CharlesDuffy suggestion, if you want to have all valid options in an array and test your value against any of them (hence having the same piece of code for all valid values), you can use an associative array:
declare -A valids=(["tst"]=1 ["prd"]=1 ["qa"]=1 ["rpt"]=1)
if [[ -z ${valids[$1]} ]] ; then
echo "Invalid parameter value"
# Any other processing here ...
exit 1
fi
# Here your parameter is valid, proceed with processing ...
This works by having a value (here 1 but it could be anything else in that case) assigned to every valid parameter. So any invalid parameter will be null and the -z test will trigger.
Credits go to him.
Depending on how many different values you have, what about a case statement? It even allows for globbing.
case $1 in
(John) printf "Likes Yoko\n";;
(Paul) printf "Likes to write songs\n";;
(George) printf "Harrison\n";;
(Ringo) printf "Da drumma\n";;
(*) printf "Management, perhaps?\n";;
esac
On another note, if you can you should avoid unportable bashisms like the [[ test operator (and use [ if you can, e.g. if [ "$1" = "John" ]; then ...; fi.)

If statement goes else every time - bash

I am very new here, so I apologize for any my mistakes, and I am sorry for my lack of knowledge (I'm just beginner).
So here it is, i am doing little script in bash with li and I have if statement, here it is
#!/bin/bash
something=$(whiptail --inputbox "Enter some text" 10 30 3>&1 1>&2 2>&3)
if [ $something ?? 'you' ];
then
echo "$something"
else
echo "nope"
fi
To be specific what i want from it - I enter some word/sentence/whatever to whiptail, and if it contains some of of you string then prints it, but every times it goes else ;_;.Please help.
EDIT now it works, thanks but I need to check if string contains word.
if [[ $string =~ .*My.* ]]
doesn't seem to work
I don't get it at all, losing hope and searching the web i've encontered on
#!/bin/bash
OPTION=$(whiptail –title “Menu Dialog” –menu “Choose your option” 15 60 4 \ “1” “Grilled ham” \ “2” “Swiss Cheese” \ “3” “Charcoal cooked Chicken thighs” \ “4” “Baked potatos” 3>&1 1>&2 2>&3)
exitstatus=$?
if [ $exitstatus = 0 ];
then echo “Your chosen option:” $OPTION
else echo “You chose Cancel.”
fi
And I've just pasted this script to check how it works and modify it, it isn't mine script and it supposed to work, but it says “You chose Cancel.”
What you may be looking for are the string comparison operators like == or !=. For example,
if [ "$something" == "you" ]; then
echo "$something"
else
echo "nope"
fi
If $something equals you then echo $something; else echo nope.
Or, as David C.Rankin mentioned in his comment you can check the string variable to prove whether a string is empty or not. For example,
if [ -z "$something"] ;then
String is empty
if [ -n "$something" ]; then
String is non-empty
For more information on this check the TEST manual page.

Unix How to check if a specific word is entered in as an argument

I'm writing a script in Unix but I need a way to check an argument that is entered in the command line is a specific word.
So if when using the script the user types:
$ ./script hello
my script can tell that "hello" was entered as an argument and can display a message appropriately.
And if the user types something other than "hello" as an argument then my script can display another message.
Thanks.
This should work:
#!/bin/bash
if [[ $1 == hello ]];then
echo "hello was entered"
else
echo "hello wasn't entered"
fi
There are a number of ways to check positional arguments against a list. When there are a number of items in the list, you can use a case statement instead of a string of if ... elif ... elif ... fi comparisons. The syntax is as follows:
#!/bin/bash
case "$1" in
"hello" )
printf "you entered hello\n"
;;
"goodbye" )
printf "well goodbye to you too\n"
;;
* )
printf "you entered something I don't understand.\n"
;;
esac
exit 0
Output/Use
$ ./caseex.sh hello
you entered hello
$ ./caseex.sh goodbye
well goodbye to you too
$ ./caseex.sh morning
you entered something I don't understand.
In Bash arguments passed to shell scripts are stored in variables named as follows:
$0 = name of the script.
$1~$n = arguments.
$# = number of arguments.
$* = single string of all arguments: "arg1,arg2,..."
you can simply use if [ $1 == "some string" ]; then ...
You can retrieve the command line arguments with $(number)
for example the first argument would exist at $1 the second at $2 etc.
You can use conditionals in BASH (I assume you are using bash) just like any other language; however the syntax is a bit wonky :). here is a link for you
http://tldp.org/LDP/Bash-Beginners-Guide/html/chap_07.html
If you are sure about the position of the argument you can :
#!/bin/bash
if [[ $1 == SearchWord]];then
echo "SearchWord was entered"
else
echo "SearchWord wasn't entered"
fi
Incase you are not sure:
You can use $*
[ `echo $* | grep $SearchWord| wc -l` -eq 1 ] && echo "Present"|| echo "Not present"

bash - if return code >=1 re run script, start at the beginning

I have a bash script that prints a heading and tests for a value of "Y" or "N".
When someone enters text that does not equal "Y" or "N", I would like to send them back to the beginning of the script, so it prints the heading and the question again.
I know you can do this with goto but I was wondering if there's a different way because I hear many individuals say you should not use goto or that it is deprecated. Whether true or not, I'd like to see if anyone else has a way to solve this problem.
Thanks in advance.
You could implement it in a loop:
while [ !$exit_loop ]
do
echo "enter choice - "
read -n 1 input
case "$input" in
y|Y) $exit_loop = 1;;
n|N) $exit_loop = 1;;
*) echo "invalid choice";;
esac
done
Personally I find no difference between using a goto/loop or any other means. I'd always say to use what is most suitable for the situation - for yours, I'd use a goto.
e.g. If you have multiple indentations spanning lots of lines, and you need to jump back to the start of a function, I'd use a goto - it's a lot easier to understand in its context.
As a direct answer to your question, you can use exec to replace the current process with another process, or, as the case may be, another fresh copy of the current process.
read -p "Yes? Or no? " yn
case $yn in
[YyNn]) ;;
*) exec "$0" "$#"
esac
If you want a more structured approach, you can use a while or until loop.
Example (slightly simplified) using #Michael's suggestion follows. The exit condition is in the while loop, but the user can also do an intermediary action to decide which action to take:
while [[ ! "${action-}" =~ ^[SsRr]$ ]]
do
echo "What do you want to do?"
read -n 1 -p $'[d]iff, [s]kip, [S]kip all, [r]eplace, [R]eplace all: \n' action
if [[ "${action-}" =~ ^[Dd]$ ]]
then
diff "$target_path" "$source_path"
fi
done
echo "Hello User, are you ready to learn some Linux?"
while true; do
echo "Please enter y/n:"
read a
bfunc() {
if [ "$a" == "y" ]
then
echo "That is great, lets get started!"
echo "This Script is under construction, functionality coming soon"
exit 0
elif [ "$a" == "n" ]
then
echo "That is too bad, see you next time!"
echo "You are now exiting the script"
exit 0
else [ "$a" != "y||n" ]
echo "Please only enter y or n!"
fi
}
bfunc
done

Resources