Super User is a question and answer site for computer enthusiasts and power users. Join them; it only takes a minute:

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

I need to gzip all files in a dir separately including all files in sub-directories without deleting the originals afterwards. So let's say I have index.html, I want to have at the end index.html.gzip AND index.html.

I am able to gzip all files in my dir via

gzip -r .

But I want to keep the original files also.

Is this possible?

share|improve this question

migrated from Oct 3 '12 at 7:36

This question came from our site for professional and enthusiast programmers.

up vote 8 down vote accepted
find . -type f | \
while read -r x
  gzip -c "$x" > "$x.gz"

The -c pushes the result to stdout and keeps the original alone. The disadvantage is, that you need to find the files yourself. For more sophisticated traversing, you can use find(1), however, like above: . searches starting from the current directory, and -type f returns the name of every regular file.

share|improve this answer
Why *.* in ls? – user332325 Feb 15 '11 at 9:50
@pooh: *.* matches all regularly named files, and usually no directories. Of course, it only works in most cases and usually not if you need it. Then use the find command. – Boldewyn Feb 15 '11 at 11:15
@Boldewyn: the question was to gzip all files in a dir. ls *.* won't pick files like TODO. – user332325 Feb 15 '11 at 11:39
ls isn't needed at all: for x in *. However, using find should be find . -type f | while read -r x. – Dennis Williamson Feb 15 '11 at 11:51
Updated answer. Thanks for the -r switch on read. – Boldewyn Feb 15 '11 at 13:17
find . -type f -not \( -name '*.gz' -or -name '*[~#]' \) -exec sh -c 'gzip -c "{}" > "{}.gz"' \;

You could easily switch it around to include what you want to compress ( -name '*.txt -or -name '*.html etc.) instead of like now, excluding some files (already compressed, backup and temporary files).

Handles spaces in the filename just fine too.

Change gzip to echo gzip for testing. Or skip the -exec part all together.

Edit: Oh, I forgot to mention that this doesn't check if <target>.gz already exists. This may or may not be a problem.

Edit2: Ok, here we go with something that checks for existing file. If that may be wanted. Pardon the oneliney-ness.

while read file; do if [ ! -f "$file.gz" ]; then echo "Compressing $file"; gzip -c "$file" > "$file.gz"; else echo "Not overwriting $file.gz";  fi  done < <(find . -type f -not \( -name '*.gz' -or -name '*[~#]' \))

My find-foo is maybe not what it could be, it may very well be possible to skip directly in find.

share|improve this answer
just read that -name should come before -type to avoid calling stat on every file. – Ben Jul 17 '12 at 5:51
@Ben Makes sense, good input! For large trees it might be noticeable. – plundra Jul 18 '12 at 8:06

Just add the -c option, I think this should work

share|improve this answer
This probably only works when gzipping a single file at a time. – its_me Mar 30 '13 at 5:48

gzip 1.6 (June 2013) added the -k, --keep option, so now you can:

gzip -kr .

Found at:

share|improve this answer
gzip -rc `ls` > archive.gz

Tested on Debian (gzip 1.3.12-6)

share|improve this answer
That creates one archive. The OP wants separate archives. ls is unnecessary, use *. – Dennis Williamson Feb 15 '11 at 11:52

You must log in to answer this question.