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 {} +
13
  • 1
    This sounds like it might be a tool for rename. This is available on the linux systems I've used through the package manager.
    – Att Righ
    Commented Dec 5, 2017 at 0:38
  • Alternatively, this sounds like a great fit for a scripting language like python: for when you hit up against the "programming" limits of bash.
    – Att Righ
    Commented Dec 5, 2017 at 0:40
  • @AttRigh I agree - however I shouldn't have to install developer level tools on a system I am administering after simple backup restores Commented Dec 5, 2017 at 0:41
  • Well rename isn't really developer level :). Umm, so I imagine the issue that you need to solve is getting sed to split on nulls rather than newline. Apparently there is a -z, --null-data argument for precisely this purpose.
    – Att Righ
    Commented Dec 5, 2017 at 0:44
  • @AttRigh I started trying this command, and I'm getting "no file exists" errors find . -name '.1.' -type f -exec rename -n 's/\.1//' '{}' \; Commented Dec 5, 2017 at 0:45

1 Answer 1

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.

5
  • "unexpected token 'done' " though I assume maybe I need to put this in a shell script? Commented Dec 5, 2017 at 3:31
  • @FaultyJuggler oops! Left out fi. Try it now. (What I get for not testing. I still didn't test, though.)
    – Wildcard
    Commented Dec 5, 2017 at 3:33
  • Okay, one small error which I can probably figure out, it's also deleting the trailing dot before the file extension Commented Dec 5, 2017 at 3: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 {} + Commented Dec 5, 2017 at 3:44
  • @FaultyJuggler, not quite. I've just added in the period.
    – Wildcard
    Commented Dec 5, 2017 at 5:02

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .