How to get long filename from ARGV - ruby

I want to make a tool that takes some filenames as parameters, but when I use this code:
ARGV.each do|a|
puts "Argument: #{a}"
end
and I use drag and drop or "send to" in Windows, I get the short filename.
So a file like "C:\Ruby193\bin\test\New Text Document.txt" becomes
C:\Ruby193\bin\test\NEWTEX~1.TXT as the argument.
There is no problem when I run the script from the commandline, with the longfilenames as parameters.
How do i get the long filename when i use drag and drop or send to?

http://www.varioustopics.com/ruby/518646-rre-ruby-cygwin-and-paths.html
require 'find'
require 'fileutils'
require 'Win32API'
def get_long_win32_filename(short_name)
max_path = 1024
long_name = " " * max_path
lfn_size = Win32API.new("kernel32", "GetLongPathName", ['P','P','L'],'L').call(short_name, long_name, max_path)
return (1..max_path).include?(lfn_size) ? long_name[0..lfn_size-1] : short_name
end
ARGV.each do|a|
puts a
puts get_long_win32_filename(a)
end

I don't know if it is possible to change the argument you recieve on a drag and drop, but you could use the Win32 getLongPathName() function, using the Ruby Win32 bindings
--edit--
Including #peter's solution formatted for readability:
require 'find'
require 'fileutils'
require 'Win32API'
def get_long_win32_filename(short_name)
max_path = 1024
long_name = " " * max_path
lfn_size = Win32API.new("kernel32",
"GetLongPathName", ['P','P','L'],'L').call(short_name, long_name, max_path)
return (1..max_path).include?(lfn_size) ? long_name[0..lfn_size-1] : short_name
end
ARGV.each do|a|
puts a
puts get_long_win32_filename(a)
end

I learned a lot trying to figure this out!
However, #peter beat me to it with a much simpler solution.
Here is mine, in case someone finds it useful. file_get_long_name.rb
I got the idea from: an archived vb-world.net article and converted it to ruby.
require 'win32ole'
def get_long_filename(shortpath, fso = WIN32OLE.new("Scripting.FileSystemObject"))
path = case
when fso.FolderExists(shortpath)
fso.GetFolder(fso.GetAbsolutePathName(shortpath))
when fso.FileExists(shortpath)
fso.GetFile(fso.GetAbsolutePathName(shortpath))
else
return nil
end
parts = path.Path.split(/\\/)
working = fso.GetDrive(parts.shift).RootFolder
longpath = working.Path
parts.each do |part|
temppath = fso.BuildPath(longpath, part)
working = fso.GetFolder(longpath)
if fso.FolderExists(temppath)
working.SubFolders.each do |sub|
longpath = fso.BuildPath(longpath, sub.Name) if part== sub.ShortName || part == sub.Name
end
elsif fso.FileExists(temppath)
working.Files.each do |sub|
longpath = fso.BuildPath(longpath, sub.Name) if part== sub.ShortName || part == sub.Name
end
end
end
longpath
end
fso = WIN32OLE.new("Scripting.FileSystemObject")
short = "C:\\DOCUME~1\\jamal\\Desktop\\NEWTEX~1.TXT"
long = get_long_filename(short, fso)
p long
# ==> "C:\\Documents and Settings\\jamal\\Desktop\\New Text Document.txt"

I found the reason my script receaved short filenames, i had done a registry patch to enable the drag and drop on ruby scripts and schortcuts as follows
Windows Registry Editor Version 5.00
[HKEY_CLASSES_ROOT\rbfile\ShellEx\DropHandler]
#="{86C86720-42A0-1069-A2E8-08002B30309D}"
[HKEY_CLASSES_ROOT\rbwfile\ShellEx\DropHandler]
#="{86C86720-42A0-1069-A2E8-08002B30309D}"
[HKEY_CLASSES_ROOT\RubyFile\ShellEx\DropHandler]
#="{86C86720-42A0-1069-A2E8-08002B30309D}"
[HKEY_CLASSES_ROOT\RubyWFile\ShellEx\DropHandler]
#="{86C86720-42A0-1069-A2E8-08002B30309D}"
But it had to be the following for LONG filenames
Windows Registry Editor Version 5.00
[HKEY_CLASSES_ROOT\rbfile\ShellEx\DropHandler]
#="{60254CA5-953B-11CF-8C96-00AA00B8708C}"
[HKEY_CLASSES_ROOT\rbwfile\ShellEx\DropHandler]
#="{60254CA5-953B-11CF-8C96-00AA00B8708C}"
[HKEY_CLASSES_ROOT\RubyFile\ShellEx\DropHandler]
#="{60254CA5-953B-11CF-8C96-00AA00B8708C}"
[HKEY_CLASSES_ROOT\RubyWFile\ShellEx\DropHandler]
#="{60254CA5-953B-11CF-8C96-00AA00B8708C}"

Related

Refactoring my code so that file closes automatically once loaded, how does the syntax work?

My program loads a list from a file, and I'm trying to change the method so that it closes automatically.
I've looked at the Ruby documentation, the broad stackoverflow answer, and this guy's website, but the syntax is always different and doesn't mean much to me yet.
My original load:
def load_students(filename = "students.csv")
if filename == nil
filename = "students.csv"
elsif filename == ''
filename = "students.csv"
end
file = File.open(filename, "r")
file.readlines.each do |line|
name, cohort = line.chomp.split(",")
add_students(name).to_s
end
file.close
puts "List loaded from #{filename}."
end
My attempt to close automatically:
def load_students(filename = "students.csv")
if filename == nil
filename = "students.csv"
elsif filename == ''
filename = "students.csv"
end
open(filename, "r", &block)
line.each do |line|
name, cohort = line.chomp.split(",")
add_students(name).to_s
end
puts "List loaded from #{filename}."
end
I'm looking for the same result, but without having to manually close the file.
I don't think it'll be much different, so how does the syntax work for automatically closing with blocks?
File.open(filename, 'r') do |file|
file.readlines.each do |line|
name, cohort = line.chomp.split(",")
add_students(name).to_s
end
end
I’d refactor the whole code:
def load_students(filename = "students.csv")
filename = "students.csv" if filename.to_s.empty?
File.open(filename, "r") do |file|
file.readlines.each do |line|
add_students(line.chomp.split(",").first)
end
end
puts "List loaded from #{filename}."
end
Or, even better, as suggested by Kimmo Lehto in comments:
def load_students(filename = "students.csv")
filename = "students.csv" if filename.to_s.empty?
File.foreach(filename) do |line|
add_students(line.chomp.split(",").first)
end
puts "List loaded from #{filename}."
end

detect/set immutable on a file in Ruby

I am in need of a ruby way to detect if immutable is set on a file. If it is I need to remove it. Make my change to the file and then return it to immutability. I have looked at File and Fileutils, I have searched but all I can find with immutable and ruby is how to make ruby objects and threads immutable, which is of course a different thing then what I am looking for. I am trying to avoid using the shell, I want to stick to ruby code if at all possible. I am imagining code something like this:
file_name = '/boot/grub2/grub.cfg'
was_immutable = false
if File.immutable?(file_name)
FileUtils.chattr '-i', file_name
was_immutable = true
end
#my changes
if was_immutable
FileUtils.chattr '+i', file_name
end
This might seem like an over-simplification, but you can probably see where it is going:
file_name = '/boot/grub2/grub.cfg'
a = %x[lsattr #{file_name}].chomp
puts a if a[/i/]
Ok, so based on the link in the comments below, here is what I could come up with as a ruby version (if a guru around, happy to see how this should look):
#!/usr/bin/env ruby
require 'fcntl'
FS_IOC_GETFLAGS = 0x80086601
EXT3_IMMUTABLE_FL = 0x00000010
$count = 0
def check(filename)
fd = IO.sysopen(filename, Fcntl::O_RDONLY)
f = IO.new(fd)
a = [0].pack("L_")
f.ioctl(FS_IOC_GETFLAGS, a)
unless a.unpack('L_')[0] & EXT3_IMMUTABLE_FL == 0
puts "#{filename} is immutable :- a[0] = #{a[0].to_s}"
$count += 1
end
f.close
end
ARGV.each do |arg|
Dir[arg + '/*'].each do |item|
check(item) if File.file?(item)
end
end
exit(1) unless $count == 0

Why is Ruby's popen3 crashing because "Too many files open"?

I am using Popen3 to run some Perl scrips and then dump their output into a text file. Once in the text file, I search for the result of the Perl script. I get the error after running for about 40 minutes, which is about 220 files.
ruby/1.8/open3.rb:49:in `pipe': Too many open files (Errno::EMFILE)
from /ruby/1.8/open3.rb:49:in `popen3'
from ./RunAtfs.rb:9
from ./RunAtfs.rb:8:in `glob'
from ./RunAtfs.rb:8
The script is below.
require 'logger'
require 'open3'
atfFolder = ARGV[0]
testResult = ARGV[1]
res = "result.txt"
open('result.txt', 'w') { }
Dir.glob(atfFolder+'/*.pl') do |atfTest|
Open3.popen3("atf.pl -c run-config.pl -t #{atfTest}") do |i, o, e, t|
while line = e.gets
$testFile = testResult + line[/^[0-9]+$/].to_s + "testOutput.txt"
log = Logger.new($testFile)
log.info(line)
end
log.close
end
lastLine = `tail +1 #{$testFile}`
file = File.open(res, 'a')
if(lastLine.include? "(PASSED)")
file.puts("Test #{atfTest} --> Passed")
file.close
File.delete($testFile)
else
file.puts("Test #{atfTest} --> Failed!")
file.close
end
end
This script is processing 4900 Perl files so I don't know if that is just too many files for popen3 or I am not using it correctly.
Thanks for helping me!
I refactored my script after some very helpful pointers! The code is working great!
require 'open3'
atf_folder, test_result = ARGV[0, 2]
File.open('result.txt', 'w') do |file| end
Dir.glob("#{ atf_folder }/*.pl") do |atf_test|
test_file = atf_test[/\/\w+.\./][1..-2].to_s + ".txt"
comp_test_path = test_result + test_file
File.open(comp_test_path, 'w') do |file| end
Open3.popen3("atf.pl -c run-config.pl -t #{ atf_test }") do |i, o, e, t|
while line = e.gets
File.open(comp_test_path, 'a') do |file|
file.puts(line)
end
end
end
last_line = `tail +1 #{comp_test_path}`
File.open('result.txt', 'a') do |file|
output_str = if (last_line.include? "(PASSED)")
File.delete(comp_test_path)
"Passed"
else
"Failed!"
end
file.puts "Test #{ atf_test } --> #{ output_str }"
end
end
Consider this:
require 'logger'
require 'open3'
atf_folder, test_result = ARGV[0, 2]
Dir.glob("#{ atf_folder }/*.pl") do |atf_test|
Open3.popen3("atf.pl -c run-config.pl -t #{ atf_test }") do |i, o, e, t|
while line = e.gets
$testFile = test_result + line[/^[0-9]+$/].to_s + "testOutput.txt"
log = Logger.new($testFile)
log.info(line)
log.close
end
end
lastLine = `tail +1 #{ $testFile }`
File.open('result.txt', 'a') do |file|
output_str = if (lastLine.include? "(PASSED)")
File.delete($testFile)
"Passed"
else
"Failed!"
end
file.puts "Test #{ atf_test } --> #{ output_str }"
end
end
It's untested, of course, since there's no sample data, but it's written more idomatically for Ruby.
Things to note:
atf_folder, test_result = ARGV[0, 2] slices the ARGV array and uses parallel assignment to retrieve both parameters at once. You should test to see that you got values for them. And, as you move to more complex scripts, take advantage of the OptionParser class that comes in Ruby's STDLIB.
Ruby lets us pass a block to File.open, which automatically closes the file when the block exits. This is a major strength of Ruby and helps reduce errors like you're seeing. Logger doesn't do that, so extra care has to be take to avoid leaving hanging file-handles, like you're doing. Instead, use:
log = Logger.new($testFile)
log.info(line)
log.close
to immediately close the handle. You are doing it outside the loop, not inside it, so you had a bunch of open handles.
Also consider whether you need Logger, or if a regular File.open would suffice. Logger has additional overhead.
Your use of $testFile is questionable. $variables are globals, and their use is generally an indicator you're doing something wrong, at least until you understand why and when you should use them. I'd refactor the code using that.
In Ruby, variables and methods are in snake_case, not CamelCase, which is used for classes and modules. That doesn't seem like much until_you_run_into CodeDoingTheWrongThing and_have_to_read_it. (Notice how your brain bogged down deciphering the camel-case?)
In general, I question whether this is the fastest way to do what you want. I suspect you could write a shell script using grep or tail that would at least keep up, and maybe run faster. You might sit down with your sysadmin and do some brain-pickin'.

Toggling true/false: editing a file in ruby

I have some code that tries to change 'false' to 'true' in a ruby file, but it only works once while the script is running.
toggleto = true
text = File.read(filename)
text.gsub!("#{!toggleto}", "#{toggleto}")
File.open(filename, 'w+') {|file| file.write(text); file.close}
As far as I know, as long as I close a file, i should be able to read it it afterwards with what I previously wrote and thus change it back and forth no matter how many times.
Larger Context:
def toggleAutoAction
require "#{#require_path}/options"
filename = "#{#require_path}/options.rb"
writeToggle(filename, !OPTIONS[:auto])
0
end
def writeToggle(filename, toggleto)
text = File.read(filename)
text.gsub!(":auto => #{!toggleto}", ":auto => #{toggleto}")
File.open(filename, 'w+') {|file| file.write(text); file.close}
end
def exitOrMenu
puts "Are you done? (y/n)"
prompt
if gets.chomp == 'n'
whichAction
else
exit
end
end
def whichAction
if action == 5
toggleAutoAction
else
puts "Sorry, that isn't an option...returning"
return 1
end
exitOrMenu
end
The problem lays within this method:
def toggleAutoAction
require "#{#require_path}/options" # here
filename = "#{#require_path}/options.rb"
writeToggle(filename, !OPTIONS[:auto])
0
end
Ruby will not load the options.rb a second time (i.e. with the exact same path name), hence your !OPTIONS[:auto] will only be evaluated once (otherwise you would get a constant-already-defined-warning, provided OPTIONS is defined in options.rb). See Kernel#require docs.
You could, of course, do crazy stuff like
eval File.read("#{#require_path}/options.rb")
but I would not recommend that (performance wise).
As noted above, reading/writing from/to YAML files is less painful ;-)

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)

Resources