Replace a word in a directory name and in the filenames in that directory
Solution 1
If your structure only has two levels, you don't need to use recursion.
Don't parse filenames with sed
. If you want to use regex and sed
style syntax to rename files, use rename
. If you're using Ubuntu 17.10, you need to install it
sudo apt install rename
Then you can use pa4080's answer.
With rename
, use the -n
option for testing.
rename -n 'expression' file(s)
You could also just use the mv
command. As a one-line command:
for d in ./*/; do mv -v "$d" "${d/Edition/Volume}"; done; for f in ./*/*; do mv -v "$f" "${f/Edition/Volume}"; done
You can use echo
for testing with mv
, ie echo mv -v source dest
, but it gives inaccurate results here, unless you test and then run the loops separately.
As a script:
#!/bin/bash
# rename directories first
for d in ./*/; do
mv -v "$d" "${d/Edition/Volume}"
done
# now rename files
for f in ./*/*; do
mv -v "$f" "${f/Edition/Volume}"
done
mv
and rename
recognise --
to indicate the end of options. Since all paths begin with ./
in this example, we do not need to use that, but if paths may begin with -
, then use --
at the end of options, eg rename -n -- 's/foo/bar/' *
, to prevent those paths being interpreted as options.
Solution 2
On multi- level directories, you can do it in one step, but the issue is that you need to make sure to rename the directories from bottom to top, to prevent the command or script to change the directory name before its content is changed.
In python, you can use:
os.walk
in combination with
topdown=False
In a script:
#!/usr/bin/env python3
import os
import shutil
import sys
for root, dirs, files in os.walk(sys.argv[1], topdown=False):
for f in files:
shutil.move(
root+"/"+f, root+"/"+f.replace("Edition", "Volume").strip()
)
for dr in dirs:
shutil.move(
root+"/"+dr, root+"/"+dr.replace("Edition", "Volume").strip()
)
Save the script as change_name.py
, run it with the directory as argument:
python3 /path/to/change_name.py <directory>
This works recursively on any number of levels.
Solution 3
You could use the command rename
two times to accomplish this task:
rename 's/Edition/Volume/' */ # rename the directories
rename 's/Edition/Volume/' */*.pdf # rename the PDF files inside
Here are two similar questions:
Solution 4
Use find
with the -depth
option combined with prename
:
find [DIRS...] -depth | prename -n 's|Edition(?=[^/]*$)|Volume|'
Explanation:
-depth
selects directory children before their parents. A tool “consuming” the selected path names for renaming can then rename children before their parents.s|Edition(?=[^/]*$)|Volume|
replaces the first occurrence ofEdition
in the path name withVolume
but only if the remainder doesn't contain a/
, i. e. it only applies to the last path name component (achieved by the positive look-ahead(?=[^/]*$)
).-n
tellsprename
to not actually rename the paths but to print how it would rename them. Remove this option to actually rename them.
Related videos on Youtube
Bishop
Updated on September 18, 2022Comments
-
Bishop over 1 year
I have a directory structure like this:
Bookname Edition 1 Bookname Edition 1 type1.pdf Bookname Edition 1 type2.pdf Bookname Edition 2 Bookname Edition 2 type1.pdf Bookname Edition 2 type2.pdf
I want to recursively change the name from
Edition
toVolume
of the directory and the filenames in those directories.I started off with this and that's fine if I'm in the directory:
for f in *.pdf; do a="$(echo $f | sed s/Edition/Volume/)" mv "$f" "$a" done
Then I tried this to change all files under the directories, and that's when I got stuck...
Please can you tell me how to do this or give me a better way of doing this. There are 15000 PDFs in 100 directories.
-
wjandrea over 6 yearsHow did you get stuck exactly? Based on that
for
loop, I would assume nothing happened, but I want to confirm. -
Bishop over 6 yearsThe script above changed all occurrences of Edition to Volume inside the directory. I want to do this from outside the directory as there are over 100 directories full of PDFs. I also want to change the directory name too from Edition to Volume.
-
wjandrea over 6 yearsOK, but how did you get stuck exactly?
-
-
Eric Duminil over 6 yearsYou could use
os.path.join
instead of concatenating strings with"/"
. -
Jacob Vlijm over 6 years@EricDuminil absolutely, but in this situation (script in answer), I used the less verbose option. There is no difference in functionality whatsoever. Thanks though.
-
Bishop over 6 yearsThanks for this - it changed the directory names but the files underneath havent been renamed.
-
Zanna over 6 years@Bishop well, it should. What exactly did you do?
-
Suleman Hasib almost 4 yearsThis worked for me, I just added to args to get the find and replace from the input.
root+"/"+f, root+"/"+f.replace(sys.argv[2], sys.argv[3]).strip()
-
Suleman Hasib almost 4 years@zanna 's solution worked for me until as you explained top-down happened.