15

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

An Dorfer
  • 1,178
  • 2
  • 8
  • 14
Ducky
  • 159
  • 1
  • 1
  • 3

5 Answers5

23

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//:/-}.

Gilles 'SO- stop being evil'
  • 69,786
  • 21
  • 137
  • 178
sds
  • 1,960
  • 2
  • 22
  • 33
  • 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 '13 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 '13 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 '13 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](http://tldp.org/LDP/abs/html/string-manipulation.html#SUBSTRREPL00). – evilsoup Oct 14 '13 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 '13 at 13:16
  • Only this answer worked. The other methods did nothing or just hung up until I pressed Ctrl+C. (I use lubuntu) – Mark Jeronimus Sep 01 '15 at 06:30
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

Gilles 'SO- stop being evil'
  • 69,786
  • 21
  • 137
  • 178
noggerl
  • 1,349
  • 9
  • 10
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
Dut A.
  • 111
  • 3
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
shupru
  • 117
  • 2
  • 7
  • @sds provides a much better answer. Should I delete my answer? I'm new to stackoverflow. – shupru Oct 14 '13 at 20:43
  • 1
    No; it still has significant value. :) – Blacklight Shining Oct 14 '13 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 '13 at 13:19
0

using renamer:

$ renamer --find ":" --replace "-" *
Lloyd
  • 121
  • 3
  • 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 '13 at 16:09