linux find and rename items with backslash not working as expected

I have tried a couple of approaches to find and rename some files that hold a backslash (mind you these files generated from a web app do not have the quoets) in a directory. I’ve tried with find and substitutions but they seem to fail at the parent level. It’s basically like it rename the parent, but then fails to descend after to child folders because its referencing the older parent name when descending?

Folder Structure:

'another' -> 'child'

When I run:

for file in `find . -name '*\*'`; do mv -v "$file" "${file/\/}"; done`

or

find . -exec rename 's/\//g' {} +

or

find . -name "*\*" -exec rename 's/\//g' {} +

They pretty much all say the same error, but do rename the parent or present dir, just fail on descending.

Can't rename ./anoter/yoyo ./anoter/yoyo: No such file or directory

What would I be missing in my command?

Asked By: gstlouis

||

You need to:

  • not loop over find‘s output like that
  • avoid the deprecated `...` form of command substitution
  • rename leaves before the branches they’re on
  • only remove the s in the name of the file, not its full path
  • avoid double quotes when wanting literal backslashes

So:

find . -depth -name '*\*' -exec rename -d 's/\//g' {} +

(here assuming a perl-based variant of rename and one that accepts that -d option to only rename the base name).

Or:

find . -depth -name '*\*' -exec zsh -c '
  for file do
    mv -v -- $file $file:h/${file:t:gs/\/}
  done' zsh {} +

Or in zsh, just:

autoload zmv
zmv -v '(**/)(*\*)(#qD)' '$1$2:gs/\/'

Here using the csh-style $var:s/string/replacement/ (with g to repeat), but you could also use the ksh-style ${var//pattern/replacement} (with // for the repeating form) if you preferred.

zmv does a depth-first traversal by default as that’s what you generally want, the (#qD) is to also consider hidden files. zmv will also do some sanity checks and not rename anything if some problems are detected like if there’s both a abc and abc file which would both be renamed to abc.

Answered By: Stéphane Chazelas

I guess that error message comes because you’re trying to remove both ‘es at once, and the directory ‘./anoter’ probably doesn’t exist.

You can handle all the files in your directory structure by just adding -type f to your find. But then you’ll still have the directories, if you know that you only have one level of subdirectories (or at least only ‘es in the names in one level) you can just use -type d to find, but if you have e.g. another/dir/ the command will fail with the same error.

You might get something to work with using -execdir instead on -exec (and using -depth), but I’ve never tried -execdir so I’m just going on what a quick reading of the manpage suggests. Thinking about it, I’m not even sure you need to handle the files first if you do that.

Categories: Answers Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.