Whole folder disappeared after running a rename command

Using 'rename' from brew to look for special characters in file names and replace them with another character so we can upload to OneDrive.

I've ran this on other macs without issue. However, an entire work folder has vanished.

This was the command which appears to have done it.

find . -exec rename -s '/' '_' {} +

The folder is not in trash. ls shows the folder is empty. Is it possible to recover this?


/ is the directory separator. It can't appear inside a file name. The graphical user interface shows / in file names, but under the hood the name contains :, which the GUI won't let you use in a file name.

(The reason for this weirdness is historical: the GUI derives from historical macOS which used : as the directory separator, whereas the core of the operating system and the command line derive from Unix which uses /.)

/ does appear in paths, as the directory separator, and rename operates on paths. Operating on paths allows it to move files between directories with very little extra effort.

find lists paths, and with find ., all paths (apart from . itself if it's listed) begin with ./. So you instructed rename to move all files under the current directory to the current directory, with _ instead of the directory separator. For example:

./foo.txt._foo.txt
./dir/foo.txt._dir_foo.txt
./dir/sub/foo_bar.txt._dir_sub_foo_bar.txt

Since all paths listed by find start with a ., all the file names now start with ._. File names starting with . are hidden. To show them in Finder, press ⌘ Command⇧ Shift.. To list them in a terminal, use the -A option to ls, e.g. ls -lA.

You can partly recover by running the opposite command:

find . -exec rename -i -s '_' '/' {} \;

Only partly though, because there's no way to tell for sure whether a _ in a file name was originally a directory separator or an underscore. A plausible heuristic would be to treat _ as a directory separator if there's a matching directory. Untested zsh code (note: this is zsh code, it won't work in bash):

function sort_by_underscores {
  local u=${${REPLY//[^_]/}//_/a}
  REPLY="$u $REPLY"
}

for x in ._*(O+sort_by_underscores); do
  d=${x%_*}
  while [[ ! -d $d ]]; do
    d=${d%_*}
  done
  echo mv -i -- $x $d/${x#"${d}_"}
done

If this looks sensible, run it again without the echo.