Reputation: 3
I'm encountering an issue with a ruby script called from CLI (not in a framework) that results in a ENOENT error (File does not exist) when attempting to open it, but only when the filename has been constructed using gsub regular expression replacement. It works when the string is constructed by any other method. I'm sure it's something simple that I've done wrong, but I've had a heck of a time figuring it out.
System Information
# lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description: Ubuntu 12.04.3 LTS
Release: 12.04
Codename: precise
# rvm -v
rvm 1.23.13 (stable) by Wayne E. Seguin <[email protected]>, Michal Papis <[email protected]> [https://rvm.io/]
# ruby -v
ruby 2.0.0p195 (2013-05-14 revision 40734) [x86_64-linux]
Filesystem: ext2
Permissions:
# ls -lah /home/boise/config
total 24K
drwxr-xr-x 2 boise boise 4.0K Nov 13 19:40 .
drwxr-xr-x 11 boise boise 4.0K Nov 13 20:22 ..
-rwxr-xr-x 1 boise boise 248 Nov 13 09:31 boise.yaml
-rwxr-xr-x 1 boise boise 335 Nov 13 19:35 config.rb
-rwxr-xr-x 1 boise boise 339 Nov 13 20:06 stanford.yaml
-rwxr-xr-x 1 boise boise 248 Nov 13 09:49 test.yaml
user being used to run scripts and owns containing directory is 'boise'
there is nothing abnormal about the file system here, no symbolic links or
mappings or anything like that.
I started a simple script to reproduce the error. In this I boiled it down as simple as I could, made the prefix of the path static and used absolute paths so that there is no question about relative paths or current working directories.
scriptname: testing
#!/usr/bin/env ruby
require 'yaml'
# Here we are "hardcoding" the values.
$config_yaml = "boise.yaml"
puts $config_yaml
config_filespec = "/home/boise/config/#{$config_yaml}"
puts config_filespec
puts File.exists? config_filespec
$configuration = YAML.load(File.open(config_filespec))
$verbose = true
hosts = []
args = []
puts $config_yaml
ARGV.each do |arg|
if arg.match /hosts\=(.*?)/i
hosts = ARGV.delete(arg).gsub(/hosts\=(.*?)/i,'\2').split(',')
elsif arg == "quiet"
$verbose = false
ARGV.delete(arg)
elsif arg.match /cfg=(.*?)/
#Here the value of the YAML file to be loaded is set with a regular expression substitution.
$config_yaml = ARGV.delete(arg).gsub(/cfg=(.*?)/, "\2")
else
end
end
#do the same output as we did the first time hardcoded, but this time the
#value got reset by a gsub -- same variable, same name, etc
puts $config_yaml
config_filespec = "/home/boise/config/#{$config_yaml}"
puts config_filespec
puts File.exists? config_filespec
$configuration = YAML.load(File.open(config_filespec))
This works as one would expect:
# ./testing
boise.yaml
/home/boise/config/boise.yaml
true
boise.yaml
boise.yaml
/home/boise/config/boise.yaml
true
But watch what happens when I'm passing in the yaml string as a command line parameter. Note that this is even the exact same filename!
# ./testing cfg=boise.yaml
boise.yaml
/home/boise/config/boise.yaml
true
boise.yaml
boise.yaml
/home/boise/config/boise.yaml
false
./testing:31:in `initialize': No such file or directory - /home/boise/config/boise.yaml (Errno::ENOENT)
from ./testing:31:in `open'
from ./testing:31:in `<main>'
I also tried forcefully casting the string with String(config_filespec), config_filespec.to_s, and using + "" to do a string concatenation, in hopes that it was some type issue with string versus literal or regex or something like that. Any ideas?
Thanks in advance Stack Overflow!
Upvotes: 0
Views: 325
Reputation: 3741
The answer is that you're not getting the results that you think you're getting from the gsub. This boils down to a ruby quirk. When using double-quotes, "\2" evaluates to "002". However, using single quotes '\2' will return the second capture group. In your code...
$config_yaml = ARGV.delete(arg).gsub(/cfg=(.*?)/, '\2')
If you're only going to accept a single file name, would it be worth using split?
$config_yaml = ARGV.delete(arg).split(/=/, 2).last
Upvotes: 1