How can I recursively zip files in their own folder?
I have the following directory structure:
/Data - file 1 - file 2 /Folder1 - file 3 - file 4 /Folder2 - file 5 - file 6 /Folder3 - file 7 - file 8In Linux I want to zip files (excluding folders) in every directory and create a 7z (or zip) archive in each folder resulting the following:
/Data Data.7z (Note: this should contain only file1 & 2, not any sub directories) /Folder1 Folder1.7z (this should contain only file3 & 4, not any sub directories) /Folder2 Folder2.7z (this should contain only file5 & 6, no Folder3) /Folder3 Folder3.7z (should contain only file7 & 8)Following script works in the first directory but not in the sub-directories :
for i in */ ; do base=$(basename “$i”) ; cd $base ; 7za a -t7z -r $base * ; .. ; cd .. ; done;How can I achieve this?
22 Answers
If you want to use 7z, the tricky part seems to be persuading it not to recurse; the documentation's indicated -r- switch appears to be non-functional, and the suggested workaround from the software's author is to exclude subdirectories with the wildcard expression -x!*/
So, given
$ tree Data
Data
├── file1
├── file2
├── Folder1
│ ├── file3
│ └── file4
├── Folder2
│ ├── file5
│ └── file6
└── Folder3 ├── file7 └── file8
3 directories, 8 filesthen
find Data -type d -execdir sh -c 'cd "$1" && 7z a "$1".7z -x!*/ && cd -' sh {} \;results in
$ tree Data
Data
├── Data.7z
├── file1
├── file2
├── Folder1
│ ├── file3
│ ├── file4
│ └── Folder1.7z
├── Folder2
│ ├── file5
│ ├── file6
│ └── Folder2.7z
└── Folder3 ├── file7 ├── file8 └── Folder3.7z
3 directories, 12 fileswhere for example we can check that Folder2.7z contains only its own folder's files using
$ 7z l Data/Folder2/Folder2.7z
7-Zip [64] 16.02 : Copyright (c) 1999-2016 Igor Pavlov : 2016-05-21
p7zip Version 16.02 (locale=en_CA.UTF-8,Utf16=on,HugeFiles=on,64 bits,2 CPUs Intel(R) Core(TM)2 Duo CPU P9600 @ 2.53GHz (1067A),ASM)
Scanning the drive for archives:
1 file, 128 bytes (1 KiB)
Listing archive: Data/Folder2/Folder2.7z
--
Path = Data/Folder2/Folder2.7z
Type = 7z
Physical Size = 128
Headers Size = 128
Solid = -
Blocks = 0 Date Time Attr Size Compressed Name
------------------- ----- ------------ ------------ ------------------------
2018-02-12 18:37:37 ....A 0 0 file5
2018-02-12 18:37:37 ....A 0 0 file6
------------------- ----- ------------ ------------ ------------------------
2018-02-12 18:37:37 0 0 2 filesNote: !*/ may require additional escaping in environments (such as the interactive bash shell) where ! is a history expansion operator.
This is untested code, only used with 'echo', since I don't like to end with multiple zip files. And it is phantasy zip syntax, since I don't know 7za, but I will explain:
find . -type d -execdir /.../ad-hoc.sh {} ";"The script ad-hoc.sh has to be adressed with an absolute path and should not be in the current path, to not affect the outcome, but it might be in the parentdir:
find . -type d -execdir $PWD/../ad-hoc.sh {} ";"and, if not made executable, be explicitly invoked:
find . -type d -execdir bash $PWD/../ad-hoc.sh {} ";"Find shall look in the current dir, only for files of type d (dirs), where to -execute a bashscript with parameter {}, the directory found.
Ok - what is the ad-hoc.sh, we execute in the dir and subdirs? It's another find:
#!/bin/bash
dir=$1
find $dir -maxdepth 1 -type f -exec echo zip -o "$dir.zip" {} +-maxdepth 1 prevents find to search subdirs, -type says to just operate on files. -exec launches a command, for testing "echo zip ...", but if it looks promising (you do backups often, don't you?), you - well, here starts my pseudo code: -o:= -output "$PWD.zip", and {} + is the file list.
tree
.
├── buch-klein.kry
├── buch.kry
├── crypt
│ ├── moveto.sh.crypt
│ └── sub1
│ ├── foo.crypt
│ └── sub2
│ └── bar.crypt
├── original
│ ├── 1
│ │ └── 2
│ │ └── 3
│ ├── moveto.sh
│ └── sub1
│ └── sub2
│ └── up3 -> ../../../nr
├── outputfile.txt
├── rot.sh
└── zoom.sh
find . -type d -execdir $PWD/../ad-hoc.sh {} ";"
zip -o ./..zip ././buch-klein.kry ././rot.sh ././buch.kry ././zoom.sh ././outputfile.txt
zip -o ./original.zip ./original/moveto.sh
zip -o ./crypt.zip ./crypt/moveto.sh.crypt
zip -o ./sub1.zip ./sub1/foo.crypt
zip -o ./sub2.zip ./sub2/bar.cryptEvery file with extension is a regular file, and every file without is a directory or a symlink to a dir (up3).
The {} has to be the last element of a find -exec command before the terminating ";" or +, so you have to build your 7z-command accordingly.
so if your 7za command is
7za a -t7z -r $dir.7za *the ad-hoc.sh might look like this:
#!/bin/bash
dir=$1
find $dir -maxdepth 1 -type f -exec echo 7za -t7z -r "$dir.7za" {} + 2