1

Okay, I'm going in circles. I'm using this command

find . -print0 -name '*.1.*' | sed -e 'p;s/\.1//' | xargs -0 -n2 mv

To try and rename hundreds of files that had ".1." added just before the file extension when they were archived after someone accidentally deleted 200GB worth of data.

I'm caught between the mac xargs interpreting spaces in filenames as separate arguments, and not being able to set the delimiter to only newlines, not spaces. I cannot figure out how to have find print the '\0' character as well as newlines between. Any ideas on how to get this to work? I've been searching in circles and it seems simply being on a mac environment is making this more complicated than necessary.

Alternatively trying rename command but still having issues

 find . -name '*.1.*' -type f -exec rename -n 's/\.1//' '{}' \;

ANSWER as per @Wildcard below

find . -name '*.1.*' -type f -exec sh -c '
  for f do
    suf="${f##*.1}"
    new="${f%.1.*}$suf"
    if [ -e "$new" ]; then
      printf "Cannot move file <%s>\n" "$new"
    else
      mv -n "$f" "$new"
    fi
  done
  ' find-sh {} +

1 Answers1

3

The following should be safe always and should work on a Mac:

find . -name '*.1.*' -type f -exec sh -c '
  for f do
    suf="${f##*.1.}"
    new="${f%.1.*}.$suf"
    if [ -e "$new" ]; then
      printf "Cannot move file <%s>\n" "$new"
    else
      mv -n "$f" "$new"
    fi
  done
  ' find-sh {} +

Note the mv -n exits successfully without renaming the file, if it would overwrite an existing file. You would probably like it reported on. So this does so.

Also, in the odd edge case where there is a directory with the target new name, mv would move the file into it without the safety check I added (even with -n). That wouldn't be desirable either.

If a file has more than one .1. string in its name, this command will remove the last one only. (Which is probably closer to what was intended than removing the first one would be.)

Theoretically you don't need the -n switch at all with the safety check if block, but I left it in to safeguard data in event of race conditions (if some other process creates a target file just before you move a file on top of that name).

Most importantly, this won't blow up no matter how bizarre your file names are. Even if they have embedded newlines, single quote characters, asterisks and all sorts of other things.

Come to think of it, though, I'm not sure how well bash parameter expansion works on Unicode.

Wildcard
  • 36,499
  • "unexpected token 'done' " though I assume maybe I need to put this in a shell script? – FaultyJuggler Dec 05 '17 at 03:31
  • @FaultyJuggler oops! Left out fi. Try it now. (What I get for not testing. I still didn't test, though.) – Wildcard Dec 05 '17 at 03:33
  • Okay, one small error which I can probably figure out, it's also deleting the trailing dot before the file extension – FaultyJuggler Dec 05 '17 at 03:40
  • Got it

    find . -name '.1.' -type f -exec sh -c ' for f do suf="${f##.1}" new="${f%.1.}$suf" if [ -e "$new" ]; then printf "Cannot move file <%s>\n" "$new" else mv -n "$f" "$new" fi done ' find-sh {} +

    – FaultyJuggler Dec 05 '17 at 03:44
  • @FaultyJuggler, not quite. I've just added in the period. – Wildcard Dec 05 '17 at 05:02