14

I have a group of files that have : (colon) within the name. I need to replace the : with - (dash).

Is there an easy way to do this in a script?

Sample FileName: 2013-10-11:11:52:08_055456663_045585_.txt

5 Answers 5

22

A simple 1-liner should do (assumes Posix sh-compatible shell):

for f in *:*; do mv -v -- "$f" "$(echo "$f" | tr ':' '-')"; done

Explanation:

  • for ... in ...; do ...; done is a loop

  • *:* matches all files and directories in the the current directory which have : in their name

  • f is assigned in turn to each such file name in the loop

  • mv renames its first argument to the second one; -v (verbose) asks it to print what it does; this option is GNU-utils specific, so it is available on Linux but not Solaris

  • $(...) executes the code in a sub-shell and substitutes the output

  • echo prints its argument to the standard output

  • tr reads standard output and translates the characters according to the supplied map

If you are using bash, you can avoid spawning an extra shell ($()) with sub-processes (tr) by replacing $(...) with ${f//:/-}.

6
  • 1
    +1 for a simple working answer, though it could do with a bit more explanation. E.g. the for f in ... loop is a loop which iterates over all files in the current directory where the files need to match *:*. (What happens with dirs which match?). The move command is a move oldname newname where the new name is generated by executing a shell with $( subshell here ). What tr does, ...
    – Hennes
    Oct 14, 2013 at 20:38
  • @Hennes: this is superuser - do I really need to go into these kinds of details? Oh well, here you go...
    – sds
    Oct 14, 2013 at 20:42
  • 1
    No. If there was a real need then I would not have +1'ed. And I understand what it does. I think it is nice for many other readers though.
    – Hennes
    Oct 14, 2013 at 20:43
  • 2
    You can replace the $(echo "$f" | tr ':' '-') with "${f//:/-}" and avoid the subshell, pipe and external program call. I think this is a bashism, but eh. See here.
    – evilsoup
    Oct 14, 2013 at 21:02
  • @evilsoup Indeed; it doesn't make much difference for small data sets (and one might favor the readability) but you'd be surprised how fast something like that adds up. I got bit by it recently; rewriting a script such that two simple string massaging commands were no longer necessary easily cut execution time by 90% or more, which on a large data set translated to several minutes of wallclock run time.
    – user
    Oct 15, 2013 at 13:16
7

As stated in another post by me the Perl-based rename tool (sometimes called prename, not to be confused with the Linux native rename tool) could do the trick for you. You just need to type

rename s/:/-/g <files to rename>

This replaces every colon with a dash in all files you name at the end, i. e. 2013-10-*. Remove the g to only replace the first colon.

Here's the link to my other Post

1

I'm sure a UNIX pro could do this with bash, but here's my quick and dirty version with ruby.

path_to_files = "/home/username/wrongnames/"
filenames = `ls #{path_to_files}`.split
filenames.each do |fn|
  `mv #{path_to_files + fn} #{path_to_files + fn.gsub(/:/, "-")}`
end

set path_to_files to the path to your misnamed files. save the above code in a file called rename.rb then:

username@machinename$ ruby rename.rb
3
  • @sds provides a much better answer. Should I delete my answer? I'm new to stackoverflow.
    – shupru
    Oct 14, 2013 at 20:43
  • 1
    No; it still has significant value. :) Oct 14, 2013 at 20:50
  • 2
    There are a few major downsides with this answer, however, that might be relevant and are definitely worth knowing about. (It might still be useful, but caveat emptor and all that.) Primarily, it looks like it'll break badly when it encounters file names with white space (not just the classic failure case newline) in them. Also, even if it wasn't for white space, you should quote the file name in the shell invocation to protect any other special characters in the file names from treatment by the shell (though I don't know exactly how Ruby's backtick operator works).
    – user
    Oct 15, 2013 at 13:19
1

If you have just one or a few files, this can do the renaming for you:

  1. Store the pattern of the file name you are targeting as a variable: p="201*".
  2. Store the old file name you want to rename: old_name=$(ls | grep $p).
  3. Store the new file name with necessary character replacements:

    new_name=$(ls | grep $p | sed 's/:/_/g')        # Using 'sed'
    
                OR
    
    new_name=$(ls | grep $p | tr ':' '_')           # Using 'tr'
    

Bonus clean up:
  a. If for uniformity's sake you want to replace the dashes (-) along with the colons (:) with underscores (_), you can do this:

    new_name=$(ls | grep $p | tr ':-' '_');

  b. If you want the last underscore (just before the .txt) gone as well, set new_name variable as:

    new_name=$(ls | grep $p | tr ':-' '_' | sed 's/_\./\./')
  1. Now rename your file as needed: mv $old_name $new_name

         NB: mv will fail if any of the file names in the renaming operation has spaces in it. In that case, wrap the appropriate variables in quotes, like:
mv "$old_name" $new_name OR mv $old_name "$new_name" OR mv "$old_name" "$new_name"


One-liners

1a: p="201*"; old_name=$(ls | grep $p); new_name=$(ls | grep $p | sed 's/:/_/g'); mv $old_name $new_name

1b: p="201*"; old_name=$(ls | grep $p); new_name=$(ls | grep $p | tr ':' '_'); mv $old_name $new_name

2: p="201*"; old_name=$(ls | grep $p); new_name=$(ls | grep $p | tr ':-' '_'); mv $old_name $new_name

3: p="201*"; old_name=$(ls | grep $p); new_name=$(ls | grep $p | tr ':-' '_' | sed 's/_\./\./'); mv $old_name $new_name
0

using renamer:

$ renamer --find ":" --replace "-" *
1
  • I think installing a complete node.js environment for just renaming files is a little bit too much. rename is a tool which is preinstalled on many linux distributions. But i think for Windows this tool may be great.
    – noggerl
    Oct 15, 2013 at 16:09

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.