0

Take a variable length filename 'abcdef - ghijkl.pdf' and rename it to 'ghijkl - abcdef.pdf'

How would I achieve that in a command line script for all files in a single folder?

1
  • If any of the existing answers solves your problem, please consider accepting it via the checkmark. Thank you!
    – Jeff Schaller
    Apr 13, 2017 at 23:25

4 Answers 4

4

Since you are using Ubuntu, you should have at least one of the perl-based rename or prename

$ rename -n -- 's/(\w*) - (\w*)/$2 - $1/' *.pdf
rename(abcdef - ghijkl.pdf, ghijkl - abcdef.pdf)

$ prename -n -- 's/(\w*) - (\w*)/$2 - $1/' *.pdf
abcdef - ghijkl.pdf renamed as ghijkl - abcdef.pdf

Alternatively, you could install and use mmv

$ mmv -n '* - *.pdf' '#2 - #1.pdf'
abcdef - ghijkl.pdf -> ghijkl - abcdef.pdf

(In all cases, remove the -n to actually complete the operation).

4

Using bash:

[[ $f =~ ^([^-]+)\ -\ ([^\.]+)\.(.*) ]] && 
  mv "$f" "${BASH_REMATCH[2]} - ${BASH_REMATCH[1]}.${BASH_REMATCH[3]}"

This uses bash's regex test operator =~ to see if the given filename (in $f) matches the pattern: "anything except dash, followed by space dash space, followed by anything except a period, followed by a period, followed by anything-or-nothing". If the filename matches that pattern, the matching bits of the parenthesized subsections of the regex get assigned to elements of the BASH_REMATCH array.

To do that for every file in a particular folder:

cd /to/that/folder
for f in *
do
  [ -f "$f" ] || continue
  [[ $f =~ ^([^-]+)\ -\ ([^\.]+)\.(.*) ]] && 
    mv "$f" "${BASH_REMATCH[2]} - ${BASH_REMATCH[1]}.${BASH_REMATCH[3]}"
done

Using only POSIX-specified variable expansions:

pre=${f%%.*}
ext=${f##*.}
mv "$f" "${pre##*- } - ${pre%% -*}.$ext"
0
0

You can create the script with that command:

\ls *-*.pdf | sed 's/\(.*\)-\(.*\).pdf/mv \1-\2.pdf \2-\1.pdf/' > script_mv.bash

Then you check that the script is correct, then you can execute it:

. ./script_mv.bash

I have written \ls, because it cancels options on ls.

The sed pattern means:

  • find anything1, a "-", anything2, ".pdf" .
  • Both anythings are enclosed in ( ) so that they get names \1 and \2 .
  • then I copy the mv line, for that I can recreate filenames thanks to \1 and \2 .
1
  • 1
    It's obvious this will fail due to the spaces in those filenames. Feb 5, 2017 at 22:15
0

bashy answer, specific to the "xxx - yyyy.ddd" name pattern..

find -type f | while read flnm ; do basename "$flnm" | awk -F'[\ .]' '{print $3 " " $2 " " $1 "."$4}'; done

Find a file and hand the name off to a while read loop. In there, use basename to extract the name and hand it off to Awk. Awk, using multiple field separators, space and period, then re-prints the fields, in the desired order.

abcdef - ghijkl.pdf

becomes

ghijkl - abcdef.pdf

practical application... rename files to this new pattern name

find -type f | while read flnm ; do mv "$(basename "$flnm")"  "$(basename "$flnm" | awk -F'[\ .]' '{print $3 " " $2 " " $1 "."$4}')"; done

The only difference is the inclusion of the mv command and the wrapping for the names as strings.

You must log in to answer this question.

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