53

For example I want to mv (.*?).sql $1.php,

is there a command that lets me specify renaming patterns?

0

6 Answers 6

39

As others have mentioned, rename is good at this, but read the man page (man rename) before you try it. There are at least two entirely different tools out there called rename and which one you have will depend on your distribution. Calling them incorrectly can be dangerous.

Here's the man page for the perl-based version by Larry Wall that ships with Ubuntu. You give it a perl expression like rename 's/\.sql$/.php/' *.sql

Here's the man page for the rename that ships with older Red Hat and CentOS distributions. Usage is simple string substitution like rename .sql .php *.sql

You could also use a bash one-liner to process each file one at a time:

$ for f in *.sql; do mv -i "$f" "${f%%.*}.php"; done
3
  • Could someone upload a Windows binary for the perl-based rename?
    – mcandre
    Nov 29, 2012 at 21:51
  • The Perl version rename can't move files from one file system to another.
    – lilydjwg
    Oct 21, 2013 at 14:19
  • bash one-liner is helpful, especially when having no rename function (ie busybox)
    – artm
    May 18, 2018 at 2:48
12

There's rename(1), which doesn't use regexes, but can solve your problem:

rename .sql .php *.sql

There's also mmv(1), but I'm unfamiliar with how it works.

1
  • In Ubuntu and Debian (not sure about other distributions), /usr/bin/rename links to /usr/bin/prename by default, which takes regexps.
    – raphink
    Dec 28, 2009 at 22:07
7

You could also try entering

for i in $(\ls -d *.sql)
do
mv $i $(echo $i | sed -e 's/\.sql$/\.php/')
done

Or to make it use regex's change it slightly to

for i in $(\ls -d | egrep -e '.*\.sql')
do
mv $i $(echo $i | sed -e 's/\.sql$/\.php/')
done

for a bit of shell coding fun.

2
  • 2
    This fails with files that contain whitespace in their name, so be careful. One should never parse ls output.
    – slhck
    Aug 23, 2013 at 21:55
  • THe sed example is quite good, though. Apr 8, 2014 at 1:11
2

Install mmv, then do this:

mmv "*.sql" "#1.php"
2

Being a plumber, I like pipes :)

Note: this answer is verbose - as a newbee I appreciate it when someone who knows takes the time to explain, so I am paying 'forward'. If you are not a newbee, please excuse the length and verbosity.

ls -1 *_201[67][0-9]* | sed -e 's/\(\(.\+\)[-_]\(201[67][0-9]\{4\}\)\([^.]\+\)\?\.[0-9a-z]\{2,3\}\)/mkdir -p \2\/\3; mv \1 \2\/\3\/\1/' | bash

Where:

ls -1 *_201[67][0-9]*

lists all files matching the pattern, in this case I am looking for files with the date in the filename in the form 'YYYYMMDD'

sed -e 's/\(\(.\+\)[-_]\(201[67][0-9]\{4\}\)\([^.]\+\)\?\.[0-9a-z]\{2,3\}\)/mkdir -p \2\/\3; mv \1 \2\/\3\/\1/'
#        : | |    |     |                  | |       |                    |:                                 :
#        : |^2....^    ^3..................^^4.......^                    |:                                 :
#        :^1..............................................................^:                                 :
#        /................filename.................pattern................./...mkdir...and....mv...command.../

^1: 1st capture group - the entire filename

^2: 2nd capture group - the 1st part of the filename pattern

^3: 3rd capture group - the 2nd filename part - here, the date as 'YYYYMMDD'

^4: 4th capture group - optional filename part between the date and the extension, and the extension itself

Sooooo....if I have a file named

CallLog_555123412_20161231-214403.7z

...then

mkdir -p \2\/\3; mv \1 \2\/\3\/\1

actually means

# make directory and sub-directory
mkdir -p CallLog_555123412/20161231
# move file from current directory to sub-sub-directory just created
mv CallLog_555123412_20161231-214403.7z CallLog_555123412/20161231/CallLog_555123412_20161231-214403.7z

Note that the output of the sed command is piped through to 'bash' to execute it. In order to first see the proposed file moves WITHOUT actually moving them, first remove the '| bash' at the end, and try it like so:

ls -1 *_201[67][0-9]* | sed -e 's/\(\(.\+\)[-_]\(201[67][0-9]\{4\}\)\([^.]\+\)\?\.[0-9a-z]\{2,3\}\)/mkdir -p \2\/\3; mv \1 \2\/\3\/\1/'

This will not make any changes at all, but you will see the command that will be executed IF you run it with '| bash' tagged on the end.

This is useful because you can create any sub-directories you want from parts in the filename, or anything else you add to the 'replacement' text in the sed command. Note that sed requires a lot of escape characters, though.

SED Regex brackets:

(...) require escaping for capture grouping --> \(...\)

{m,n} require escaping for quantifying previous object --> \{m,n\}

/ requires escaping in the replacement string --> \/

[...] DO NOT require escaping for character classes

\1 in the replacement string refers to the 1st captured group, and so on...

SECURITY WARNING: NEVER PIPE ANYTHING TO 'bash' UNLESS YOU FULLY UNDERSTAND THE OUTCOME...

Hope that helps clear some blockages :)

1

Yes, mvregex does exactly this. https://github.com/nathanshearer/mvregex

I wrote it a while ago and use it frequently to fix problematic files. It is implemented in Bash so it is portable with minimal dependencies. It even works in cygwin which allows you to rename files with trailing spaces when the entire windows GUI, command line, and powershell will refuse to interact with said files.

You must log in to answer this question.

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