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?
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems. It only takes a minute to sign up.
Sign up to join this communityTake 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?
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).
[[ $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
pre=${f%%.*}
ext=${f##*.}
mv "$f" "${pre##*- } - ${pre%% -*}.$ext"
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:
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.