bash scripting if-else simple issue - linux

Dude, so sorry about the absolute noob question.
What is the problem with the following code?
I like to have a simple script which says MEH! when there is no input arg, otherwise prints it.
#!/bin/bash
if [${#$1}==0] then
echo "MEH!";
else
echo $1;
fi
OS says the line 4 has a error (unexpected token else at line 4).
So sorry dude.
Thanks in advance.

You probably wanted to use:
#!/bin/bash
if [ ${#1} -eq 0 ]; then
echo "MEH!";
else
echo $1
fi
Problems in your current if [${#$1}==0] then condition:
spaces around [ and ] are needed. For further information, you can check the excellent answer by Charles Bailey in Why equal to operator does not work if its not surrounded by space?.
== is used on string comparison. In this case you want integer comparison, that is -eq. See Bash Conditional Expressions in the manual for a full list of them.
In general, if you want to check if your script is receiving at least a parameter, you'd better do:
if [ $# -ge 1 ]; then
echo "$# parameters given"
else
echo "no parameters given"
fi
or also, as commented by Charles Duffy:
if [ -z "$1" ] # true if the variable $1 has length 0
Last and not least: check the comments below, as good information was provided.

Related

Linux Shell if-statements

I am trying to write a script to add integers together. My first script works to add numbers
for number in $#; do
sum=$(($sum + $number))
done
echo $sum
Now I am trying to write an if statement that checks if $number is an valid integer and if not display an error message and kill the script.
if [ -z $number]; then
sum=$(($sum + $number))
else
echo $number
echo "Sorry, $number is not a number"
exit
fi
I have been trying to figure this out for a few hours and keep getting stuck and help would be nice
The -z operator doesn't check if the string is a number, it checks if the length of the string is zero.
There isn't an operator to check if a string is an integer, however there are several ways to do this in bash. The answers on this previous discussion should be helpful.
the -z test will just test of the variable is empty (zero length) or not. If you want to know that is has an integer you could use a regex match like
if [[ $number =~ ^-?[0-9]+$ ]]; then
sum=$((sum + number))
else
echo "Sorry, $number is not a number"
exit
fi

Convert bash script using regexes to sh

I'm really struggling with writing a Bourne shell script. Basically, I have three input formats for a variable called "ref" that I'm trying to detect:
ref="refs/head/.*" (i.e. begins with "refs/head/" I'm interested in the bit at the end, after the slash)
ref="refs/tags/.*" (i.e. begins with "refs/tags/" I'm interested in the bit at the end, after the slash)
everything else (i.e. ignore everything that doesn't begin with either "refs/head/" or "refs/tags/")
For example,
If ref="refs/head/master", set TAG="master"
If ref="refs/tags/0.2.4", set TAG="0.2.4"
For everything else, set TAG=""
Now I wrote something in bash shell, but I'm really struggling to convert it to Bourne (#!/bin/sh):
#!/bin/bash
#
#This works!
#
TAG=""
re='^refs/head/.*' #regex: begins with refs/head/, ends with anything
re2='^refs/tags/.*' #regex: begins with refs/tags/, ends with anything
if [[ $ref =~ $re ]]; then
#do nothing - OK
true #NOP
else
#check if it's a tag update
if [[ $ref =~ $re2 ]]; then
TAG=${$ref##*/} #looks worse that it is: http://stackoverflow.com/questions/3162385/how-to-split-a-string-in-shell-and-get-the-last-field
fi
exit 0
fi
echo $TAG
It took me ages to a) write this program and b) find out why my program was going nuts - turned out I need #!/bin/sh and not #!/bin/bash
How can I convert this to sh? Maybe someone else has a more elegant solution to my regex gymnastics?
Update:
Thanks for the answers sofar (especially #gboffi). I think I'm almost there.
All I need now is to know if $TAG comes from "refs/head/", "refs/tags/" or neither. I tried to modify some of the answers, but really struggling with sh. I'll need to go away and learn more about sh from first principles instead of trying to hack it.
Update 2:
So after a night's sleep, I figured it out in about 20 minutes. Here is my solution:
#!/bin/sh
ref="refs/asdf/master"
TAG=""
TAG="${ref#refs/heads/}"
if [ "$ref" != "${ref#refs/heads/}" ]; then
echo "heads"
echo $TAG
else
TAG="${ref#refs/tags/}"
if [ "$ref" != "${ref#refs/tags/}" ]; then
echo "heads"
echo $TAG
else
TAG=""
fi
fi
echo "--->$TAG"
I'm sure there's a much more elegant solution; but I just don't have the time!
Here it is a function, defined in dash (the linux version of sh)
% dash
$ tag() {
> TAG=""
> [ "$1" != "${1#refs/head/}" ] && TAG="${1#refs/head/}"
> [ "$1" != "${1#refs/tags/}" ] && TAG="${1#refs/tags/}"
> echo "$TAG"
>}
$ tag poldo
$ tag refs/head/poldo
poldo
$ tag refs/tags/pippo
pippo
$ tag to093u0refs/head/poldo
$exit
%
The most idiomatic way to handle this is with a case statement and pattern matching:
#!/bin/sh
case $ref in
refs/head/*) true ;; # original behavior: no output, success
refs/tags/*) printf '%s\n' "${ref##*/}" ;; # original behavior: emit output
*) false # exit with an error # original code didn't specify behavior
esac
The reason I'm exiting with an error for the case where neither refs/head/* or refs/tags/* matches is that if you wanted to exit with success in that case, you could omit the refs/head/* test entirely.
You can use rev-parse for this, here are some examples from one of my
repositories. Notice the last one produces no output, as desired.
$ git rev-parse --abbrev-ref refs/heads/master
master
$ git rev-parse --abbrev-ref refs/tags/5
5
$ git rev-parse --abbrev-ref #^
Having read your description of the desired function it seems that it boils down to this:
check the input argument. If it starts with refs/head or refs/master, proceed, else stop.
take the terminal piece of information and set the variable TAG to it.
thus, assuming your input is in the variable REF,
TAG=""
echo $REF | egrep -q 'refs/head|refs/master'
if [ $? -eq 0 ]
then
TAG=`echo $REF | sed "s/.*\///"`
fi
ought to do it.
(Side note for shell heads: is basename a better solution than sed in this particular case?)

Bash scripting using an exit status in an if statement

May be a novice question but anyways in my intro to linux/unix class were touching on bash scripting and in one of the problems I got the it tasked me with making a script so if the user searched to a name in a file that wasn't there it would output a messaged saying 'your_input is not in the directory'
It says to use if statements and the exit status $?.
So far I got the input portion but I'm not sure how to properly use the $? in a if statement if its possible.
#!/bin/bash
name=$1
if [ "$name" = "" ]
then echo -n "Enter a name to search for: "
read name
fi
grep -i $name ~uli101/2014c/phonebook
if [ "$?" < "0" ]
then echo "error"
fi
I get the error:
./phone4: line 14: 0: No such file or directory
My question is: How can I use the $? with and if statement, and If I can't, can you explain me how to use the $? in this problem?
Note: I did use echo $? to see how $? gave a 0 if grep worked and a 1 if it didn't.
There's two bugs in it. The one you already see is that in the [] expression, the < is interpreted not as "less than" but as stream redirection operator. The reason is that [ is just another program (an alias for test), so [ "$?" < "0" ] is similar to cat < filename. The other error is that you don't want to check for "less than" but for "not equal". In sum:
if [ "$?" < "0" ]
should be
if [ "$?" -ne "0" ]
Or you could write
if ! grep "$name" ~uli101/2014c/phonebook
...because if interprets a return code of zero as true and everything else as false.
never mind one of my friends pushed me in the right direction:
all I had to do is:
if [ "$?" = "1" ]
then echo "error"
fi
pretty much I was over thinking it, I just needed to say if $? = 1 then error, because in the readings it said $? can be greater then 1 I was trying to compensate for that.
It should be
if [ "$?" -gt 0 ]
The symbol '<' is a redirection operator, and it's not a Python or C - everything in a shell script is a command, including the text after 'if', and you are executing a command named '[' here, you may find it at the location /usr/bin/[, and this command uses -gt and -lt parameters to compare numbers, instead of '>' and '<', which are special shell operators.
You can rewrite this code like this:
if grep -i "$name" ~uli101/2014c/phonebook
then true # 'true' is also a command, which does nothing and returns success
else echo "Error"
fi
or even like this, using '||' operator, which will execute following command only if previous command returned an error:
grep -i "$name" ~uli101/2014c/phonebook || echo "Error"
The "$?" doesn't need quotes, as it is a number really. If you want better script, check on existance of the Phonebook file, and exit before asking the Name input if the file is missing. Also, if you reply nothing (enter only) on the READ command, you may need to do something.
#!/bin/bash
name=$1
phonebook=~/phonebook
if [ "$name" = "" ]
then
echo -n "Enter a name to search for: "
read name
fi
grep -i "$name" $phonebook
if [ $? -gt 0 ]
then
echo "error, no \"$name\" in $phonebook"
fi

Need to validate the length of a variable and perform an action accordingly in unix

We have a variable with a value in it. My requirement is if the length of the variable is greater than 0(zero) do something else do another thing
Step1: I have a variable with some value say
abc="asdfg"
Step2: Determine the length of the variable
xyz=`echo ${#abc}`
Step3: Now I use a If loop to determine if the variable has value greater than 0
if [[$xyz -gt 0]]; then
echo "success"
else
echo "fail"
fi
I am having a problem at step 3. it does not give me a success message. need assistance
You can check if a variable is not zero length with -n
if [ -n $abc ]; then
echo "success"
fi
if [[$xyz -gt 0]]; then
should be:
if [[ $xyz -gt 0 ]]; then
You need spaces between the brackets and parameters.
You can do it easily like this:
VAR=""
if [ "$VAR" ]; then echo Full; fi
VAR="a"
if [ "$VAR" ]; then echo Full; fi
Full
You are missing some spaces around your square brackets.
A short and fast solution to do this is to use ${#var} directly in an if-test:
if (( ${#abc} )); then
# ...
fi
This removes the unnecessary command-substituion that you have there and tests directly on the length. If the length is zero then the test fails.

Bash ARGS help - getting random error

Trying to set rules if a certain variable is put into place, can someone identify wtf I'm missing here?
test.sh:
#!/bin/bash
args=($#)
if [ "$#" = "--cron" ]; then
echo "WORKS!";
else echo "FAILS"
fi
output of ./test.sh:
./test.sh: line 3: [: =: unary operator expected
FAILS
However, when I run ./test.sh --cron, it works, and WORKS is output.
The correct way to do this varies a bit depending on exactly what you're trying to do. If you want to check whether the first argument is --cron, use this:
if [ "$1" = "--cron" ]; then
If you want to check whether the only argument is --cron, use this:
if [ "$*" = "--cron" ]; then
(Note that this is one of very few cases where "$*" is the right way to do something -- it expands to all arguments separated by spaces, but treated as a single word for parsing purposes.)
If you want to check whether any argument is --cron, use this:
cronopt=false
for argument; do
if [ "$argument" = "--cron" ]; then
cronopt=true
break # note: if you are scanning the arguments for other things too, remove this
fi
done
if $cronopt; then
...
BTW, I'm not sure what you're using the args=($#) line for, but if you want to store the arguments in an array the correct way to do it is args=("$#") -- the quotes keep it from doing word splitting, filename expansion, etc before putting the args into the array.
This should work, but only for the first element, of you want more you might have to do a for or while loop to iterate thru the arguments.
#!/bin/bash
args=($1)
if [ $args ] && [ $args = "--cron" ]; then
echo "WORKS!";
else echo "FAILS"
fi
In this case, I believe "$#" is too well quoted.
Try comparing against "$1", or use:
#!/bin/bash
args=($#)
if [ "$*" = "--cron" ]; then
echo "WORKS!";
else
echo "FAILS"
fi

Resources