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)
Related
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.
I have a directory filled with 5 files with no filetype (perhaps their filetype is '.txt' - I am uncertain), named "file1", "file2"...
I am trying to convert them to CSV format with the following code:
require('fileutils')
folder_path = "correct_folder_path"
Dir.foreach(folder_path) do |f|
next if f == '.' || f == '..'
#confirm inputs are correct (they are)
#p f
#p f+".csv"
File.rename(f, f+".csv")
end
I have p'd out f to confirm everything is working, but the line
File.rename(f,f+".csv")
is throwing the error: "in `rename': No such file or directory... (Errno::ENOENT)"
Does anyone know why this isn't working?
With Dir and File
You could change the directory to folder_path. If some files might have '.txt' extension, you need to remove the extension first in order not to get a .txt.csv file :
folder_path = "correct_folder_path"
Dir.chdir(folder_path) do
Dir.foreach(".") do |f|
next if File.directory?(f)
basename = File.basename(f, '.*')
new_file = basename + '.csv'
p f
p new_file
## Uncomment when you're sure f and new_file are correct :
# File.rename(f, new_file) unless f == new_file
end
end
With Pathname
With Pathname, it's usually much easier to filter and rename files :
require 'pathname'
folder_path = "correct_folder_path"
Pathname.new(folder_path).children.each do |f|
next if f.directory?
p f
p f.sub_ext('.csv')
## Uncomment if you're sure f and subext are correct :
# f.rename(f.sub_ext('.csv'))
end
The paths returned by Dir.foreach are relative to the folder_path that you passed in. Your call to File.rename tries to rename a file in the current working directory, which is probably not the same directory as that specified by folder_path.
You can make the rename succeed by prepending folder_path to the filename:
f = File.join(folder_path, f)
File.rename(f, f + ".csv")
One alternative:
require 'pathname'
folder.children.each do |child|
# Other logic here
child.rename(child.dirname + (child.basename.to_s + '.csv'))
end
How do you escape a colon, when renaming files in Ruby?
I have following code (names is a hash with data already filled in):
new_filename = ""
counter = 0
Dir.glob(folder_path + "/*").each do |f|
numbering = names.index(names.values.sort[counter])
new_filename = numbering + " - " + names.values.sort[counter]
puts "New file name: " + new_filename
File.rename(f, folder_path + "/" + new_filename + File.extname(f))
counter += 1
end
puts "Renaming complete."
The output of new_filename is correct, e.g. "Foo - Bar: Foo.txt". When it renames the file, the file has following format: "Foo - Bar/ Foo.txt".
I tried escaping with the colon with a backslash, but doesn't seem to work, because my output then looks like this: "Foo - Bar/\ Foo.txt".
Is is possible to have a colon in a string for renaming files?
FYI - in NTFS a colon identifies a separate stream of the same file... "Foo Bar: Foo.txt" identifies file "Foo Bar", stream " Foo.txt". Reference "Alternate Data Streams" (currently http://support.microsoft.com/kb/105763). AFIK this feature is not really widely used, though I have seen it used to tag files with thrid-party data (I use it to store a file's sha1 for dupe identification under the stream *:sha1).
Here's my .rb file:
puts "Renaming files..."
folder_path = "/home/papuccino1/Desktop/Test"
Dir.glob(folder_path + "/*").sort.each do |f|
filename = File.basename(f, File.extname(f))
File.rename(f, filename.capitalize + File.extname(f))
end
puts "Renaming complete."
The files are moved from their initial directory to where the .rb file is located. I'd like to rename the files on the spot, without moving them.
Any suggestions on what to do?
What about simply:
File.rename(f, folder_path + "/" + filename.capitalize + File.extname(f))
Doesn't the folder_path have to be part of the filename?
puts "Renaming files..."
folder_path = "/home/papuccino1/Desktop/Test/"
Dir.glob(folder_path + "*").sort.each do |f|
filename = File.basename(f, File.extname(f))
File.rename(f, folder_path + filename.capitalize + File.extname(f))
end
puts "Renaming complete."
edit: it appears Mat is giving the same answer as I, only in a slightly different way.
If you're running in the same location as the file you want to change
File.rename("test.txt", "hope.txt")
Though honestly, I sometimes I don't see the point in using ruby at all...no need probably so long as your filenames are simply interpreted in the shell:
`mv test.txt hope.txt`
If you are on a linux file system you could try mv #{filename} newname
You can also use File.rename(old,new)
Don't use this pattern unless you are ready to put proper quoting around filenames:
`mv test.txt hope.txt`
Indeed, suppose instead of "hope.txt" you have a file called "foo the bar.txt", the result will not be what you expect.
I am trying to write a script that takes 2 arguments : the first file contain some files extensions , and the second a directory .
My script move the files which their extension existed in the extension's file to the directory.
This is my script:
BEGIN{
}
{
file_ext = $1
folder = $2
isexist = "[ -e " $1 " ]"
if( ( system(isexist) ) != 0 )
{
getline < file_ext
system("find *." $0" -exec mv {} " folder " \;")
next
}
}
END{
}
But when I call the script in the shell , I am getting this error :
mv: missing destination file operand after `koko.cpp'
When I put folder name directly it works fine , but when I pass it by argument it doesn't work, why?
Command line arguments are accessed via the ARGV array:
BEGIN {
# "pop" the directory name off the arguments list
folder = ARGV[2]
ARGV[2] = ""
}
{
# then process the "file_ext" file
system("find . -name \\*." $0 " -exec mv {} " folder " \\;")
}