1

I have backups script that removes backups older than x days.

On Linux machine this is working well:

find /backups/dummy-prod-db -name "*_D_dummy-prod-db*" -type f -mtime +7 -exec rm -v {} \;

But if I run this on Windows machine with Cygwin:

c:\cygwin64\bin\find.exe "C:\\backups\\dummy-prod-db\\" -name "*_D_dummy-prod-db*" -type f -mtime +7 -exec rm -v {} \;

it returns

/usr/bin/find: missing argument to `-exec'

How can I fix it?

Edit:

I'm using CMD shell on Windows.

Kamil
  • 2,686

2 Answers2

3

As you have already found out, the fix is to use ; instead of \;. In my answer I will try to explain the mechanics behind this.

At first it's good to know why you need \; on Linux. It's not obvious, but find on Linux expects ; (or +) as an argument somewhere after -exec, not \;; see the specification from The Open Group, it says -exec utility_name [argument ...] ;. The backslash is for the shell. The problem is ; is special for the shell, it's a command terminator. If you used ;, the shell would interpret it as such, find wouldn't see it at all. To tell the shell ; should not be treated specially we can escape it with a backslash (\;) or we can quote it (";" or ';'). The shell will interpret and remove the backslash or the quotes and find will see sole ; it expects.

The path c:\cygwin64\bin\find.exe you used makes me believe you were not working in Bash (or similar shell) in Cygwin, but rather in cmd.exe. In cmd.exe ; is not special, so there is no need to escape it. Additionally \ is not an escape character (^ is). \; you used got to find.exe literally as \;. The tool did not find ; (nor +) it expected, hence the error. Sole ; gets to find.exe as ; and this is what the tool expects.

AFAIK ";" in cmd.exe should ultimately get to find.exe as ;. ";" works on Linux as well, so we can call ";" "universal syntax" in the context of this problem. Note ';' is not universal, single-quotes are not special in cmd.exe*.


* The situation is somewhat more complicated.

In Linux, executables like find get their arguments as an array; in your case: find, /backups/dummy-prod-db, -name, *_D_dummy-prod-db*, -type, f, -mtime, +7, -exec, rm, -v, {}, ;. If you run find … in a shell then the shell handles word splitting and globbing (in your case there were no unquoted wildcards though) and it removes quotes, backslashes (this is when your \; becomes ;), all this before find is started. find cannot affect this procedure because when it starts, the job is already done.

In Windows, executables like find.exe get their arguments as a single string. AFAIK technically before the Windows system calls main(), it calls _setargv() to parse the command line into the standard C argc and argv[] arguments that it passes to the main() function of the program. The program can provide its own _setargv(). This means that interpretation of quotes and wildcards like *, and splitting to words (separate arguments) are really the job of each executable alone, not of cmd.exe. The executable may use some standard _setargv(), then it's almost like cmd.exe did the job, a situation similar to what is in Linux; or it may provide its own _setargv().

Where I wrote

";" in cmd.exe should ultimately get to find.exe as ;

"ultimately" means "after _setargv()". Before _setargv() the quotes were there along with the rest of the command string; if find.exe provided a custom _setargv(), it could interpret these quotes in any way the author desired.

And where I wrote

single-quotes are not special in cmd.exe

I really meant "not special for commonly used _setargv()". If find.exe provided a custom _setargv(), it could interpret single-quotes in any way the author wanted, e.g. like single-quotes in Bash. I don't know, maybe your find.exe does interpret single-quotes like this.

In general a phrase "something works (or doesn't work) in cmd.exe" includes cmd.exe and some standard _setargv().

For comparison: FIND in Windows (the native FIND, not a port of *nix find) has its own peculiar syntax because it interprets the string it gets differently than any standard _setargv() would. You can find more information here: Tcler's Wiki / exec quotes problem (note some parts of the article are in the context of Tcl which has nothing to do with our problem, still the fragment about FIND is educative).

0

I have found answer here:

https://stackoverflow.com/a/32377808/1215291

It says that backslash at the end should be removed. I changed this:

... -exec rm -v {} \;

To this:

... -exec rm -v {} ;

and it worked, but I don't understand why.

I would appreciate if someone could explain it.

Kamil
  • 2,686