Sebastian Zeki
Sebastian Zeki

Reputation: 6874

How to find and replace a list of filenames from a list

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

Upvotes: 1

Views: 226

Answers (3)

Cary Swoveland
Cary Swoveland

Reputation: 110725

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.

Upvotes: 1

Nabeel
Nabeel

Reputation: 2302

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

Upvotes: 1

tuo
tuo

Reputation: 586

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.

Upvotes: 2

Related Questions