0

I want to gzip all of my CSS files recursively. But my command is not working properly.

I'm using find with the -exec option

$ find . -name "*.css" -exec gzip -c "{}" > "{}.gz" \;

When using -exec echo (or just leave the -exec part) it's clearly finding my plugin[1,2].min.css together with index.css but when using -exec gzip it's just ignoring any files, unless index.css.

The file size of index.css.gz is the same as when running gzip manually for index.css.

I've no idea whats going on. Any ideas?


$ tree
.
├── css
│   ├── custom
│   │   └── index.css
│   └── vendor
│       ├── plugin1
│       │   └── plugin1.min.css
│       └── plugin2
│           └── plugin2.min.css
 # ...

$ find . -name "*.css" -exec gzip -c "{}" > "{}.gz" \;
$ tree
.
├── css
│   ├── custom
│   │   ├── index.css
│   │   └── index.css.gz
│   └── vendor
│       ├── plugin1
│       │   └── plugin1.min.css
│       └── plugin2
│           └── plugin2.min.css
4ae1e1
  • 7,228
  • 8
  • 44
  • 77
boop
  • 7,413
  • 13
  • 50
  • 94
  • Currently `> "{}.gz"` is captured by the shell and not find; also, you can't do redirection in a `find -exec` command anyway. Use `bash -c "blah"` as the `-exec` command. – 4ae1e1 Dec 07 '15 at 01:47
  • 3
    By the way, beware of quoting of paths. To be safe, you should pass the path as an argument to `bash -c`: `find . -name '*.css' -exec bash -c 'gzip -c "$1" > "$1".gz' -- {} \;`. – 4ae1e1 Dec 07 '15 at 01:49
  • What does the double dash at the end do? Also: you could post this as an answer! – boop Dec 07 '15 at 01:50
  • `man bash`: If the `-c` option is present, then commands are read from the first non-option argument command_string. If there are arguments after the command_string, they are assigned to the positional parameters, starting with `$0`. But you want to assign to `$1`. – 4ae1e1 Dec 07 '15 at 01:52
  • I'll post it as answer if it solves your problem. – 4ae1e1 Dec 07 '15 at 01:52
  • @4ae1e1 Post it as answer, but add `-type f` ;) – boop Dec 07 '15 at 10:16
  • *sigh*. I'm **absolutely positive** that this is a duplicate, but finding the original question is being a bit elusive. – Charles Duffy Dec 07 '15 at 17:32
  • Hmm. http://stackoverflow.com/questions/15030563/redirecting-stdout-with-find-exec-and-without-creating-new-shell is somewhat on-point; likewise http://stackoverflow.com/questions/12965400/use-current-filename-multiple-times-in-find-exec – Charles Duffy Dec 07 '15 at 17:36

1 Answers1

2

gzip foo.txt bar.txt creates foo.txt.gz and bar.txt.gz, with no need for extra output redirections.

So, consider

find . -type f -name '*.css' -exec gzip {} +

as a simpler way to approach this task.

Edit

So, I can't read questions accurately: OP needs to keep the source .css files around. Fair enough.

To flog the "simpler find command" approach, I might create a separate utility script that would more cleanly handle spaces and other special characters in directory and file names. Call it do_the_gzip (make it executable, etc.):

#!/bin/sh

while [ $# -ge 1 ]; do
  gzip -c "$1" > "$1".gz
  shift
done

Edit 2

Charles Duffy has a nice suggestion in the comments, teaching me something new: shell for loops iterate over "$@" implicitly, so the script can be simpler:

#!/bin/sh

for f; do gzip -c "$f" > "$f".gz; done

...end of Edit 2.

Then the find command still looks similar:

find . -type f -name '*.css' -exec do_the_gzip {} +

and where there are directories and/or files with white space, quotes, and shell globbing characters, it can work cleanly.

Sorry for the careless first reading of the question.

sjnarv
  • 2,334
  • 16
  • 13
  • 1
    Apparently what OP wants here is to create the gzipped files AND retain the original files. – 4ae1e1 Dec 07 '15 at 05:06
  • What @4ae1e1 said is correct. – boop Dec 07 '15 at 10:11
  • Ah - my mistake. Thanks for the correction, and see the edit above. – sjnarv Dec 07 '15 at 17:24
  • 1
    No need for the `while` / `shift` fun: a `for` loop will do what you want automatically. `for f; do gzip -c "$f" >"$f.gz"; done` -- the default thing for a `for` loop to iterate over is `"$@"`, and that frees you from needing to check `$#` also. – Charles Duffy Dec 07 '15 at 17:26
  • Thanks, @CharlesDuffy! A bit of learning for me today... – sjnarv Dec 07 '15 at 17:39
  • No need to apologize. I still prefer @4ae1e1's answer since it's simpler tho. Thanks for your effort tho. – boop Dec 07 '15 at 18:13