ruby: method_missing backtick typo? - ruby

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.

Related

While using a single quote in a string in shell with Ruby I get sh: -c: line 0: unexpected EOF while looking for matching `''

This works fine in Ruby, and it should work:
puts 'don\'t'
But I want to run the same in BASH with Ruby:
%x(echo 'don\'t')
I get this error:
sh: -c: line 0: unexpected EOF while looking for matching''`
Same error occurs with ``, system(), Open3
My actual code snippet:
require 'open3'
module XdoTool
BIN = 'xdotool'
EXEC = ::ENV['PATH'].split(::File::PATH_SEPARATOR).map { |path| ::File.join(path, BIN) if ::File.executable?(::File.join(path, BIN)) }.compact.last
raise RuntimeError, "No #{BIN} found in the exported paths. Please make sure you have #{BIN} installed" unless EXEC
class << self
def type(str)
Open3.capture2("#{EXEC} type --delay 0 '#{str = str.gsub("'", '\'')}'")
str
end
end
end
# Types the quoted text anywhere.
XdoTool.type("What is the reason of the error?")
# sh: -c: line 0: unexpected EOF while looking for matching `''
# sh: -c: line 1: syntax error: unexpected end of file
XdoTool.type("What's the reason of the error?")
Please do note that the str can have anything. It can contain alphanumeric characters, symbols, emojis, or combination of all these things. How can I get around the problem with quotes here?
In shell, you simply cannot include a single quote inside a single-quoted string. It has to be in a double-quoted string. That means, if you want an argument that contains both, you need to concatenate separately quoted stings together.
echo 'He said "I can'"'"t'"'
or escape the double quotes inside a double-quoted string
echo "He said \"I can't\""
(Some shells provide yet another form of quoting that can contain an escaped single quote, namely $'He said "I can\'t"'. However, that's an extension to the POSIX standard that you can't assume is supported by the
shell Ruby will use to execute your command.)

How does : <<'END' work in bash to create a multi-line comment block?

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.

Difference between \`nonexistent_command\` and \``"nonexistent_command"\`

Why do \`some nonexistent command\` and `\`"some nonexistent command"\` behave differently?
irb> \`nonexistent_command\`
Errno::ENOENT: No such file or directory - nonexistent_command
in ``'
irb> \`"nonexistent_command"\`
sh: nonexistent_command: command not found
=> ""
Why is it like this?
when you use backticks ` that is telling ruby to go out and try and run that system command. You are getting an "Error no entry" (ENOENT) error back from the operating system which can't find that command or file.
when you use double quotes " you are creating a string and that is no problem.
food for thought: perhaps you mean to use single quotes (which is simply a string that does not get variable interpolation done on it) instead of backticks?

Ruby: Why do I get warning "regex literal in condition" here?

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.

SyntaxError for strings "#$" and "##"

Can someone explain this behavior to me?
>> "#$"
SyntaxError: (irb):3: unterminated string meets end of file
from /Users/milan/.rvm/rubies/ruby-1.9.2-head/bin/irb:16:in `<main>'
>> "##"
SyntaxError: (irb):4: syntax error, unexpected $undefined
(irb):4: unterminated string meets end of file
from /Users/milan/.rvm/rubies/ruby-1.9.2-head/bin/irb:16:in `<main>'
>> "#$$"
"10994"
Did I miss some new feature of 1.9.2? Confused.
As you probably know you can use #{ expression } inside a double quoted value to insert the value of expression into the string at that position. A little known sub-feature is that if the expression is just a global or instance variable, you can leave out the braces. I.e. #$foo inside a double quoted string will insert the value of the global variable $foo and ##foo will do the same for instance variables.
So your first two examples error out, because it thinks you want to get the variables $" or #" respectively (the latter of which is not a variable name - though the first one is - which is why you get two error messages for the second and just one for the first), leaving the string unclosed. And the third example simply gives you the value of the variable $$.
If you don't want this to happen you can escape the # with a backslash in front of it (or simply use single quotes instead of double quotes if you don't need any double-quote-specific behavior).
This behavior is not specific to ruby 1.9 - it has always been like this.

Resources