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.
Related
I invoke Ruby from my shell script like this:
ruby -n -e "print if %r($fromre)...%r($tore)" "$#"
Since Ruby 2.6, I get the warning
warning: flip-flop is deprecated
So it seems that i have to be prepared that the wise high priests of Ruby have decided that this (IMO useful) construct will be completely gone one day.
What would be an easy work-around? Of course I can replace
print if %r($fromre)...%r($tore)
by
inside ||= %r($fromre)
if inside
print
inside = false if %r($tore)
end
but I wonder whether there is a more concise way to write this.
I'm now reading Programming Ruby 1.9&2.0 by Dave Thomas, in which there is the following command line script:
$ ruby -pi.bak -e "gsub(/Perl/, 'Ruby')" *.txt
I know from this text that -p option places program code within the loop while; ...; print; end, and regular expressions match against $_ within -e scripts. And I found that gsub is identical to $_.gsub within the -e script. But how gsub got its receiver object, are there any explicit rule describing it?
There is no explicit rule describing it because it works like everywhere else in Ruby and has nothing to do with the -p flag.
gsub gets sent to the main object, because it is the default receiver here, and, like you noted, there is no explicit receiver defined.
Ruby has two different gsub methods. The one in String that you were probably thinking of, and the one in Kernel that is the answer to your question. Kernel gets included by Object, main is an instance of Object.
From the Kernel#gsub documentation:
Equivalent to $_.gsub..., except that $_ will be updated if substitution occurs. Available only when -p/-n command line option specified.
$_ is the "The last string read by gets or readline in the current scope."
I found a great answer for how to comment in bash script (by #sunny256):
#!/bin/bash
echo before comment
: <<'END'
bla bla
blurfl
END
echo after comment
The ' and ' around the END delimiter are important, otherwise things inside the block like for example $(command) will be parsed and executed.
This may be ugly, but it works and I'm keen to know what it means. Can anybody explain it simply? I did already find an explanation for : that it is no-op or true. But it does not make sense to me to call no-op or true anyway....
I'm afraid this explanation is less "simple" and more "thorough", but here we go.
The goal of a comment is to be text that is not interpreted or executed as code.
Originally, the UNIX shell did not have a comment syntax per se. It did, however, have the null command : (once an actual binary program on disk, /bin/:), which ignores its arguments and does nothing but indicate successful execution to the calling shell. Effectively, it's a synonym for true that looks like punctuation instead of a word, so you could put a line like this in your script:
: This is a comment
It's not quite a traditional comment; it's still an actual command that the shell executes. But since the command doesn't do anything, surely it's close enough: mission accomplished! Right?
The problem is that the line is still treated as a command beyond simply being run as one. Most importantly, lexical analysis - parameter substitution, word splitting, and such - still takes place on those destined-to-be-ignored arguments. Such processing means you run the risk of a syntax error in a "comment" crashing your whole script:
: Now let's see what happens next
echo "Hello, world!"
#=> hello.sh: line 1: unexpected EOF while looking for matching `''
That problem led to the introduction of a genuine comment syntax: the now-familiar # (which was first introduced in the C shell created at BSD). Everything from # to the end of the line is completely ignored by the shell, so you can put anything you like there without worrying about syntactic validity:
# Now let's see what happens next
echo "Hello, world!"
#=> Hello, world!
And that's How The Shell Got Its Comment Syntax.
However, you were looking for a multi-line (block) comment, of the sort introduced by /* (and terminated by */) in C or Java. Unfortunately, the shell simply does not have such a syntax. The normal way to comment out a block of consecutive lines - and the one I recommend - is simply to put a # in front of each one. But that is admittedly not a particularly "multi-line" approach.
Since the shell supports multi-line string-literals, you could just use : with such a string as an argument:
: 'So
this is all
a "comment"
'
But that has all the same problems as single-line :. You could also use backslashes at the end of each line to build a long command line with multiple arguments instead of one long string, but that's even more annoying than putting a # at the front, and more fragile since trailing whitespace breaks the line-continuation.
The solution you found uses what is called a here-document. The syntax some-command <<whatever causes the following lines of text - from the line immediately after the command, up to but not including the next line containing only the text whatever - to be read and fed as standard input to some-command. Here's an alternate shell implementation of "Hello, world" which takes advantage of this feature:
cat <<EOF
Hello, world
EOF
If you replace cat with our old friend :, you'll find that it ignores not only its arguments but also its input: you can feed whatever you want to it, and it will still do nothing (and still indicate that it did that nothing successfully).
However, the contents of a here-document do undergo string processing. So just as with the single-line : comment, the here-document version runs the risk of syntax errors inside what is not meant to be executable code:
#!/bin/sh -e
: <<EOF
(This is a backtick: `)
EOF
echo 'In modern shells, $(...) is preferred over backticks.'
#=> ./demo.sh: line 2: bad substitution: no closing "`" in `
The solution, as seen in the code you found, is to quote the end-of-document "sentinel" (the EOF or END or whatever) on the line introducing the here document (e.g. <<'EOF'). Doing this causes the entire body of the here-document to be treated as literal text - no parameter expansion or other processing occurs. Instead, the text is fed to the command unchanged, just as if it were being read from a file. So, other than a line consisting of nothing but the sentinel, the here-document can contain any characters at all:
#!/bin/sh -e
: <<'EOF'
(This is a backtick: `)
EOF
echo 'In modern shells, $(...) is preferred over backticks.'
#=> In modern shells, $(...) is preferred over backticks.
(It is worth noting that the way you quote the sentinel doesn't matter - you can use <<'EOF', <<E"OF", or even <<EO\F; all have the same result. This is different from the way here-documents work in some other languages, such as Perl and Ruby, where the content is treated differently depending on the way the sentinel is quoted.)
Notwithstanding any of the above, I strongly recommend that you instead just put a # at the front of each line you want to comment out. Any decent code editor will make that operation easy - even plain old vi - and the benefit is that nobody reading your code will have to spend energy figuring out what's going on with something that is, after all, intended to be documentation for their benefit.
It is called a Here Document. It is a code block that lets you send a list of commands to another command or program
The string following the << is the marker determining the end of the block. If you send commands to no-op, nothing happens, which is why you can use it as a comment block.
That's heredoc syntax. It's a way of defining multi-line string literals.
As the answer at your link explains, the single quotes around the END disables interpolation, similar to the way single-quoted strings disable interpolation in regular bash strings.
I'm working on implementing Project Euler solutions as semantic Ruby one-liners. It would be extremely useful if I could coerce Ruby to automatically puts the value of the last expression. Is there a way to do this? For example:
#!/usr/bin/env ruby -Ilib -rrubygems -reuler
1.upto(100).into {|n| (n.sum.squared - n.map(&:squared).sum)
I realize I can simply puts the line, but for other reasons (I plan to eval the file in tests, to compare against the expected output) I would like to avoid an explicit puts. Also, it allots me an extra four characters for the solution. :)
Is there anything I can do?
You might try running it under irb instead of directly under a Ruby interpreter.
It seems like the options -f --noprompt --noverbose might be suitable (.
#!/usr/bin/env irb -f --noprompt --noverbose -Ilib -rrubygems -reuler
'put your one-liner here'
The options have these meanings:
-f: do not use .irbrc (or IRBRC)
--noverbose: do not display the source lines
--noprompt: do not prefix the output (e.g. with =>)
result = calculate_result
puts result if File.exist?(__FILE__)
result of eval is last executed operation just like any other code block in ruby
is doing
puts eval(file_contents)
an option for you?
EDIT
you can make use of eval's second parameter which is variables binding
try the following:
do_not_puts = true
eval(file_contents, binding)
and in the file:
....
result = final_result
if defined?(do_not_puts)
result
else
puts(result)
end
Is it an option to change the way you run scripts?
script.rb:
$_= 1.upto(100).into {|n| (n.sum.squared - n.map(&:squared).sum)
invoke with
echo nil.txt | /usr/bin/env/ruby -Ilib -rrubygems -reuler -p script.rb, where nil.txt is a file with a single newline.
So, I get this warning when I'm running my tests in ruby/RoR
.(eval):289: warning: don't put space before argument parentheses
I've checked every where (but obvoiusly not) and I can't find the origin of this error.
The above error just pops up inbetween the unit tests ...
Can someone clue me in onto how to find the location of this error?
The file and line number are contained in the backtrace. However, in your case, the warning is inside a string being evaled at runtime. Which means there is no file. (Actually, the eval method does take optional arguments for the file name and line number that should be displayed in a backtrace, but in this case whoever wrote the code in question unfortunately forgot to pass those arguments.)
I fear that you have no other choice than to manually examine every single call to eval in your entire codebase, and that includes Rails, your testing framework, your entire application, your tests, your plugins, your helpers, the ruby standard library, ...
Of course, you should be aware that the problem might not be obvious as in
eval 'foo (bar, baz)'
It could also be something like
def foo(*args)
puts args.join
end
bar = 'Hello'
baz = 'World'
foostr = 'foo' # in one file
barstr = 'bar' # in another file in a different directory
bazstr = 'baz' # in another file in a different directory
argstr = "(#{barstr}, #{bazstr})" # in yet another file
$, = ' ' # in some third-party plugin
str = [foostr, argstr].join # in a fourth file
eval str # somewhere else entirely
eval str, binding, __FILE__, __LINE__ # this is how it *should* be done
Note the difference between the two warning messages: the first one reads exactly like the one you posted, but the second one has the filename instead of (eval) and the line number inside the file instead of the line number inside the eval string.
By the way: the line number 289 in the warning message is the line number inside the evald string! In other words: somewhere in your application there is a string being evald, which is at least 289 lines long! (Actually, it is more likely that this done not in your application but rather in Rails. The Rails router used to be a particularly bad offender, I don't know if this is still the case.)
It sounds to me that there is a rule which forbids a space between a function name and the parentheses enclosing the arguments of the function.
In many languages this would be considered a permissible stylistic variation.
Is the eval mentioned in the warning message, the 'function' being complained about?
Does the number 289 mean anything as a line number?
Could you search your source files for a parenthesis preceded by a space?
Incidentally, the message says warning. What happens if you ignore it?
If it's happening in between the unit tests it might be in a setup or teardown method. Try searching for eval or try reducing the code you are running until the error goes away. Then you'll know where to look (the code you just removed).