SyntaxError for strings "#$" and "##" - ruby

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.

Related

String operations and `eval`

I have an AWS lambda function that receives user's code from a browser as a string and runs eval in a sandboxed environment as in a standard REPL. I am not trying to interpolate a string. I am trying to have eval recognize and perform operations on strings.
I am somewhat limited in the operations I can perform. Basic regex replacement is cool, but I don't think I would be able to do anything more involved than that, but perhaps I'm mistaken.
This works for basic arithmetic operations, the creation of class instances, etc. However, it fails to perform string operations properly. When I pass:
eval `("3*4")`
it returns 12, which is great. However, if I pass:
eval(""str:" + " test"")
it fails to return "str: test". In fact it returns nothing.
It has been suggested that I escape the double quotes. In a REPL, replacing all double quotes with escaped ones, such as \", works.
eval("\"str \" + \"test\"") # => "str test"
However, when I try this with AWS, it returns "\"str \" + \"test\"".
I look forward to hearing your responses.
You shouldn't expect eval(""str:" + " test"") to work. The problem is not related to AWS lambda. If you try this on your own machine, using pry or irb, you will get a SyntaxError. That's because your interpreter can't understand that you are only passing one unified string to eval. So you need to escape all quotation marks inside your string:
eval("\"str \" + \"test\"")
If you have tested it in a REPL without escaping, and it worked, it seems that the REPL you are using, somehow changes your input before sending it to interpreter.
I have seemed to find a work around.
To begin, I will first clarify what my issue was. Say a user entered the following:
class Test
attr_accesssor :data
def initialize(data = nil)
#data = data
end
end
Followed by
Test.new(4).data
eval would properly return 4.
Now say, however, that the user instead wrote
Test.new("A nice, fine string").data
eval would return nothing.
I noticed, however, that if I tacked on a .inspect, as shown below:
Test.new("A nice, fine string").data.inspect
I would be returned A nice, fine string.
So my solution was to wrap the entirety of the user's code in parenthesis and then call .inspect.
Which was accomplished by the following line
code = "(" << code << ").inspect"
Thank you to everyone who took the time to help me out. I really appreciate all of the feedback and suggestions.
You need to be careful about string interpolation when using eval on strings:
str = %(This is my String. It's name is string. Now I can eval "Hooray for my string")
eval(str.inspect)
#=> "This is my String. It's name is string. Now I can eval \"Hooray for my string\""
however these will raise errors
eval(str)
#=> SyntaxError: (eval):1: unterminated string meets end of file
eval(puts str) # will output the string but raise an error after it.
#=> This is my String. It's name is string. Now I can eval "Hooray for my string"
# TypeError: no implicit conversion of nil into String
but this will work
eval("puts #{str.inspect}")
#=> This is my String. It's name is string. Now I can eval "Hooray for my string"

What does "unterminated quoted string meets end of file" mean in MacVim?

I'm looking a way to compile Ruby code on OSX. I am using MacVim, and my code says:
puts "test"
I type rubydo %, and I get an error message SyntaxError: eval:1: unterminated quoted string meets end of file. What am I doing wrong?
Vim's rubydo command executes a command. You may have thought it was a filename (and used "%" as the parameter, i.e., the current buffer). The % is ruby's alternate string delimiter, and depending on how rubydo is implemented, the bare "%" could be mistaken for the beginning of a string.
The % Notation (Ruby Programming)
Vim documentation: if_ruby

ruby: method_missing backtick typo?

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.

error in shell script: unexpected end of file

The following script is showing me "unexpected end of file" error. I have no clue why am I facing this error. My all the quotes are closed properly.
#!/usr/bin/sh
insertsql(){
#sqlite3 /mnt/rd/stats_flow_db.sqlite <<EOF
echo "insert into flow values($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18)"
#.quit
}
for i in {1..100}
do
src_ip = "10.1.2."+$i
echo $src_ip
src_ip_octets = ${src_ip//,/}
src_ip_int = $src_ip_octets[0]*1<<24+$src_ip_octets[1]*1<<16+$src_ip_octets[2]*1<<8+$src_ip_octets[3]
dst_ip = "10.1.1."+$i
dst_ip_octets = ${dst_ip//,/}
dst_ip_int = $dst_ip_octets[0]*1<<24+$dst_ip_octets[1]*1<<16+$dst_ip_octets[2]*1<<8+$dst_ip_octets[3]
insertsql(1, 10000, $dst_ip, 20000, $src_ip, "2012-08-02,12:30:25.0","2012-08-02,12:45:25.0",0,0,0,"flow_a010105_a010104_47173_5005_1_50183d19.rrd",0,12,$src_ip_int,$dst_ip_int,3,50000000,80000000)
done
That error is caused by <<. When encountering that, the script tries to read until it finds a line which has exactly (starting in the first column) what is found after the <<. As that is never found, the script searches to the end and then complains that the file ended unexpectedly.
That will not be your only problem, however. I see at least the following other problems:
You can only use $1 to $9 for positional parameters. If you want to go beyond that, the use of the shift command is required or, if your version of the shell supports it, use braces around the variable name; e.g. ${10}, ${11}...
Variable assignments must not have whitespace arount the equal sign
To call your insertsql you must not use ( and ); you'd define a new function that way.
The cass to your insertsql function must pass the parameters whitespace separated, not comma separated.
A couple of problems:
There should be no space between equal sign and two sides of an assignment: e.g.,: dst_ip="10.1.1.$i"
String concatenation is not done using plus sign e.g., dst_ip="10.1.1.$i"
There is no shift operator in bash, no <<: $dst_ip_octets[0]*1<<24 can be done with expr $dst_ip_octets[0] * 16777216 `
Functions are called just like shell scripts, arguments are separated by space and no parenthesis: insertsql 1 10000 ...
That is because you don't follow shell syntax.
To ser variable you are not allowed to use space around = and to concatenate two parts of string you shouldn't use +. So the string
src_ip = "10.1.2."+$i
become
src_ip="10.1.2.$i"
Why you're using the string
src_ip_octets = ${src_ip//,/}
I don't know. There is absolutely no commas in you variable. So even to delete all commas it should look like (the last / is not required in case you're just deleting symbols):
src_ip_octets=${src_ip//,}
The next string has a lot of symbols that shell intepreter at its own way and that's why you get the error about unexpected end of file (especially due to heredoc <<)
src_ip_int = $src_ip_octets[0]*1<<24+$src_ip_octets[1]*1<<16+$src_ip_octets[2]*1<<8+$src_ip_octets[3]
So I don't know what exactly did you mean, though it seems to me it should be something like
src_ip_int=$(( ${src_ip_octets%%*.}+$(echo $src_ip_octets|sed 's/[0-9]\+\.\(\[0-9]\+\)\..*/\1/')+$(echo $src_ip_octets|sed 's/\([0-9]\+\.\)\{2\}\(\[0-9]\+\)\..*/\1/')+${src_ip_octets##*.} ))
The same stuff is with the next strings.
You can't do this:
dst_ip_int = $dst_ip_octets[0]*1<<24+$dst_ip_octets[1]*1<<16+$dst_ip_octets[2]*1<<8+$dst_ip_octets[3]
The shell doesn't do math. This isn't C. If you want to do this sort of calculation, you'll need to use something like bc, dc or some other tool that can do the sort of math you're attempting here.
Most of those operators are actually shell metacharacters that mean something entirely different. For example, << is input redirection, and [ and ] are used for filename globbing.

Watir magic escape sequence?

I am currently using Watir with Firefox and it seems that when I try to set a field with the following text:
##$QWER7890uiop
The command I am using is the following:
text_field(:name, "password").value=("!##$QWER7890uiop)
I've also tried this:
text_field(:name, "password").set "!##$QWER7890uiop)
Only the first 2 characters get entered. Is there something I can do to by pass this feature?
You need to escape the string using single quotes '.
text_field(:name, "password").value='"!##$QWER7890uiop'
Many characters are substituted inside double quotes.
Escape sequences like \n, \t, \s, etc are replaced by their equivalent character(s). See here for full list.
#{} where anything the braces is interpreted as a ruby expression.
#$something where $something is interpreted as a ruby global variable. That's the problem with your quote above, beside not being terminated.
%s is interpreted as an ERB template expression (it is interpolated).
For instance:
puts "%s hours later" % 'Five'
results in
"Five hours later".

Resources