Rename a file inside a zip with ruby - ruby

I am trying to rename a single file from an zip archive using zip gem in Ruby.
zip_path = "C:/Downloads/"
zipfile_name = "C:/Downloads/02004 - 1850 W PINHOOK RD Lafayette LA.zip"
old_name = 'Reports.xls'
new_filename = 'Reports.csv'
Zip::ZipFile.open('C:/Downloads/02004 - 1850 W PINHOOK RD Lafayette LA.zip').each do |zipfile|
files = zipfile.select(&:file?)
file = files.find{|f| f.name == old_name}
zipfile.rename(file.name, new_filename) if file
end
end`
This is the code that I use and I get the the following error:
**NoMethodError: private method `select' called for #<Zip::Entry:0x00000000062ff098>
./features/step_definitions/rename_csv_definition.rb:21:in `block (2 levels) in <top (required)>'
./features/step_definitions/rename_csv_definition.rb:20:in `/^I rename the excel file to csv$/'**
Any suggestions?

Working example:
$ touch 1.csv 2.csv 3.csv
$ zip -r files.zip 1.csv 2.csv 3.csv
$ touch zip_rename.rb
# zip_rename.rb
require 'zip/zip'
zip_path = './'
zipfile_name = 'files.zip'
old_name = '1.csv'
new_filename = '777.csv'
Zip::ZipFile.open(zip_path + zipfile_name) do |zipfile|
if zipfile.find_entry(old_name)
zipfile.rename(old_name, new_filename)
end
end
Check archive content:
$ unzip -l files.zip
Archive: files.zip
Length Date Time Name
--------- ---------- ----- ----
0 03-18-2021 18:37 3.csv
0 03-18-2021 18:37 2.csv
0 03-18-2021 19:43 777.csv
--------- -------
0 3 files
Note, that rename method is avaliable for Zip::ZipFile object(zipfile) and take Zip::ZipEntry instance file name as argument
Here is the source
rubyzip Zip::File#rename

Related

Ruby LoadError with gmp

$ gem install gmp
Building native extensions. This could take a while...
Successfully installed gmp-0.7.43
Parsing documentation for gmp-0.7.43
Done installing documentation for gmp after 0 seconds
1 gem installed
$ cat gmp-test.rb
require 'gmp'
$ /opt/src/ruby-3.2.0/bin/ruby gmp-test.rb
<internal:/opt/src/ruby-3.2.0/lib/ruby/site_ruby/3.2.0/rubygems/core_ext/kernel_require.rb>:85:in `require': cannot load such file -- /home/dunham/.local/share/gem/ruby/3.2.0/gems/gmp-0.7.43/lib/../ext/gmp (LoadError)
from <internal:/opt/src/ruby-3.2.0/lib/ruby/site_ruby/3.2.0/rubygems/core_ext/kernel_require.rb>:85:in `require'
from /home/dunham/.local/share/gem/ruby/3.2.0/gems/gmp-0.7.43/lib/gmp.rb:9:in `<top (required)>'
from <internal:/opt/src/ruby-3.2.0/lib/ruby/site_ruby/3.2.0/rubygems/core_ext/kernel_require.rb>:159:in `require'
from <internal:/opt/src/ruby-3.2.0/lib/ruby/site_ruby/3.2.0/rubygems/core_ext/kernel_require.rb>:159:in `rescue in require'
from <internal:/opt/src/ruby-3.2.0/lib/ruby/site_ruby/3.2.0/rubygems/core_ext/kernel_require.rb>:149:in `require'
from gmp-test.rb:1:in `<main>'
<internal:/opt/src/ruby-3.2.0/lib/ruby/site_ruby/3.2.0/rubygems/core_ext/kernel_require.rb>:85:in `require': cannot load such file -- gmp (LoadError)
from <internal:/opt/src/ruby-3.2.0/lib/ruby/site_ruby/3.2.0/rubygems/core_ext/kernel_require.rb>:85:in `require'
from gmp-test.rb:1:in `<main>'
Library seems to be dead. It's expecting gmp.so to be in ext directory but it ends up in lib directory, it's probably new rubygems doing things differently.
>> require "gmp"
<internal:/home/alex/.rbenv/versions/3.2.0/lib/ruby/3.2.0/rubygems/core_ext/kernel_require.rb>:85:in 'require':
cannot load such file -- /home/alex/.rbenv/versions/3.2.0/lib/ruby/gems/3.2.0/gems/gmp-0.7.43/lib/../ext/gmp (LoadError)
$ ls $(dirname $(gem which gmp))
gmp.rb gmp.so
# move ^ that to ../ext/
$ mv $(dirname $(gem which gmp))/gmp.so $(dirname $(gem which gmp))/../ext/
>> require "gmp"
=> true
>> GMP::Z(0)
=> 0
Update
From the linked logs looks like you're still not loading gmp.so which is where all the classes are defined. You can copy gmp.so into your app:
# copy `gmp.rb`
$ cp $(gem which gmp) .
# copy `gmp.so`
$ cp $(dirname $(gem which gmp))/gmp.so .
# or if you moved it to ext
$ cp $(dirname $(gem which gmp))/../ext/gmp.so .
$ touch app.rb
$ ls
app.rb gmp.rb gmp.so
gmp.rb defines GMP.sprintf method, if this is not a required method and you don't use it, you can remove this file.
# gmp.rb
# require 'rbconfig'
#
# ENV['PATH'] = [File.expand_path(
# File.join(File.dirname(__FILE__), "..", "ext")
# ), ENV['PATH']].compact.join(';') if RbConfig::CONFIG['host_os'] =~ /(mswin|mingw|mingw32)/i
#
# require File.dirname(__FILE__) + '/../ext/gmp'
# unless RUBY_VERSION =~ /^1.8/
module GMP
def self.sprintf(format, *args)
first_pct = format.index '%'
result = format[0...first_pct]
#format.gsub(/(?<!%)%[0#+ ']*[0-9]*.?[0-9]*[a-zA-Z][^%]*/) do |fragment|
format.gsub(Regexp.new('(?<!%)%[0#+ \']*[0-9]*.?[0-9]*[a-zA-Z][^%]*')) do |fragment|
arg = args.shift
if fragment =~ /%[0#+ ']*[0-9]*.?[0-9]*[ZQF]/
result << sprintf2(fragment, arg)
elsif fragment =~ /%[0#+ ']*[0-9]*.?[0-9]*[PR]/ && GMP.const_defined?(:MPFR_VERSION)
result << GMP::F.sprintf2(fragment, arg)
else
result << (fragment % arg)
end
end
result
end
end
# end
# app.rb
require_relative "gmp.so"
p GMP::Z
p GMP::Z(0)
p GMP::Q
p GMP::F
require_relative "gmp.rb"
p GMP.sprintf "%Zd", GMP.Z(0)
$ ruby --yjit -v
ruby 3.2.0 (2022-12-25 revision a528908271) +YJIT [x86_64-linux]
$ ruby --yjit app.rb
GMP::Z
0
GMP::Q
GMP::F
"0"

Rubyzip fail to add a file with a same name from another folder [Zip::EntryExistsError]

Using the following test tree folder for example:
- test1
- folder2
- test1 # This is the file rubyzip will break on.
- test2
And copied this code from here:
path = File.expand_path path
archive = File.join(__dir__, File.basename(path)) + '.zip'
FileUtils.rm archive, force: true
Zip::File.open(archive, Zip::File::CREATE) do | zipfile |
Dir["#{path}/**/**"].reject{|f|f==archive}.each do | item |
basename = File.basename(item)
zipfile.add(basename, item)
end
end
It fails because there is two files having the same name even if their are not in the same directory (test1 in my example).
Is there something I am missing ?
Thanks to #simonoff (here), I shouldn't use the basename but the full relative path so Rubyzip could make the difference between test1 and folder2/test1.
Here is a code fixing it:
basename = File.basename path
dirname = File.dirname path
internal_path = path.sub %r[^#{__dir__}/], ''
archive = File.join(dirname, basename) + '.zip'
FileUtils.rm archive, force: true
Zip::File.open(archive, Zip::File::CREATE) do | zipfile |
Dir["#{internal_path}/**/**"].map{|e|e.sub %r[^#{internal_path}/],''}.reject{|f|f==archive}.each do | item |
zipfile.add(item, File.join(internal_path, item))
end
end
There is certainly a much cleaner way to do it.

Ruby: Ignore periods in directories when searching all directories for files?

I created a find script to search for text in files in all directories from which the script is ran.
print "Enter text to search: "
input = gets.chomp
search = Regexp.escape(input)
Dir.glob("**/*.*").each do |file|
data = File.read(file)
if data.match(search)
puts "Text Found: " + __dir__ + "/" + file
end
end
It works but I get this error as the directory I am searching in contains directory names with periods..
find.rb:7:in `read': Is a directory # rb_sysopen - cramws/cram_ws.egg-info (Errno::EISDIR)
from find.rb:7:in `block in <main>'
from find.rb:6:in `each'
from find.rb:6:in `<main>'
How can I ingore these?
For a quick fix, you can add:
next if File.directory?(file)
at the beginning of your block.

Error unzipping a file in cmd (using 7z in Windows)

I am attempting to use 7 Zip through the command line. As you can see below, using the command 7z l lists the 3 files in the target zip file.
C:\Users\User1\Downloads>7z l recording_20130731180507.zip
--
Path = recording_20130731180507.zip
Type = zip
Physical Size = 311686
Date Time Attr Size Compressed Name
------------------- ----- ------------ ------------ ------------------------
2013-07-31 18:05:06 ..... 655 655 SD_DISK\20130731\18\2013073
1_180505_A4BC_00408CC2B40B\recording.xml
2013-07-31 18:05:06 ..... 309752 309752 SD_DISK\20130731\18\2013073
1_180505_A4BC_00408CC2B40B\20130731_18\20130731_180505_59EB_00408CC2B40B.mkv
2013-07-31 18:05:06 ..... 279 279 SD_DISK\20130731\18\2013073
1_180505_A4BC_00408CC2B40B\20130731_18\20130731_180505_59EB_00408CC2B40B.xml
------------------- ----- ------------ ------------ ------------------------
310686 310686 3 files, 0 folders
However, when I attempt to actually unzip the file, I get a "no files to process error". I've never tried unzipping from cmd before. Do I have to try to dig into the zip file to extract those 3 files?
C:\Users\User1\Downloads>7z e recording_20130731180507.zip o-C:\users\User1\do
cuments\folder1\test
No files to process
Files: 0
Size: 0
Compressed: 311686
The option is -o, not o-. Run the command like this:
7z e recording_20130731180507.zip -o"C:\users\User1\documents\folder1\test"

renaming files using variables in Ruby

How do you use variables to rename files in Ruby?
File.rename("text1.txt", "text2.txt")
The above example is fine when using irb, but I writing a script where both var1 and var2 are unknown to me.
for example:
script_dir = File.expand_path File.dirname(__FILE__)
Dir.chdir(script_dir)
Dir.glob('Cancer1-1.pencast').each do |pencast|
pencast_title = File.basename(File.basename(pencast), '.*')
i = 1
audio_title = File.basename(`unzip -l #{pencast} | grep .aac | awk '{print $4;}' | awk 'NR=='#{i}''`)
audio_path = `unzip -l #{pencast} | grep .aac | awk '{print $4;}' | awk 'NR=='#{i}''`
audio_extension = File.extname(File.basename(audio_path))
new_name = "#{pencast_title}-#{i}#{audio_extension}"
File.rename(audio_title, new_name)
does not work...
but if i use puts var1 I see the file name I want.
The error I get is:
prog_test.rb:12:in `rename': No such file or directory - audio-0.aac (Errno::ENOENT)
or Cancer1-1-1.aac
from prog_test.rb:12
from prog_test.rb:5:in `each'
from prog_test.rb:5
but the file audio-0.aac is there... I'm looking at it.
I am certain I have located the problem:
it seems to be adding a variable to another variable. This is a simplified example that produces the same output:
audio_title = "audio-0.aac"
fullPath = File::SEPARATOR + "Users" + File::SEPARATOR + "name" + File::SEPARATOR + "Desktop" + File::SEPARATOR + audio_title
newname = File::SEPARATOR + "Users" + File::SEPARATOR + "name" + File::SEPARATOR + "Desktop" + File::SEPARATOR + "audio1.aac"
puts fullPath
puts newname
File.rename(fullPath, newname)
OUTPUT :
/Users/name/Desktop/audio-0.aac
/Users/name/Desktop/audio1.aac
prog_test.rb:22:in `rename': No such file or directory - /Users/name/Desktop/audio-0.aac or /Users/name/Desktop/audio1.aac (Errno::ENOENT)
from prog_test.rb:22
You should be passing the full file path to File.rename, not just the basename
I am not sure what is going on in your example inside File.basename() , but imagine the following:
fullPath = "C:" + File::SEPARATOR + "Folder" + File::SEPARATOR + "File.txt" # C:\Folder\File.txt
basename = File.basename(fullPath) # File
newFileName = "File.bak"
File.rename(basename, newFileName)
# How can Ruby possibly know which directory to find the above file in, or where to put it? - It will just look in the current working directory
So instead, you need to pass the full path to File.rename, like so:
fullPath = "C:" + File::SEPARATOR + "Folder" + File::SEPARATOR + "File.txt" # C:\Folder\File.txt
directory = File.dirname(fullPath) # C:\Folder
newFileName = "File.bak"
File.rename(fullPath, directory + File::SEPARATOR + newFileName)

Resources