How can I recursively bzip2 all files that do not end with the .bz2
extension in Linux?
- 666
- 3
- 7
- 12
5 Answers
find is your friend. I reckon the following ought to do it:
find <target_dir> -not -name \*.bz2 -exec bzip2 \{\} \;
i.e. if the dir where the files you want to bzip are is /var/log/blah it would be:
find /var/log/blah -not -name \*.bz2 -exec bzip2 \{\} \;
- 2,742
- 2
- 17
- 24
-
1It may be faster to use `+` or `xargs` instead of `\;`. It's not necessary to escape the curly braces. POSIX supports `!` for "not". – Dennis Williamson Aug 14 '10 at 13:15
-
Why vote down? Will it not work on the platform specified? – Jason Tan Aug 14 '10 at 13:55
-
No clue why the downvote, this is the best (and correct) answer, I use it all the time. I also use xargs though, can't stand the -exec to find. Also consider using -print0 on the find and -0 to xargs to use NULL separation instead of spaces in case the filenames have spaces. – Aug 14 '10 at 14:18
-
1Unless they're all very small files, xargs is probably premature optimization anyways (the actual compression will take most of the time, not the fork and exec of starting up bzip2 repeatedly)... But: `find /var/log/blah -not -name \*.bz2 -print0 | xargs -0 bzip2` – freiheit Aug 14 '10 at 16:56
Off the top of my head (sorry, don't have a shell handy to test quoting etc.):
for _t in `find . -print |grep -v -E "\.bz$"`; do bzip2 -9 $_t && echo OK $_t || echo FAIL $_t; done
This uses find to find all files, grep to strip out the ones with a .bz2 extension and then feed them one at a time into bzip2. I expect some of the quoting is wrong, though - I'd test the bit in the backquotes separately first.
Good luck! You might want to use xz instead, though - it usually compresses better - or even tar everything up and bzip2 or xz instead.
- 255
- 5
- 15
-
... or apparently you can do it all in find. I didn't know it could do '-not' hence the grep etc. here. – Rup Aug 14 '10 at 12:52
-
I think the -not in find is only a GNU find option - so I don't think you could use it with most proprietary find commands - but since the question specifies Linux, GNU find is probably OK - your approach is probably more portable. – Jason Tan Aug 14 '10 at 12:56
-
POSIX supports `!` for "not". Using a `for` loop will fail for files that have spaces in their names. You should use `find | while read`. – Dennis Williamson Aug 14 '10 at 13:15
With zsh (setopt extended_glob
must be on):
bzip2 **/^*.bz2(.)
**
recurses in subdirectories; ^*.bz2
matches everything except *.bz2
; (.)
restricts to regular files.
With bash 4, if you're ok with ignoring bzip2
's complains about being invoked on directories:
bzip2 **/!(*.bz2)
- 9,181
- 1
- 30
- 48
Here is a one liner. If necessary, install ifne
first. On Ubuntu:
sudo apt-get install moreutils
And then:
find YOUR_DIR -type f -not -name \*.bz2 -print0 | ifne xargs -0 bzip2
This does the same as other answers, but you won't get warning messages about bzip2ing directories, and if there is nothing to bzip2
anymore, you won't get warning messages about bzip2ing compressed data to a terminal. It also uses xargs
etc.
YOUR_DIR can have wild cards or multiple dirs, e.g. YOUR*DIR or the like.
- 181
- 2
- 11
In the fish
console, it's pretty straightforward, and you don't have to worry about pipes and weird backslashes:
set files (find *)
for i in $files
bzip2 $i
end
- 101