Super User is a question and answer site for computer enthusiasts and power users. It's 100% free, no registration required.

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

In Windows/DOS, I can say rename myfile.* yourfile.* to change the name but keep the extension. How is that accomplished on Linux?

The man page only suggests how to change the extension, but that's the opposite of what I want.

I actually want to put a photo's creation date into its filename, to get something like 20091231 2359 New Year.jpg. I'm afraid that I need some non-trivial combination of commands to achieve that?

share|improve this question
up vote 12 down vote accepted

Here's an answer for the bonus question.

I actually want to put a photo's creation date into its filename, to get something like 20091231 2359 New Year.jpg. I'm afraid that I need some non-trivial combination of commands to achieve that?

Assuming you want to take the photo's creation date from the EXIF data, you'll need a separate tool for that. Luckily it turns out that jhead offers a trivial way to do exactly what you want, with its -n option.

$ jhead -h


             Rename files according to date.  Uses exif date if present, file
             date otherwise.  If the optional format-string is not supplied,
             the format is mmdd-hhmmss.  If a format-string is given, it is
             is passed to the 'strftime' function for formatting
             In addition to strftime format codes:
             '%f' as part of the string will include the original file name

Here's an example:

$ jhead -n%Y-%m-%d-%f New_year.jpg   
New_year.jpg --> 2009-12-31-New_year.jpg

Edit: Of course, to do this for a bunch of photos, it'd be something like:

$ for i in *jpg; do jhead -n%Y-%m-%d-%f $i; done

To tweak the date formatting to your liking, take a look at the output of date --help, for example; it will list the available format codes.

(jhead is widely available for different systems. If you are e.g. on Ubuntu or Debian, simply type sudo apt-get install jhead to install it.)

share|improve this answer
I had not thought of jhead, only of an ugly combination of rename, stat, and cut. Thank you for the great answer! – Torben Gundtofte-Bruun Jan 1 '10 at 13:56
betelgeuse:tmp james$ ls myfile.* yourfile.*
ls: yourfile.*: No such file or directory   
myfile.a    myfile.b
betelgeuse:tmp james$ for file
> in myfile.*
> do
> mv "${file}" "`echo $file | sed 's/myfile\./yourfile./'`"
> done
betelgeuse:tmp james$ ls myfile.* yourfile.*
ls: myfile.*: No such file or directory
yourfile.a  yourfile.b

The key is that, if you've seen an example which shows how to munge one part of the filename with a regex, that's the only example you need. Extensions have no special status on unix filesystems - they're just a part of the filename that happens to be after a . character.

share|improve this answer
This is the answer to my question. (But the other answer gets me there faster&easier.) – Torben Gundtofte-Bruun Jan 1 '10 at 13:55
No need to fork+exec sed: mv "$file" yourfile"${file#myfile}". Works on any modern Bourne-like shell (or any POSIX shell), but probably not the actual Bourne shell. – Chris Johnsen Jan 1 '10 at 18:03

For just the renaming part, the 'rename' program will work. It's the same as the example you saw in the man page, just switched around.

justin@eee:/tmp/q$ touch myfile.{a,b,c,d}
justin@eee:/tmp/q$ ls
myfile.a  myfile.b  myfile.c  myfile.d
justin@eee:/tmp/q$ rename -v s/myfile/yourfile/ myfile.*
myfile.a renamed as yourfile.a
myfile.b renamed as yourfile.b
myfile.c renamed as yourfile.c
myfile.d renamed as yourfile.d
justin@eee:/tmp/q$ ls
yourfile.a  yourfile.b  yourfile.c  yourfile.d
share|improve this answer
On some distros, this perl rename program is called prename. – camh Jan 4 '10 at 9:56
This is really useful! I was surprised by how many of these types of questions I had to search through before finding "rename". Thank you. – alanning Feb 9 at 17:09

Here are a couple more different ways to manipulate filenames

for f in *.jpg
    mv "$f" "before_part${f%.*}after_part.${f##*.}"
    # OR mv "$f" "before_part$(basename "$f" ".jpg")after_part.jpg"

The parameter expansions in the mv command work as follows:

${f%.*} - Delete the shortest matching pattern from the end of the string contained in $f, in this case delete everything after and including the last dot. The single % means "shortest from the end".

${f##*.} - Delete the longest matching pattern from the beginning of the string contained in $f, in this case everything before and including the last dot (this includes any other dots as well). The double # (##) means "longest from the beginning".

So, for example, if $f contains "":

echo "${f%.*}"



echo "${f##*.}"



So the mv command, once expanded would look like:

mv "" ""
share|improve this answer
I actually ended up using the FOR loop but not the MV example because I don't understand it :-) – Torben Gundtofte-Bruun Jan 26 '10 at 22:00
Note: The meaning of those f% and f## are described here, for instance: – Torben Gundtofte-Bruun Feb 10 at 9:02

There are no filename extensions in Linux.

Use regular expressions to cut particular substrings from the filename and access them.


Real-life scenario: you are extracting html from a chm file. Filenames in Windows are case-insensitive, so in Linux you'll get broken links. You have a file named index.HTML, but href="index.html" in URLs. So your goal is to adapt filenames to match links to them.

Assume you have the filename in a variable:


Starting with version 3.0 bash supports regular expressions itself, so you don't need any additional tools like grep/sed/perl etc to perform string manipulation. The following example illustrates the replacement of a back-end match in a string:

echo ${FILENAME/%\.HTML/.html}

The match and replacement strings can be parametrized if you wish, this provides additional flexibility when writing script. The following code snippet achieves the same goal:

echo ${FILENAME/%$match/$replacement}

Consult the bash docs for additional info.

share|improve this answer
Any example, on these regular expressions? – Gnoupi Jan 1 '10 at 10:21
+1. This answer is very useful now (after the edit) – I had no idea you can do such string manipulation in Bash. – Jonik Jan 2 '10 at 17:04
Indeed, much better now, with examples. +1 – Gnoupi Jan 6 '10 at 8:25

Here's another:

find -name "*.jpg" -printf '"%p" "%h/%TY%Tm%Td %TH%TM %f"\n' | while read -r f
    eval "mv ${f}"
share|improve this answer

There's always more than one way to do it. I put the following script as /usr/local/bin/mrename.

Then in the script containing the photo files, just type: mrename

There's also an optional commented-out feature in the script to scale the photos (using ImageMagick).

Hope this is useful for some folks.

# mrename files
use strict;

# if no 2 args, use defaults
my $dir = ".";

# read in path from command line
$dir = $ARGV[0] if ( defined( $ARGV[0] ) && $ARGV[0] ne "" );

# read in directory contents
opendir( DIR, $dir );
my @files = readdir( DIR );
closedir( DIR );

# rename and/or scale each file in directory
my $number_of_files = scalar( @files );
my $curfile = 0;

foreach my $file( @files ) {
    # only rename and scale jpg/gif files
    if ( $file =~ /\w+\.(jpg)$/ ) {
        my $extension = $1;
        $extension =~ tr/A-Z/a-z/;
        my $full_filename = "$dir/$file";

        # get stats on file- specifically the last modified time
        (my $dev,my $ino,my $mode,my $nlink,my $uid,my $gid,my $rdev,my $size,
        my $atime,my $mtime,my $ctime,my $blksize,my $blocks) = stat($full_filename);

        # convert last-modified time from seconds to practical datetime terms
        (my $sec,my $min,my $hour,my $mday,my $mon,my $year,my $wday,my $yday,
        my $isdst) = localtime($mtime);

        $year += 1900;

        my $filecdate = sprintf( "m%04i%02i%02i_%02i%02i%02i.$extension", $year, $mon, $mday, $hour, $min, $sec );
        my $full_newfilename = "$dir/$filecdate";

        # to scale files, use imagemagick by using the command below instead of mv 
        #my $cmd = "convert $full_filename -resize $scale% $full_newfilename";
        my $cmd = "mv $full_filename $full_newfilename";
        system( $cmd );

        # update percentage done
        my $percent_done = sprintf( "%5.2lf", 100* (++$curfile) / $number_of_files );
        print "\r$percent_done%";
print "\n";
share|improve this answer

Your Answer


By posting your answer, you agree to the privacy policy and terms of service.

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