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.

3
  • I don't have a cygwin to test on at the moment, but try quoting the braces: -exec rm -v "{}" ";" Commented Jan 12, 2023 at 15:24
  • @glennjackman -exec rm "{}" \ ";" -exec rm "{}" "\;" not working. I found working solution, last backslash should be removed, see my answer below.
    – Kamil
    Commented Jan 12, 2023 at 15:37
  • I'm using CMD shell on Windows : Just to make sure: Are you joking here? You are writing bash commands, tag your question correctly as bash, and then pretend that you would execute this in a Windows CMD shell instead of a bash?? Please make clear, what you are using here, and how. Commented Jan 3 at 11:38

2 Answers 2

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, {}, ;). The shell handles word splitting and globbing (in your case there were no unquoted wildcards though), it removes quotes, backslashes.

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).

1
  • Thanks for such deep and clear explanation. Part about how executables get their arguments in Windows and Linux was very interesting.
    – Kamil
    Commented Jan 13, 2023 at 0:43
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.

You must log in to answer this question.

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