Take the 2-minute tour ×
Super User is a question and answer site for computer enthusiasts and power users. It's 100% free, no registration required.

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.

Bonus:
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
add comment

7 Answers 7

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
 [...]

 -n[format-string]

             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
1  
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
add comment
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
1  
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
4  
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
add comment

Here are a couple more different ways to manipulate filenames

for f in *.jpg
do
    mv "$f" "before_part${f%.*}after_part.${f##*.}"
    # OR mv "$f" "before_part$(basename "$f" ".jpg")after_part.jpg"
done
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
add comment

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
justin@eee:/tmp/q$ 
share|improve this answer
    
On some distros, this perl rename program is called prename. –  camh Jan 4 '10 at 9:56
add comment

There are no filename extensions in Linux.

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

Example:

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:

FILENAME='index.HTML'

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:

match='\.HTML'
replacement='.html'
echo ${FILENAME/%$match/$replacement}

Consult the bash docs for additional info.

share|improve this answer
1  
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
add comment

Here's another:

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

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.

#!/usr/bin/perl
#
# 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);

        ++$mon;
        $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
add comment

Your Answer

 
discard

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.