Linux: rename file but keep extension?

429

Solution 1

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.)

Solution 2

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$ 

Solution 3

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.

Solution 4

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

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 "Foo.bar.baZ.jpg":

echo "${f%.*}"

gives

Foo.bar.baZ

and

echo "${f##*.}"

gives

jpg

So the mv command, once expanded would look like:

mv "Foo.bar.baZ.jpg" "before_partFoo.bar.baZafter_part.jpg"

Solution 5

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:
429

Related videos on Youtube

Anis
Author by

Anis

Updated on September 17, 2022

Comments

  • Anis
    Anis over 1 year

    I've been dealing with this error message for a while now and nobody has been able to help me...

    I am trying to probe a java application using the standalone TPTP probekit agent with the following linux command:

    java '-agentlib:JPIBootLoader=JPIAgent:server=standalone;ProbekitAgent:ext-pk-BCILibraryName=BCIEngProbe,ext-pk-probescript=ome/anis/qf-t/core/probe/testProbe.probescript' junit.textui.TestRunner quickfix.test.acceptance.AcceptanceTestSuite

    My application is actually a junit testcase from the Quickfixj package (quickfixj.org)...

    Running the above command, the probing starts and I am able to see the runtime traces. However, at some point during execution I am getting the runtime exception:

    java.lang.NoClassDefFoundError: testProbe_probe$Probe_0

    25-Jan-2012 10:25:11 AM quickfix.test.acceptance.ATServer run
    SEVERE: error in AT server
    java.lang.NoClassDefFoundError: testProbe_probe$Probe_0
        at com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl.<init>(DocumentBuilderFactoryImpl.java)
        at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
        at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:39)
        at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27)
        at java.lang.reflect.Constructor.newInstance(Constructor.java:513)
        at java.lang.Class.newInstance0(Class.java:355)
        at java.lang.Class.newInstance(Class.java:308)
        at javax.xml.parsers.FactoryFinder.newInstance(FactoryFinder.java:147)
        at javax.xml.parsers.FactoryFinder.find(FactoryFinder.java:233)
        at javax.xml.parsers.DocumentBuilderFactory.newInstance(DocumentBuilderFactory.java:123)
        at quickfix.DataDictionary.load(DataDictionary.java:906)
        at quickfix.DataDictionary.read(DataDictionary.java:893)
        at quickfix.DataDictionary.<init>(DataDictionary.java:109)
        at quickfix.DefaultSessionFactory.getDataDictionary(DefaultSessionFactory.java:325)
        at quickfix.DefaultSessionFactory.createDataDictionary(DefaultSessionFactory.java:219)
        at quickfix.DefaultSessionFactory.processFixtDataDictionaries(DefaultSessionFactory.java:258)
        at quickfix.DefaultSessionFactory.create(DefaultSessionFactory.java:113)
        at quickfix.mina.acceptor.AbstractSocketAcceptor.createSessions(AbstractSocketAcceptor.java:242)
        at quickfix.mina.acceptor.AbstractSocketAcceptor.startAcceptingConnections(AbstractSocketAcceptor.java:99)
        at quickfix.SocketAcceptor.initialize(SocketAcceptor.java:66)
        at quickfix.SocketAcceptor.start(SocketAcceptor.java:59)
        at quickfix.test.acceptance.ATServer.run(ATServer.java:193)
        at java.lang.Thread.run(Thread.java:662)**
    

    which states that my probe file is missing. But this file has been needed in the first place to print the traces!!???.....

    Well, here are some facts:

    • I am able to probe other applications, including a simple junit test:

      java '-agentlib:JPIBootLoader=JPIAgent:server=standalone;ProbekitAgent:ext-pk-BCILibraryName=BCIEngProbe,ext-pk-probescript=ome/anis/qf-t/core/probe/testProbe.probescript' junit.textui.TestRunner quickfix.test.acceptance.AnisJUnitTestExample

    • I am able to run the quickfix.test.acceptance.AcceptanceTestSuite using junit (no probing):

      java junit.textui.TestRunner quickfix.test.acceptance.AcceptanceTestSuite

    • I have set the path to testProbe_probe$Probe_0 in CLASSPATH

    • I have called the System.getenv("CLASSPATH") from within my testcase and the CLASSPATH seems to be fine

    • My testcase deals with threads, sockets, etc

    And my system configuration is:

    -os: ubuntu 10.04-64bit (on VirtualBox!)
    
    -java version "1.6.0_24"
    
    -junit: junit-4.10.jar
    
    -TPTP agent controller: agntctrl.linux_em64t-TPTP-4.7.2
    

    Any ideas??

    Thanks, Anis

  • Gnoupi
    Gnoupi over 14 years
    Any example, on these regular expressions?
  • Murray Furtado
    Murray Furtado over 14 years
    This is the answer to my question. (But the other answer gets me there faster&easier.)
  • Murray Furtado
    Murray Furtado over 14 years
    I had not thought of jhead, only of an ugly combination of rename, stat, and cut. Thank you for the great answer!
  • Chris Johnsen
    Chris Johnsen over 14 years
    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.
  • Assaf Levy
    Assaf Levy over 14 years
    +1. This answer is very useful now (after the edit) – I had no idea you can do such string manipulation in Bash.
  • camh
    camh over 14 years
    On some distros, this perl rename program is called prename.
  • Gnoupi
    Gnoupi over 14 years
    Indeed, much better now, with examples. +1
  • Murray Furtado
    Murray Furtado over 14 years
    I actually ended up using the FOR loop but not the MV example because I don't understand it :-)
  • alanning
    alanning about 8 years
    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.
  • Murray Furtado
    Murray Furtado about 8 years
    Note: The meaning of those f% and f## are described here, for instance: tldp.org/LDP/abs/html/parameter-substitution.html