I am trying to make a bash script that should replace any occurrence of a given pattern
with an other, given, expression
in any path in a given directory. For instance, if I have the following tree structure:
.
|- file1
|- file-pattern-pattern.html
|- directory-pattern/
| |- another-pattern
| \- pattern.pattern
\- other-pattern/
\- a-file-pattern
it should end up looking like
.
|- file1
|- file-expression-expression.html
|- directory-expression/
| |- another-expression
| \- expression.expression
\- other-expression/
\- a-file-expression
The main issue I have is that most solution I have found make either usage of the **
glob pattern alongside with a shopt -s globstar nullglob
or find
to execute rename
on all the files, but since I actually change the name of a directory during that operation, it breaks with messages like find: ./directory-pattern: No such file or directory
or rename: ./directory-pattern/another-expression: not accessible: No such file or directory
.
The second issue is that, according to rename
'a man page, it "will rename the specified files by repalcing the first occurrence" of the pattern, not all occurrences, and I didn't find any option to overwrite this behavior. Of course, I don't want to "just run rename
with -v
until it doesn't spit anything anymore", which just sounds silly.
So the question is: how do I achieve that bulk-renaming in Bash?
CodePudding user response:
Edit: leave only the 1-pass solution that apparently works as well as the 2-passes.
You'll probably have to explore the hierarchy depth first. Example with find
and a bash
exec script:
$ find . -depth -name '*pattern*' -exec bash -c \
'f=$(basename "$1"); d=$(dirname "$1"); \
mv "$1" "$d/${f//pattern/expression}"' _ {} \;
Demo:
$ tree .
.
├── file-pattern-pattern.html
├── file1
├── foo-pattern
│ └── directory-pattern
│ ├── another-pattern
│ └── pattern.pattern
└── other-pattern
└── a-file-pattern
$ find . -depth -name '*pattern*' -exec bash -c \
'f=$(basename "$1"); d=$(dirname "$1"); \
mv "$1" "$d/${f//pattern/expression}"' _ {} \;
$ tree .
.
├── file-expression-expression.html
├── file1
├── foo-expression
│ └── directory-expression
│ ├── another-expression
│ └── expression.expression
└── other-expression
└── a-file-expression
Explanation: -depth
tells find
to process each directory's contents before the directory itself. This avoids one of the issues you encountered when referring to a directory that was already renamed. The bash
script uses simple pattern substitutions to replace all occurrences of string pattern
by string expression
.