1

I have a CSV file made of two columns.

One column is the old filename, and the other is the new filename.

I want to replace the actual filenames in a folder with the new file names.

I have looked extensively but can't find out how to do this.

My input file is this:

    New Old
Dys.FSA_BB_NEW_0204_Sp_5_HBG_fq.gz.res  Cfda
Dys.FSC_Sp_BB_LC_0028_R1_30_HBG_fq.gz.res   Cyffa
Dys_BB_NEW_0177_Sp_FSD.5_HBG_fq.gz.res  Cyfsaff
Dys_FSE.BB_AM_0104_Sp_5_HBG_fq.gz.res   afffa
Dys_FSF.AM_0068_Sp_5_HBG_fq.gz.res  Cvdsd
Dys_FSG.BB_LC_261_Sp_15_HBG_fq.gz.res   vsvds0
Dys_FSH.BB_LC_0047_Sp_10_HBG_fq.gz.res  Cyto_vds.0
Dys_FastB_BB_LC_0028_Sp_15_HBG_fq.gz.res    Cvsv
Dys_FSA_OC_AH_255_E_B1_Biopsy_30_LBG_fq.gz.res  AneupvsvEFS
Dys_FSC_OC_UC_025_E_B1_Biopsy_25_LBG_fq.gz.res  vdvsupDysplasticBEFS
Dys_FSD_BB_POR_0028_Biopsy_30_HBG_fq.gz.res 24vdvdS
Dys_FSH_BB_NEW_0097_Biopsy_70_HBG_fq.gz.res Avdsgf3
Dys_FSI_BB_AM_0003_Biopsy_60_LBG_fq.gz.res  AnfdsfdsFS

I imagine I have to create an array of hashes first from the csv using something like this:

require 'csv'
csv_data = CSV.read '/Users/sebastianzeki/Desktop/tbb.csv'
headers = csv_data.shift.map {|i| i.to_s }
string_data = csv_data.map {|row| row.map {|cell| cell.to_s } }
array_of_hashes = string_data.map {|row| Hash[*headers.zip(row).flatten] }

This gives me:

 [{"New"=>"Dys.FSA_BB_NEW_0204_Sp_5_HBG_fq.gz.res", "Old"=>"Cfda"}, {"New"=>"Dys.FSC_Sp_BB_LC_0028_R1_30_HBG_fq.gz.res", "Old"=>"Cyffa"}, {"New"=>"Dys_BB_NEW_0177_Sp_FSD.5_HBG_fq.gz.res", "Old"=>"Cyfsaff"}, {"New"=>"Dys_FSE.BB_AM_0104_Sp_5_HBG_fq.gz.res", "Old"=>"afffa"}, {"New"=>"Dys_FSF.AM_0068_Sp_5_HBG_fq.gz.res", "Old"=>"Cvdsd"}, {"New"=>"Dys_FSG.BB_LC_261_Sp_15_HBG_fq.gz.res", "Old"=>"vsvds0"}, {"New"=>"Dys_FSH.BB_LC_0047_Sp_10_HBG_fq.gz.res", "Old"=>"Cyto_vds.0"}, {"New"=>"Dys_FastB_BB_LC_0028_Sp_15_HBG_fq.gz.res", "Old"=>"Cvsv"}, {"New"=>"Dys_FSA_OC_AH_255_E_B1_Biopsy_30_LBG_fq.gz.res", "Old"=>"AneupvsvEFS"}, {"New"=>"Dys_FSC_OC_UC_025_E_B1_Biopsy_25_LBG_fq.gz.res", "Old"=>"vdvsupDysplasticBEFS"}, {"New"=>"Dys_FSD_BB_POR_0028_Biopsy_30_HBG_fq.gz.res", "Old"=>"24vdvdS"}, {"New"=>"Dys_FSH_BB_NEW_0097_Biopsy_70_HBG_fq.gz.res", "Old"=>"Avdsgf3"}, {"New"=>"Dys_FSI_BB_AM_0003_Biopsy_60_LBG_fq.gz.res", "Old"=>"AnfdsfdsFS"}]

So how do I now convert the actual filename in a folder to the new one (in the same folder)?

edited using @tuo's answer

csv_lines = CSV.open('/Users/sebastianzeki/Desktop/tbb.csv',
                     headers: true,
                     col_sep: "\b")


filenames = Dir.glob("/Users/sebastianzeki/myfolder/*")

csv_lines.each do |row|
  old_name = row['Old']
  new_name = row['New']
  filenames.each do |filename|
  File.rename(old_name,new_name)
    end
end
Sebastian Zeki
  • 6,690
  • 11
  • 60
  • 125
  • 1
    Have you looked at [How to rename a file in Ruby](http://stackoverflow.com/questions/5530479/how-to-rename-a-file-in-ruby) ? What you mean by `convert the actual filename`? – Wand Maker Jan 01 '16 at 16:36
  • Yes I saw that. The problem is I want to try to convert the old filename with the new filename based on the hash I put in the question. The actual filename is the filename of the file (as opposed to just converting the old name in the hash to the new name in the hash). – Sebastian Zeki Jan 01 '16 at 16:43
  • What is the point of "edited using @tuo's answer" (which is not correct, incidentally, as it repeatedly renames `old_name` to `new_name` for each `row`)? Edits should only be to clarify the question, and sometimes to show what you've tried. – Cary Swoveland Jan 01 '16 at 18:42
  • So my edit is to show what I've tried. Not sure I see what the problem is.... – Sebastian Zeki Jan 01 '16 at 18:43
  • Are you saying you tried modifying @tuo's answer and it still doesn't work, or "here's the correct answer after modifying tuo's answer"? Note `File.rename` is not the same as tuo's. – Cary Swoveland Jan 01 '16 at 18:50
  • I expect you think the question is perfectly clear, but it is not. I don't understand it, so others will not either. The lack of interest by readers is also telling. From your statement of the problem, for a given folder, if the file "Cyto_Dys2.0" is present you want to rename it to "Dys.FSA_BB_NEW_0204_Sp_5_HBG_fq.gz.res". That's obviously not your intent, as there is no point to renaming the same file several more times. Also, it is unrelated to the "This gives me:" array. Moreover, I don't understand the difference between "old" and "actual" filenames, which I expect is important. (cont...) – Cary Swoveland Jan 01 '16 at 19:42
  • ... I suggest you walk through how, say, `{"New"=>"Dys.FSA_BB_NEW_0204_Sp_5_HBG_fq.gz.res", "Old"=>"Cgd"}` was obtained. I'm just the messenger, so don't shoot; we just want to help. – Cary Swoveland Jan 01 '16 at 19:43
  • Thank you for pointing out the fact that the input file was incorrect. I have now corrected it. – Sebastian Zeki Jan 02 '16 at 12:17

3 Answers3

2

You might want to load the CSV into CSV::Rows like this:

csv_lines = CSV.open(input_file_name,
                     headers: true,
                     col_sep: "\b")

This will give you all the csv rows with headers, you can iterate those lines like this:

path = '/your/dir/path/'

csv_lines.each do |row|
  old_name = row['Old']
  new_name = row['New']

  #TODO: find the file with the old name and update it to the new one 

  #EDIT: it can be done like this:
  File.rename(path + old_name, path + new_name)
end

I guess you already know how to do the rename job. :)

Edit: I added renaming into my code. No need to scan the folder in each loop. You just need to find one file and rename it at a time.

PS. You can add exception handler in the loop in case there is any missing file in your input file.

tuo
  • 586
  • 1
  • 4
  • 20
  • Hmmm. Actually its the rename job that I can't figure out. I've had a go at it above- is that likely to work? – Sebastian Zeki Jan 01 '16 at 17:10
  • Not sure I can figure out how to loop through the folder with the file names in it and find the old name? Hopefully that makes sense from my update code – Sebastian Zeki Jan 01 '16 at 17:12
1

I'm slightly confused as well by your input file, but assuming a CSV file with comma separation and no headers you could do this:

rename.csv (new_file,old_file)

foo_file_one,file_one
foo_file_two,file_two

Assuming rename.csv and your files to be renamed are in the same folder

require 'csv'

rename_list = CSV.parse(File.read('rename.csv'))

rename_list.each do |new, old|
  File.rename(old, new) rescue ''
end

However does this mean that your rename.csv can not have any spaces but uses commas

Nabeel
  • 2,272
  • 1
  • 11
  • 14
  • Thank you. Does this mean that the File.rename will see the old filename and then replace it with the corresponding new file name it is 'paired' with? – Sebastian Zeki Jan 02 '16 at 12:20
  • Yes, it will take the old name in the csv and replace it with the new name. If the file does not exist it will rescue it in this case (if you don't then it will give a SystemCallError if the file does not exist). – Nabeel Jan 02 '16 at 23:14
1

Suppose your file contains just these five line:

my_data = <<_ 
    New Old
Dys.FSA_BB_NEW_0204_Sp_5_HBG_fq.gz.res  Cfda
Dys.FSC_Sp_BB_LC_0028_R1_30_HBG_fq.gz.res   Cyffa
Dys_BB_NEW_0177_Sp_FSD.5_HBG_fq.gz.res  Cyfsaff
Dys_FSE.BB_AM_0104_Sp_5_HBG_fq.gz.res   afffa
_

Let's create the file

FName = "my_file.txt"

in an empty directory:

File.write(FName, my_data)
  #=> 201

Dir.entries(".")
  #=> [".", "..", "my_file.txt"] 

For testing, let's create (empty) files given by the array:

arr = ["Cfda", "Cyffa", "Cyfsaff"]

(but not "afffa") in the same directory:

arr.each { |name| File.write(name,'') }

Dir.entries(".")
  #=> [".", "..", "Cfda", "Cyffa", "Cyfsaff", "my_file.txt"] 

We can now read the file FName line-by-line into an array, throw away the header and rename files in the current directory with names given by "old name":

File.readlines(FName)[1..-1].each do |s|
  new, old = s.chomp.split
  File.rename(old, new) if File.exist?(old)
end

Dir.entries(".")
  #=> [".", "..", "Dys.FSA_BB_NEW_0204_Sp_5_HBG_fq.gz.res",
  #    "Dys.FSC_Sp_BB_LC_0028_R1_30_HBG_fq.gz.res",
  #    "Dys_BB_NEW_0177_Sp_FSD.5_HBG_fq.gz.res", "my_file.txt"]

You could use CSV class methods, but there is no need to do so.

If you want to do this in a directory that is not the current directory, either change the current directory or prepend the path to the filenames.

Cary Swoveland
  • 106,649
  • 6
  • 63
  • 100