2

So I have a script that adds 2 films together using the audio from the $1.audio file. What I would like to do is rename any file in the directory with:

*.mp4 

To:

*.audio 

Keeping original file name.

0

5 Answers 5

7

You can use the rename command. It's not portable, but it exists in different forms in different distributions.

In CentOS/RHEL and probably Fedora:

rename .mp4 .audio *.mp4

Should do it. From man rename on CentOS 6:

SYNOPSIS
       rename from to file...
       rename -V

DESCRIPTION
       rename  will  rename  the specified files by replacing the first occur-
       rence of from in their name by to.

In Ubuntu and probably any Debian variant:

rename 's/\.mp4$/.audio/' *.mp4

should do it. From man rename on Ubuntu 14.04:

SYNOPSIS
       rename [ -v ] [ -n ] [ -f ] perlexpr [ files ]

DESCRIPTION
       "rename" renames the filenames supplied according to the rule specified
       as the first argument.  The perlexpr argument is a Perl expression
       which is expected to modify the $_ string in Perl for at least some of
       the filenames specified.  If a given filename is not modified by the
       expression, it will not be renamed.  If no filenames are given on the
       command line, filenames will be read via standard input.

       For example, to rename all files matching "*.bak" to strip the
       extension, you might say

               rename 's/\.bak$//' *.bak
3
  • This is the shortest working code which I prefer, love the dual OS answer's... seperate thread really, but what does "portable" mean Oct 28, 2015 at 1:08
  • "Portable" means a command/tool would work the same on various distributions or various OSes. Portability isn't actually a simple yes/no; something can be more portable or less portable because of the wide variety of Linux distributions available, not to mention other OSes. rename differs widely from distribution to distribution even for simple use cases, so I said it's "not portable". find, on the other hand (see jilliagre's answer), is very portable as it will work in any Linux distribution you're likely to ever encounter.
    – Wildcard
    Oct 28, 2015 at 4:53
  • Indeed, and not only any Linux distributions but on any Unix and Unix like distribution too.
    – jlliagre
    Oct 28, 2015 at 7:47
4

Here is a fast and portable solution still handling oddly named files :

find . -name "*.mp4" -exec sh -c 'for i do mv -- "$i" "${i%.mp4}.audio"; done' sh {} +
2
  • Portable means that will work on any Unix/Unix like distribution. The answer you accepted rely on a command that is not standardized, that has a different syntax depending on the implementations, and that might be or is missing on many Unix distributions (freeBSD, OpenBSD, Solaris, HP-UX, AIX) and probably in some Linux ones.
    – jlliagre
    Oct 28, 2015 at 5:02
  • Your answer is definitely the best portable solution. +1. :) If it's for a personal script (like I suspect from the question), it will only need to be run on one distribution, and rename is definitely more readable.
    – Wildcard
    Oct 28, 2015 at 8:10
2

Use this for loop:

for f in *; do
  [ -f "$f" ] && mv -v -- "$f" "${f%.mp3}.audio"
done
  • for i in * loops trough all files and directories (except dot-files) in the current working directory and stores the current processed file in $f
    • [ -f "$f" ] checks if it's a regular file
    • mv -v renames the file (-- is that the filenames will not be interpreted as arguments by mistake)
    • ${f%.mp3}.audio removes the .mp3 extension and adds the .audio extension (Parameter Expansion)
1
  • That's actually very clean as well as portable. It's readable enough, for those who know shell scripting fairly well. I think I would use this myself even in preference to my accepted answer of using rename.
    – Wildcard
    Nov 27, 2015 at 8:07
0

Here is a solution that uses find, sed, and xargs. This solution works even if there are spaces in the name.

It first gets the files using find. Then, gets the base name of the file using sed. Finally, it does the move to change the extension.

Note the code below is multi-line for clarity. You should probably execute it in one line.

# split into three lines for clarity
# should execute as one line
find . -maxdepth 1 -name '*.mp4'
| sed -r 's/.*\/([^/]+)\.mp4/\1/g'
| xargs -ix mv "x.mp4" "x.audio"

Below is an alternative solution using basename. This solution may work with names containing spaces (have not tested this one).

It first gets all the files with .mp4 extension, then gets the base name, and finally renames each with the audio extension.

# split into three lines for clarity
# should execute as one line
find . -maxdepth 1 -name '*.mp4' -print0
| xargs -0 -ix basename "x" .mp4
| xargs -ix mv "x.mp4" "x.audio"
-1

You can use

for file in `ls *.mp4`; { mv $file `echo $file | sed 's/.mp4/.audio/g'`; }
5
  • 2
    This will fail given any special characters in filenames—even a space will cause it to fail, to say nothing of newlines or other trickery. See mywiki.wooledge.org/ParsingLs on why you shouldn't parse the output of ls.
    – Wildcard
    Oct 27, 2015 at 5:01
  • 3
    What kind of undocumented for-loop construct is that? It doesn't work in dash, but works in bash and zsh. I vaguely remember seeing this syntax before, but it's been literally decades ago. Is it a ksh thing? It's really kind of neat, substitute { for do, and } for done.
    – RobertL
    Oct 27, 2015 at 6:08
  • I think its short form of for..do..done. It works in bash, and I didn't test it on any other shells.
    – Soheil
    Oct 27, 2015 at 6:12
  • @Soheil - Yes that's what I said. Where did you learn it?
    – RobertL
    Oct 27, 2015 at 6:47
  • @RobertL From my colleagues.
    – Soheil
    Oct 27, 2015 at 6:57

Not the answer you're looking for? Browse other questions tagged .