Bash — Renaming Files

2019-08-31 07:13发布

问题:

I would like to write a script to rename a massive number of files by a few different rules. I need to remove a specific string from some, then rename others by regex (some of which will be the ones that I previously removed the string from), then rename others based on the numbers in their filenames.

In general, let's say I have multiple directories (hundreds) that all look generally like this:

1234-pic_STUFF&TOREMOVE_2000.tiff
1234-pic_STUFF&TOREMOVE_4000.tiff
1234-MORESTUFFTOREMOVE-012.jpg
1234-MORESTUFFTOREMOVE-037.jpg
1234-STUFF&TOREMOVE.pptx.pdf        (don't ask about this one)
1234-ET.jpg
1234-DF.jpg

To one that looks like:

1234_smallNum.tiff
1234_bigNum.tiff
1234_smallNum.jpg
1234_bigNum.jpg
1234_CaseReport.pdf
1234_ET.jpg
1234_DF.jpg

I already have shell scripts that use a perl script to rename by regex (I got it off SO, but I can't find it again to reference it). They are like remove_stuff_to_remove.sh and rename_case_reports.sh, and I can cd to each directory and do them individually by calling them with no input.

However, I don't know how to convert the filenames based on the numbers (2000 and 012 to smallNum; 4000 and 037 to bigNum; note these numbers vary greatly, so I can't go by a range or regex; I have to compare the numbers to each other.)

And I don't know how to automate the whole process, so that I can call one script from the root directory of all these directories and it will do all of these things for me. I understand regexes pretty well, but I am not doing so well with the find command, or with shell scripting in general.

Also, I say Bash, but really, if this could be better done in Java, C, Python, Ruby, or Lisp, I know those languages much better, and I just want a solution to this before I get these files dumped on me (in the next month or so)...

回答1:

Really - don't torture yourself with bash, just use your favorite scripting language. The following will give you some idea how to approach this in Ruby. Written hastily, so please don't laugh:

#!/usr/bin/env ruby

require 'find'

def move(path, old_name, new_suffix)
    number = old_name.sub(/^(\d+).*/,'\1')
    File.rename(path+'/'+old_name, path+'/'+number+'_'+new_suffix)
end

where_to_look = ARGV[0] || '.'
Find.find(where_to_look) do |dir|
    path = where_to_look+'/'+dir
    next if !File.directory?(path)
    entries = Dir.entries(path).select{|x| File.file?(path+'/'+x) }
    next if entries.size != 7

    %w(tiff jpg).each do |ext|
        imgs = entries.select{|x| x =~ /\d+\.#{ext}$/ }
        next if imgs.size != 2
        imgs.sort{|a,b| ai = $1.to_i if a =~ /(\d+)\.#{ext}$/ ; bi = $1.to_i if b =~ /(\d+)\.#{ext}$/ ; ai <=> bi }
        move(path, imgs.first, 'smallNum.'+ext)
        move(path, imgs.last, 'bigNum.'+ext)
    end
    report = entries.detect{|x| x =~ /\.pptx\.pdf$/ }
    move(path, report, 'CaseReport.pdf') if !report.nil?
    %w(ET DF).each do |code|
        file = entries.detect{|x| x =~ /^\d+-#{code}\.jpg$/ }
        move(path, file, code+'.jpg') if !file.nil?
    end
end


回答2:

Bash's string replacement:

$ match="foo"
$ repl="bar"
$ value="___foo___"
$ echo "${value/$match/$repl}"
___bar___

http://tldp.org/LDP/abs/html/string-manipulation.html

You can apply this pattern to each of your transformations.

$ for file in $(find . -name "*-pic_STUFF\&TOREMOVE_2000.tiff"); do
    mv "$file" "${file/-pic_STUFF\&TOREMOVE_2000.tiff/_smallNum.tiff}"; done