I'm trying to place a zip file with the content of the directory in each directory that doesn't contain any subdirectory.

The following command works if there's no space in dirnames and/or filenames but fails if there are:

find pictures/ -type d -links 2 -execdir sh -c "pwd; echo '{}'; zip -v '{}/{}.zip' '{}/*' -x \*.zip -x \*.id" \;

I don't understand why it fails since I've quoted the '{}' as usual. Also tried "{}". Said differently, it should create A/B/C/C.zip with the content of A/B/C/*. If there are spaces in 'C', I get:

zip warning: name not matched: ./C/*
zip error: Nothing to do! (./C/./C.zip)

2 Answers 2


With your version of find, the {} in the string is replaced by the file name. It is almost always an error to use {} as part of a string, because the file name is inserted just like that. Here, the file name is used as a shell script fragment. If there's a directory called a'$(touch wibble)' then your command executes the shell code

pwd; echo 'a'$(touch wibble)''; zip -v 'a'$(touch wibble)'/a'$(touch wibble)'.zip' 'a'$(touch wibble)'/*' -x \*.zip -x \*.id"`

Note how this executes the command touch wibble (four times).

You need to pass the file name as an argument to the shell snippet instead. That's the only way to use find -exec or find -execdir to call a shell reliably (except in cases where file names are constrained to have no special characters, including spaces). The first argument after sh -c SCRIPT can be accessed from the script as "$0".

Furthermore zip doesn't accept wildcards in its list of files to zip, so the wildcard must be left unquoted, to be expanded by the shell.

find pictures/ -type d -links 2 -execdir sh -c 'pwd; echo "$0"; zip -v "$0/$0.zip" "$0/"* -x \*.zip -x \*.id' {} \;
  • 1
    That fixes the obvious problem, but still fails because of "$0/*". zip can't handle that. "$0"/* is better but still wrong. Thus zip -r ... "$0" instead of zip ... "$0/*", which is basically my answer. Jul 19, 2016 at 5:12
  • @SatoKatsura Ah, I hadn't spotted that, thanks. Why is "$0"/* wrong? That does something sensible, and I don't see any indication that it would not be what the asker wanted. Jul 19, 2016 at 18:26
  • It would miss files dir/.file. Jul 19, 2016 at 19:08
  • @SatoKatsura Sure, but that may have been by design. Some files are explicitly excluded and the zip is not recursive so the intent is not to store the whole directory. But anyway I think this is a peripheral issue, not related to the main point of the question which is safely getting a file name into a shell in find -exec. Jul 19, 2016 at 19:12

Change it like this:

find pictures -type d -links 2 -execdir \
    sh -c 'pwd; echo "$1"; zip -vr "$1/$1.zip" "$1" -x \*.zip -x \*.id' sh {} \;
  • Can you please explain what the sh at the very end does ?
    – dargaud
    Jul 19, 2016 at 7:20
  • 1
    The arguments for commands in sh -c ... start at 0. The sh at the end provides a dummy $0, so that I can write echo "$1" ... rather than echo "$0" .... It looks slightly less confusing. Jul 19, 2016 at 7:26

You must log in to answer this question.

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