Moving a file containing a space in ruby using FileUtils - ruby

I'm using Mac OS X and I'm trying to write a little script that moves a file to a specific folder. I'm using the FileUtils API since I don't want to run system specific commands (system("mv a b").
The script looks something like this:
#!/usr/bin/env ruby
require 'rubygems'
require 'escape'
require 'fileutils'
absolut_input_filename = Escape.shell_single_word ARGV[0]
move_folder = Escape.shell_single_word "/move/to/folder"
FileUtils.mv absolut_input_filename, move_folder
As long as the input filename contains no space, everything works fine. But as soon as I put in a file with a space the error output is something like this:
./scripts/success /path/to/file\ with\ space
/usr/local/Cellar/ruby/1.9.2-p0/lib/ruby/1.9.1/fileutils.rb:1418:in `stat': No such file or directory - '/path/to/file with space' (Errno::ENOENT)
from /usr/local/Cellar/ruby/1.9.2-p0/lib/ruby/1.9.1/fileutils.rb:1418:in `block in fu_each_src_dest'
from /usr/local/Cellar/ruby/1.9.2-p0/lib/ruby/1.9.1/fileutils.rb:1432:in `fu_each_src_dest0'
from /usr/local/Cellar/ruby/1.9.2-p0/lib/ruby/1.9.1/fileutils.rb:1416:in `fu_each_src_dest'
from /usr/local/Cellar/ruby/1.9.2-p0/lib/ruby/1.9.1/fileutils.rb:504:in `mv'
from ./scripts/success:8:in `<main>'
For escaping I use the 'escape' gem in version 0.0.4 in which the shell_single_word looks like this:
def shell_single_word(str)
if str.empty?
"''"
elsif %r{\A[0-9A-Za-z+,./:=#_-]+\z} =~ str
str
else
result = ''
str.scan(/('+)|[^']+/) {
if $1
result << %q{\'} * $1.length
else
result << "'#{$&}'"
end
}
result
end
end

you can just not use escape
require 'fileutils'
absolut_input_filename = ARGV[0]
move_folder = "/move/to/folder"
FileUtils.mv absolut_input_filename, move_folder

I don't actually know from Ruby, so take this with a grain of salt, but I know the underlying OS primitives inside and out, and from C you can do this with rename(2). Therefore, from Ruby, you should be able to do this with File.rename, which requires no quoting at all. Try this:
#! /usr/bin/env ruby
tomove = ARGV[0]
target = "/path/to/target/folder"
File.rename(tomove, File.join(target, File.basename(tomove)))

Solved using soft links:
ln -s ~/Folder\ with\ spaces/foo/ ./foo
now i can use FileUtils commands without problems:
FileUtils.cp("bar.txt", "foo/foobar.txt")
Hope will help!

Related

How do I run a Ruby script in the Terminal?

I'm attempting to run a Ruby script (linked below) that was shared by DHH to convert a number of .dcp Leica Q camera profiles to Leica M10 camera profiles.
I'm just not sure how to run it. I understand it needs to be run in Terminal but that's about it.
I have all Leica Q camera profiles in a single folder on the desktop... Now what?
I've downloaded the DCP tool that's mentioned in the comments of the script.
Here's a link to the GitHub repo: https://gist.github.com/dhh/d3c8cf9309b662047257b7e583c3f595#file-dcp-converter-rb-L8
I know this might be pretty basic but any help would be greatly appreciated!
Here's the actual script:
#!/usr/bin/env ruby
# Requires that you have ./bin/dcpTool from https://sourceforge.net/projects/dcptool/
require 'rubygems'
require 'bundler/setup'
require 'nokogiri'
input_camera_model = ARGV[0] || "LEICA Q (Typ 116)"
output_camera_model = ARGV[1] || "LEICA M10"
input_dir = ARGV[2] || "./input"
output_dir = ARGV[3] || "./output"
def convert_profile_name(profile_name, input_camera_model, output_camera_model)
File.basename(profile_name.gsub(/#{input_camera_model.gsub(/\(/, "\\(").gsub(/\)/, "\\)")}/, output_camera_model), ".dcp")
end
def replace_camera_model(xml_profile_filename, output_camera_model)
profile_doc = Nokogiri::XML(File.read(xml_profile_filename))
profile_doc.xpath('//UniqueCameraModelRestriction').first.content = output_camera_model
File.open(xml_profile_filename, "w+") { |file| file.write(profile_doc.to_xml) }
end
Dir.entries(input_dir).reject { |file| file =~ /^(\.|\.\.)$/ }.each do |existing_profile|
converted_profile = convert_profile_name(existing_profile, input_camera_model, output_camera_model)
existing_dcp_filename = File.join(input_dir, existing_profile)
xml_filename = "#{File.join(output_dir, converted_profile)}.xml"
decompile_command = "./bin/dcpTool -d '#{existing_dcp_filename}' '#{xml_filename}'"
puts "Decompiling #{existing_dcp_filename} into XML"
`#{decompile_command}`
puts "Replacing camera model: #{input_camera_model} -> #{output_camera_model}"
replace_camera_model(xml_filename, output_camera_model)
converted_dcp_filename = "#{File.join(output_dir, converted_profile)}.dcp"
recompile_command = "./bin/dcpTool -c '#{xml_filename}' '#{converted_dcp_filename}'"
puts "Recompiling XML into #{converted_dcp_filename}"
`#{recompile_command}`
File.delete(xml_filename)
puts
end```
Easiest way:
Create an "input" and an "output" directory in the same location as this script.
Place all of your files in "input"
In the terminal navigate to this location
type ruby dcp-converter.rb.
Note: You may have to run gem install bundler nokogiri first.
If you have a different model than the one shown you may have to pass additional arguments e.g. ruby dcp-converter.rb "LEICA Q (Typ 202)"
The argument order would be ruby dcp-converter.rb [input_model] [output_model] [input_directory] [output_directory]
The defaults are
[input_model] = "LEICA Q (Typ 116)"
[output_model]="LEICA M10"
[input_directory]="./input"
[output_directory]="./output"

Ruby Scripts - Reference Gems

I am writing my first ruby script and am curious how to actually have gem referenced in the script. I am unable to test the code before hand because it reads form an email in /etc/aliases through a pipe.
Any one one with experiences with ruby scripts to advise?
P.S So many bugs because not tested or refactored
Sample Script
#!/usr/bin/env ruby
# Reading files
mail = File.open(ARGV[0])
lines = []
mail.each_with_index do |i,line|
line[i] = lines.#remove leading and trailing spaces
end
first_line = line[1].strip
if line[1] /^(256)/
phone_number = first_line.gsub("+", "")
else
phone_number = "256#{first_line.gsub(/^0+/,"")}"
end
message = line[2].strip
# Sending message
url = "http://xxxxxxxxxxx.com/api/v2/json/messages?token=XXXXXXXXXXXXXXXXXXXXXXXXXXX&to=#{phone_number}&from=XXXXXX&message=#{CGI.escape(message)}"
5.times do |i|
response = HTTParty.get(url)
body = JSON.parse(response.body)
if body["status"] == "Success"
break
end
end
Gems in question are CGI, Httparty, and Json parsing.
Using external gems can be done by calling the "require" method.
So to include them in your script, the first few lines could be something like this:
#!/usr/bin/env ruby
require "json"
require "cgi"
require "httparty"
#rest of your code...
I assume you have installed your gems with gem install <gemname>?

Reading file with Ruby returns strange output

I am trying to read in a JSON file with Ruby and the output is extremely strange. Here is the code that I am using:
require 'rubygems'
class ServiceCalls
def initialize ()
end
def getFile()
Dir.entries('./json').each do |mFile|
if mFile[0,1] != "."
self.sendServiceRequest(mFile)
end
end
end
def sendServiceRequest(mFile)
currentFile = File.new("./json/" + mFile, "r")
puts currentFile.read
currentFile.close
end
end
mServiceCalls = ServiceCalls.new
mServiceCalls.getFile
And here is the output:
Macintosh H??=A?v?P$66267945-2481-3907-B88A-1094AA9DAB6D??/??is32???????????????????????????????????vvz?????????????????????????????????????????????????????????????????????????????????????????????vvz?????????????????????????????????????????????????????????????????????????????????????????????vvz???????????????????????????????????????????????????????????s8m+88888888???????89????????99?????????9:??????????:;??????????;=??????????=>??????????>????????????#??????????#A??????????AC??????????CD??????????DE??????????EE??????????E6OXdknnkdXO6ic118?PNG
bookmark88?A[DT>??A?#
ApplicationsMAMPhtdocsServiceTestAutomationMDXservicecatalog-verizon.json$4T??
`?
U?????l??????
Macintosh H??=A?v?P$66267945-2481-3907-B88A-1094?is32???????????????????????????????????vvz?????????????????????????????????????????????????????????????????????????????????????????????vvz?????????????????????????????????????????????????????????????????????????????????????????????vvz???????????????????????????????????????????????????????????s8m+88888888???????89????????99?????????9:??????????:;??????????;=??????????=>??????????>????????????#??????????#A??????????AC??????????CD??????????DE??????????EE??????????E6OXdknnkdXO6ic118?PNG
UIEvolutions-MacBook-Pro-109:MDXServiceTesting Banderson$ ruby testmdxservices.rb
bookmark88?A?,P>??A?#
ApplicationsMAMPhtdocsServiceTestAutomationMDXservicecatalog-adaptation.json$4T??
`?
U?????l??????
Macintosh H??=A?v?P$66267945-2481-3907-B88A-1094AA9DAB6D??/?<icns<?TOC his32?s8mic118il32?l8mic1?ic07ic13#ic08#ic14^?ic09_ic1?is32???????????????????????????????????vvz?????????????????????????????????????????????????????????????????????????????????????????????vvz?????????????????????????????????????????????????????????????????????????????????????????????vvz???????????????????????????????????????????????????????????s8m+88888888???????89????????99?????????9:??????????:;??????????;=??????????=>??????????>????????????#??????????#A??????????AC??????????CD??????????DE??????????EE??????????E6OXdknnkdXO6ic118?PNG
IHDR szz?iCCPICC Profile(?T?k?P??e???:g >h?ndStC??kW??Z?6?!H??m\??$?~?ًo:?w?>?
كo{?a???"L?"???4M'S??????9'??^??qZ?/USO???????^C+?hM??J&G#Ӳy???lt?o߫?c՚?
? ??5?"?Y?i\?΁?'&??.?<?ER/?dE?oc?ግ#?f45#? ??B:K?#8?i??
??s??_???雭??m?N?|??9}p?????_?A??pX6?5~B?$?&???ti??e??Y)%$?bT?3li?
??????P???4?43Y???P??1???KF????ۑ??5>?)?#????r??y??????[?:V???ͦ#??wQ?HB??d(??B
a?cĪ?L"J??itTy?8?;(???Gx?_?^?[???????%׎??ŷ??Q???麲?ua??n?7???
Q???H^e?O?Q?u6?S??u
?2??%vX
???^?*l
O?????ޭˀq,>??S???%?L??d????B???1CZ??$M??9??P
'w????\/????]???.r#???E|!?3?>_?o?a?۾?d?1Z?ӑ???z???'?=??????~+??cjJ?tO%mN?????
|??-???bW?O+
o?
^?
I?H?.?;???S?]?i_s9?*p???.7U^??s.?3u?
Can someone please tell me what I am doing wrong? Do I need to specify what type of encoding I'm using? I have tried to read the file with gets, sysread, and another I can't remember.
I am not completely sure why but I believe it is the './json' path that is causing the issue. I tried the script on my Windows XP machine and got similar results.
However, when I rewrote the script to include File.dirname(__FILE__) instead of './' it worked. I also cleaned up some of the code.
class ServiceCalls
def get_file
dirname = File.join(File.dirname(__FILE__), 'json')
Dir.entries(dirname).each do |file|
unless file.start_with? '.'
File.open(File.join(dirname, file), 'r') {|f| puts f.read}
end
end
end
end
sc = ServiceCalls.new
sc.get_file
__FILE__ is the path of the current script. File.join uses system independent path separators. File.open, if you pass it a block, will actually close the file for you when it completes the block. String#start_with? is a cleaner way than using [0,1] to get the first element of a string.
try this:
Dir.entries('./json').each do |mFile|
next if ['.', '..'].include?(mFile)
self.sendServiceRequest(mFile)

How do I create directory if none exists using File class in Ruby?

I have this statement:
File.open(some_path, 'w+') { |f| f.write(builder.to_html) }
Where
some_path = "somedir/some_subdir/some-file.html"
What I want to happen is, if there is no directory called somedir or some_subdir or both in the path, I want it to automagically create it.
How can I do that?
You can use FileUtils to recursively create parent directories, if they are not already present:
require 'fileutils'
dirname = File.dirname(some_path)
unless File.directory?(dirname)
FileUtils.mkdir_p(dirname)
end
Edit: Here is a solution using the core libraries only (reimplementing the wheel, not recommended)
dirname = File.dirname(some_path)
tokens = dirname.split(/[\/\\]/) # don't forget the backslash for Windows! And to escape both "\" and "/"
1.upto(tokens.size) do |n|
dir = tokens[0...n]
Dir.mkdir(dir) unless Dir.exist?(dir)
end
For those looking for a way to create a directory if it doesn't exist, here's the simple solution:
require 'fileutils'
FileUtils.mkdir_p 'dir_name'
Based on Eureka's comment.
directory_name = "name"
Dir.mkdir(directory_name) unless File.exists?(directory_name)
How about using Pathname?
require 'pathname'
some_path = Pathname("somedir/some_subdir/some-file.html")
some_path.dirname.mkdir_p
some_path.write(builder.to_html)
Based on others answers, nothing happened (didn't work). There was no error, and no directory created.
Here's what I needed to do:
require 'fileutils'
response = FileUtils.mkdir_p('dir_name')
I needed to create a variable to catch the response that FileUtils.mkdir_p('dir_name') sends back... then everything worked like a charm!
Along similar lines (and depending on your structure), this is how we solved where to store screenshots:
In our env setup (env.rb)
screenshotfolder = "./screenshots/#{Time.new.strftime("%Y%m%d%H%M%S")}"
unless File.directory?(screenshotfolder)
FileUtils.mkdir_p(screenshotfolder)
end
Before do
#screenshotfolder = screenshotfolder
...
end
And in our hooks.rb
screenshotName = "#{#screenshotfolder}/failed-#{scenario_object.title.gsub(/\s+/,"_")}-#{Time.new.strftime("%Y%m%d%H%M%S")}_screenshot.png";
#browser.take_screenshot(screenshotName) if scenario.failed?
embed(screenshotName, "image/png", "SCREENSHOT") if scenario.failed?
The top answer's "core library" only solution was incomplete. If you want to only use core libraries, use the following:
target_dir = ""
Dir.glob("/#{File.join("**", "path/to/parent_of_some_dir")}") do |folder|
target_dir = "#{File.expand_path(folder)}/somedir/some_subdir/"
end
# Splits name into pieces
tokens = target_dir.split(/\//)
# Start at '/'
new_dir = '/'
# Iterate over array of directory names
1.upto(tokens.size - 1) do |n|
# Builds directory path one folder at a time from top to bottom
unless n == (tokens.size - 1)
new_dir << "#{tokens[n].to_s}/" # All folders except innermost folder
else
new_dir << "#{tokens[n].to_s}" # Innermost folder
end
# Creates directory as long as it doesn't already exist
Dir.mkdir(new_dir) unless Dir.exist?(new_dir)
end
I needed this solution because FileUtils' dependency gem rmagick prevented my Rails app from deploying on Amazon Web Services since rmagick depends on the package libmagickwand-dev (Ubuntu) / imagemagick (OSX) to work properly.

Ruby Dir.mkdir Usage

I am pretty new to ruby and have a very simple ruby script that has the following purpose:
Read lines of file
Access jira instance using jira4r gem
Query jira instance for issue(s)
Create a directory using the issue key and issue summary
I've come to the conclusion after some tinkering that the Dir.mkdir command does not accept the object I am passing it as argument.
Findings:
If Dir.mkdir is passed a line, #{chompline}, from my textfile directory creation execute properly.
If Dir.mkdir is passed a string consisting of issue.key and issue.summary it chokes with the following error:
./readFile.rb:29:in `mkdir': No such file or directory - (Errno::ENOENT)
from ./readFile.rb:29
Based on point #1 and #2, it must be something about the string I create from issue key and summary.
I have the following theories/questions:
Is "#{keyPlusSummary}"the correct object type to pass into mkdir as argument ?
I believe it to be string, but perhaps I am assuming incorrectly.
Source:
#!/usr/bin/env ruby
require 'rubygems'
require 'jira4r'
require 'FileUtils'
jira = Jira4R::JiraTool.new(2, "http://jira.somejirainstance.com")
baseurl = jira.getServerInfo().baseUrl
puts "Base URL: " + baseurl , "\n"
jira.login("someUser", "somePassword")
file = File.new("awkOutput.txt", "r")
while (line = file.gets)
chompline = "#{line}".chomp!
issue = jira.getIssue("#{chompline}")
keyPlusSummary = "#{issue.key}"+"#{issue.summary}"
puts keyPlusSummary
Dir.mkdir "#{keyPlusSummary}"
end
file.close
It's a string, but you don't tell us what's in it.
# More canonical, both in var naming, and there's
# no need for concatenation in this case.
dir_name = "#{issue.key}#{issue.summary}"
Are you making the string "directory-name friendly"?
I would not use a JIRA issue summary as a directory name; IMO just the project/issue # would be enough. If you do use the summary, make it something that's directory-friendly by stripping out anything non-alphanumeric, and replacing spaces with underscores.
keyPlusSummary is a string, so it is of the right type. What may be the problem is slashes in the string. Like mkdir in UNIX, Dir.mkdir will not create parent directories for you, it will only create a single directory. If the key + summary includes a '/', then it will read it as a multi-level directory. You need to either escape the '/', or (better), use FileUtils.mkdir_p, or (best) do cleanup to replace ' ' with '_', and remove special characters that make using the directory harder :)
As an aside, your code doesn't need to have the interpolations it does:
#!/usr/bin/env ruby
require 'rubygems'
require 'jira4r'
require 'FileUtils'
jira = Jira4R::JiraTool.new(2, "http://jira.somejirainstance.com")
baseurl = jira.getServerInfo().baseUrl
puts "Base URL: #{baseurl}\n" #use it here!
jira.login("someUser", "somePassword")
File.new("awkOutput.txt", "r") do |file| #using the block form to ensure you close the file
while (line = file.gets)
chompline = line.chomp! #line is already a string, no need to interpolate
issue = jira.getIssue(chompline) #line is already a string, no need
keyPlusSummary = "#{issue.key}#{issue.summary}" #already interpolating, no need to add
puts keyPlusSummary
Dir.mkdir keyPlusSummary #already a string
end
end

Resources