I know this questions has been touched on elsewhere, but I'm slightly confused about the proper syntax for multi-line (block?) if else statements in ruby.
As an example:
if condition then
do something
do somethingelse
do yetanotherthing
done
else
do acompletelyunrelatedthing
done
I understand that the then statement is required if multiple lines are used, but is the done before the else necessary? This seems like it would break out of the if...else context. When I do include this done I get:
syntax error, unexpected '\n', expecting tCOLON2 or '[' or '.'
When I don't include it I get:
syntax error, unexpected keyword_else, expecting keyword_end
Um... there is no done keyword in Ruby. Here is the correct syntax:
if condition
# do stuff
else
# do other stuff
end
The then keyword is not needed either.
The then keyword is only used if you need to put the whole thing on one line (to separate the condition from the action to take if true):
if condition then do_something else do_something_different end
If you don't want it all one line (usually you don't), the syntax is as in Doorknob's answer.
Related
I have a script like this:
#!/bin/bash
x=${1:-20}
for ((i=1;i<=x;i++))
{
if ((i%3==0))
{
echo 'Fizz'
}
echo $i
}
I get an error color on the last brace in VIM and when I try to run the script I get a "syntax error near unexpected token" for that same brace. Without the nested if statement, this will print 1 through 20 on a new line for each number, which is the expected outcome. If the number is divisible by 3, it should print Fizz instead of that number. I'm not as worried about how to implement the replacement, that should be easy to figure out, but what I don't understand is why I cannot use a brace to close the for loop. If I take out the brace, I get an error that says end of file expected. So what is the proper syntax for ending a for loop with a nested if statement? I've looked around online and here on stack but haven't found a similar format to what I am trying to do. I don't like the
for f in *
format as it is not as easy to read for someone coming from a different coding language and I like to keep my code looking very similar across different languages (I use comments too, but just the same, I try to keep things as similar as possible which is why I used (( )) with the for loop.)
If I comment out the if statement and leave everything else intact, the error disappears and it will print
1
Fizz
2
Fizz
etc.
Any insight into this would be greatly appreciated. Thanks!
So here is what I was able to figure out thanks to #Cyrus:
x=${1:-20}
for ((i=1;i<=x;i++))
do
if ((i%3==0))
then
echo 'Fizz'
else
echo $i
fi
done
In many ways bash is simpler than most other languages but that makes it harder to work with when you are used to "higher" level languages.
So, to help out anyone else that's like me and just starting to code with bash, here is the full program I made, with comments as to why I coded it the way I did. If there are errors in my explanation or my formatting style, please point them out. Thanks! This was kind of fun to write, call me crazy.
# This will literally just print the string inside the single quotes on the screen
echo 'Try running this again but with something like this: fizzbuzz 25 pot kettle black'
# The $0 is the first index, in other words the file name of the executable,
# this will set the default value of x to 20 but will allow the user to input
# something else if they want.
x=${1:-20}
# This is the same but with string variables
f=${2:-FizzBuzz}
g=${3:-Fizz}
b=${4:-Buzz}
# We start the index variable at 1 because it's based on the input,
# otherwise it would echo 0 thru 19
for ((i=1;i<=x;1++))
do
# I recommend using (( )) for if statement arithmetic operations
# since the syntax is similar to other programming languages
if ((i%3==0 && i%5==0)); then echo $f
# you need to use a semicolon to separate if and then if they are
# on the same line, otherwise you can just go to the next line for
# your then statement
else if ((i%3==0)); then echo $g
else if ((i%5==0)); then echo $b
else echo $1
# You need fi in order to finish an if then statement
fi fi fi
done
A simple Ruby program, which works well (using Ruby 2.0.0):
#!/usr/bin/ruby
while gets
print if /foo/../bar/
end
However, Ruby also outputs the warning warning: regex literal in condition. It seems that Ruby considers my flip-flop-expression /foo/../bar/ as dangerous.
My question: Where lies the danger in this program? And: Can I turn off this warning (ideally only for this statement, keeping other warnings active)?
BTW, I found on the net several discussions of this kind of code, also mentioning the warning, but never found a good explanation why we get warned.
You can avoid the warning by using an explicit match:
while gets
print if ~/foo/..~/bar/
end
Regexp#~ matches against $_.
I don't know why the warning is shown (to be honest, I wasn't even aware that Ruby matches regexp literals against $_ implicitly), but according to the parser's source code, it is shown unless you provide the -e option when invoking Ruby, i.e. passing the script as an argument:
$ ruby -e "while gets; print if /foo/../bar/ end"
I would avoid using $_ as an implicit parameter and instead use something like:
while line = gets
print line if line=~/foo/..line=~/bar/
end
I think Neil Slater is right: It looks like a bug in a parser. If I change the code to
#!/usr/bin/ruby
while gets
print if $_=~/foo/..$_=~/bar/
end
the warning disappears.
I'll file a bug report.
I was building a method to send me an email through mutt when my ruby scripts fail. It looks something like this:
begin
UnknownFunction()
rescue
subject = 'Error'
to_array = ['email#email.com','email2#email.com']
body = "An error occurred:\n#{$!}"
%x[echo "#{body}" | mutt -s "#{subject}" #{to_array.join(",")}]
end
the command was throwing the following error:
sh: -c: line 1: unexpected EOF while looking for matching ``'
sh: -c: line 2: syntax error: unexpected end of file
I finally looked closely enough to see that $! contains a backtick before the undefined method's name followed by a single quote:
undefined method `UnknownFunction' for main:Object
I dug into the code and verified the method_missing method has a backtick before and single quote afterwards. Should the backtick be a single quote or vice versa? If not, what's the reasoning behind it?
raise NoMethodError, "undefined method `#{mid}' for #{self}", caller(1)
It's a substitute for an open single quote (‘) in plain text/pre-Unicode environments. See: Why do plain-text technical articles often enclose terms within backticks and single quotes?
The description of the NoMethodError is not meant to be code, so the use of a backtick there is purely for aesthetic reasons. If you want to pass an arbitrary string to the shell, use Shellwords.shellescape.
backticks are fine for simple commands, but once you start throwing data at the child process I think its better to use something more sophisticated than piping the text via echo. I'd use IO.popen:
IO.popen(
"mutt -s '%s' %s" % [ subject, to_array.join(',') ],
'w'
) do |mutt|
mutt.puts body
end
That's untested but is what I'd start with. It's more readable because it gets rid of the backtick jungle of interpolated variables. It also avoids potential problems of the sub-shell trying to help by interpreting variables or looking for embedded backticks in the text being sent to mutt.
I found a nifty little shell script that I wanted to use from this website here. I have followed everything step-by-step, but receive the following error when running this on my CentOS box.
./deploy: line 3: =: command not found
Line 3 only contains...
$ERRORSTRING = "Error. Please make sure you've indicated correct parameters"
I've tried toying around with a bit, but don't understand why it won't accept the "=" character. Is there something wrong with the script, or is it merely something different in the way that my server processes the script?
Thanks!
Gah, that script is full of bad scripting practices (in addition to the outright error you're running into). Here's the outright error:
$ERRORSTRING = "Error. Please make sure you've indicated correct parameters"
As devnull pointed out, this should be:
ERRORSTRING="Error. Please make sure you've indicated correct parameters"
A couple of lines down (and again near the end), we have:
echo $ERRORSTRING;
...which works, but contains two bad ideas: a variable reference without double-quotes around it (which will sometimes be parsed in unexpected ways), and a semicolon at the end of the line (which is a sign that someone is trying to write C or Java or something in a shell script). Use this instead:
echo "$ERRORSTRING"
The next line is:
elif [ $1 == "live" ]
...which might work, depending on whether the value of $1 has spaces, or is defined-but-blank, or anything like that (again, use double-quotes to prevent misparsing!). Also, the == comparison operator is nonstandard -- it'll work, because bash supports it in its [ ... ] builtin syntax, but if you're counting on having bash extensions available, why not use the much cleaner [[ ... ]] replacement? Any of these would be a better replacement for that line:
elif [ "$1" = "live" ]
elif [[ $1 == "live" ]]
elif [[ "$1" == "live" ]]
Personally, I prefer the last. The double-quotes aren't needed in this particular case, but IMO it's safest to just double-quote all variable references unless there's a specific reason not to. A bit further down, there's a elif [ $2 == "go" ] that the same comments apply to.
BTW, there's a good sanity-checking tool for shell scripts at www.shellcheck.net. It's not quite as picky as I am (e.g. it doesn't flag semicolons at the ends of lines), but it pointed out all of the actual errors in this script...
"Devnulls" answer was correct -- I had to remove the spaces around the "=" and remove the "$" from that line as well. The end result was...
ERRORSTRING="Error. Please make sure you've indicated correct parameters"
I've upvoted Devnull and gniourf_gniourf's comments.
Thank you to all whom have assisted!
Working on exercise #26 of Learn Ruby The Hard Way -- correcting a ficticious programmer's bad code.
I've got most of it worked out, but can't even get to testing because I keep getting this syntax error:
syntax error, unexpected tIDENTIFIER, expecting ')'
...on this line:
sentence = "All good\tthings come to those who wait."
I thought that was always the way variables were declared? Since the error was listing parens, I tried those too -- around sentence (even though it made no sense), around the string (both with and without quotes), with the equals sign, without the equals sign...I'm not really sure what the issue is here.
Not always errors are on the same lines as interpreter says ;) So it would be better if you include some adjacent lines next time. But as I found these lines are:
puts "We can also do that this way:"
puts "We'd have %d beans, %d jars, and %d crabapples." % secret_formula(start_pont
sentence = "All god\tthings come to those who weight."
words = ex25.break_words(sentence)
sorted_words = ex25.sort_words(words)
From here we see that the line before your specified line doesn't have closing parenthesis ')'.