Linux: rename file but keep extension?
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.
Related videos on Youtube
Anis
Updated on September 17, 2022Comments
-
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 over 14 yearsAny example, on these regular expressions?
-
Murray Furtado over 14 yearsThis is the answer to my question. (But the other answer gets me there faster&easier.)
-
Murray Furtado over 14 yearsI had not thought of jhead, only of an ugly combination of rename, stat, and cut. Thank you for the great answer!
-
Chris Johnsen over 14 yearsNo 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 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 over 14 yearsOn some distros, this perl rename program is called prename.
-
Gnoupi over 14 yearsIndeed, much better now, with examples. +1
-
Murray Furtado over 14 yearsI actually ended up using the FOR loop but not the MV example because I don't understand it :-)
-
alanning about 8 yearsThis 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 about 8 yearsNote: The meaning of those
f%
andf##
are described here, for instance: tldp.org/LDP/abs/html/parameter-substitution.html