40

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

is there a command that lets me specify renaming patterns?

migrated from stackoverflow.com Nov 13 '09 at 18:48

This question came from our site for professional and enthusiast programmers.

32

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
  • Could someone upload a Windows binary for the perl-based rename? – mcandre Nov 29 '12 at 21:51
  • The Perl version rename can't move files from one file system to another. – lilydjwg Oct 21 '13 at 14:19
  • bash one-liner is helpful, especially when having no rename function (ie busybox) – artm May 18 '18 at 2:48
10

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.

  • In Ubuntu and Debian (not sure about other distributions), /usr/bin/rename links to /usr/bin/prename by default, which takes regexps. – ℝaphink Dec 28 '09 at 22:07
5

G'day,

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
    This fails with files that contain whitespace in their name, so be careful. One should never parse ls output. – slhck Aug 23 '13 at 21:55
  • THe sed example is quite good, though. – bright-star Apr 8 '14 at 1:11
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

Install mmv, then do this:

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

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.