1

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 8

In 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?

2
  • are you looking specifically for Bash, or are you looking for any solution in general?
    – Thomas Ward
    Feb 12, 2018 at 23:17
  • I'm looking for a solution.
    – alien250
    Feb 12, 2018 at 23:32

2 Answers 2

3

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 files

then

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 files

where 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 files

Note: !*/ may require additional escaping in environments (such as the interactive bash shell) where ! is a history expansion operator.

1
  • Brilliant! This is exactly the result I wanted. I can use rsync to copy all the 7z files to another directory. Thank you for your time!
    – alien250
    Feb 13, 2018 at 12:48
0

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.crypt

Every 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
  • I tried this once and didn't work for me. I'll try again. Thanks for your time!
    – alien250
    Feb 13, 2018 at 12:50
  • Well, that error report isn't very useful. Feb 13, 2018 at 13:28

You must log in to answer this question.

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