find . -type f -name '*%*' -execdir bash -c ' name=$1 name=${name//%20/ } # replace each %20 by a space name=${name//%28/(} # replace each %28 by a ( mv "$1" "$name"' bash {} \;
This uses find
to locate any regular file in or beneath the current directory that has a %
character in their name. For each such file, a short bash
script is executed which does the two substitutions that you mention on the given filename, and then renames the file.
It would be easy to add other parameter substitutions to this to delete or change other strings in the filenames.
A possibly quicker way would be to write a simple loop to do the same thing (assuming the bash
shell for this particular variation):
shopt -s nullglob dotglob globstar for pathname in ./**/*%*; do # skip pathnames to non-regular (or links to non-regular) files [[ ! -f "$pathname" ]] && continue name=${pathname##*/} # strip off directory components name=${name//%20/ } # ... so that we don't accidentally name=${name//%28/(} # ... modify the directory path # the destination is made up by the directory path # (original pathname with filename stripped off) # followed by the new name mv "$pathname" "${pathname%/*}/$name" done
The nullglob
shell option makes the pattern ./**/*%*
expand to nothing if it doesn't match anything (it would ordinarily be left unexpanded), dotglob
allows globbing patterns to mach hidden names, and globstar
enables the use of **
to match down into subdirectories "recursively".
Adapting that second loop into something that uses find
for generating the pathnames rather than a globbing pattern (e.g. for running under bash
releases earlier than release 4, which didn't have **
):
find . -type f -name '*%*' -exec bash -c ' for pathname do name=${pathname##*/} # strip off directory components name=${name//%20/ } # ... so that we don't accidentally name=${name//%28/(} # ... modify the directory path # the destination is made up by the directory path # (original pathname with filename stripped off) # followed by the new name mv "$pathname" "${pathname%/*}/$name" done' bash {} +
rename
command doesn't read from standard input, so you can't put it in a pipeline. If you could describe and show what it is you'd want to do, I'm sure someone would be able to give you some idea of what could be done.