0

I'm trying to make a script which will see if the directory exists and will zip all of its contents under it according to the name/path of the directory.

The farthest I could go was -

#!/system/bin/sh -xv

Z="$PWD/../WorkingDir";
T='320';
F='480';
I='540';
D='dark';
L='light';
P='play';

cd $Z;

if [ -d $T/$D ]; then
    R=""$T"_"$D".zip";
else
    if [ -d $T/$L ]; then
        R=""$T"_"$L".zip";
    else
        R="file.zip";
    fi
fi;

for dir in */*/; do
    #Skip directories where the zip already exists
    [ -e "$dir"/"$R" ] || 
    ( cd -- "$dir" && zip -0rq "$R" .)
done

But this happens -

#!/system/bin/sh -xv
...
.
.
if [ -d $T/$D ]; then
    R=""$T"_"$D".zip";
else
    if [ -d $T/$L ]; then
        R=""$T"_"$L".zip";
    else
        R="file.zip";
    fi
fi;
+ [ -d 320/dark ]
+ R=320_dark.zip

for dir in */*/; do
    #Skip directories where the zip already exists
    [ -e "$dir"/"$R" ] ||
    ( cd -- "$dir" && zip -0rq "$R" .)
done
+ [ -e 320/dark//320_dark.zip ]
+ cd -- 320/dark/
+ zip -0rq 320_dark.zip .
+ [ -e 320/light//320_dark.zip ]
+ cd -- 320/light/
+ zip -0rq 320_dark.zip .
.
.
...and so on..

Every file gets named the same (320_dark.zip in this case). I want it different (according to the folder name/path) for every file that gets zipped.

I wanted -

+ [ -d 320/dark ]
+ R=320_dark.zip

-to change every time for every folder. Like it should be 320_light.zip for the folder path 320/light.

So the last part of the output would be -

+ [ -e 320/dark//320_dark.zip ]
+ cd -- 320/dark/
+ zip -0rq 320_dark.zip .
+ [ -e 320/light//320_light.zip ]
+ cd -- 320/light/
+ zip -0rq 320_light.zip .
  • Your script sets the ZIP archive file name before entering the for loop, consequently all compressed files will have that very same name. Could you add examples of what you'd wished? – user86969 Feb 7 '15 at 11:30
  • @Nasha I've edited the question. Please check now. – Chinmay Kunkikar Feb 7 '15 at 11:39
  • 3
    Not an answer, but you definitely should try to use more verbose variable names. Single character names are only a recipe for disaster – darnir Feb 7 '15 at 12:02
  • @cremefraiche Yes I get you, but that's working too. So I'm not quite bothered about that. – Chinmay Kunkikar Feb 7 '15 at 12:33
  • @cremefraiche : it makes no difference whether // or / as shells properly eliminate the dupe slash evaluating expressions. – user86969 Feb 7 '15 at 12:38
2

Here's my suggestion, removing single-letter variables and changing the location of the archive for consistency's sake:

#!/system/bin/sh -xv

cd "$PWD/../WorkingDir"

compress() {
    # Detect only known dir names, convert slash to underscore
    local arch="$(echo $1 | sed -rne '/320\/(light|dark)/s@/@_@gp')"

    # Set a default value to the variable
    : ${arch:=file}

    # Only create a ZIP archive if not present in the parent directory
    # Note $PWD is evaluated *before* cd
    [ -f "${arch}.zip" ] || ( cd -- $1 && zip -0rq "${PWD}/${arch}.zip ." )
}

for dir in */*/; do
    compress $dir
done

Just change the sed line to add what I called "well-known directory names".


EDIT: Adding a few explanations, as per OP request.

The script does verify conditions prior to zipping directories each time a new directory is found by the for loop. Variables have also been eliminated. Let's dissect the code.

The main loop

cd "$PWD/../WorkingDir"

for dir in */*/; do     # Ensures to select only directories
    compress $dir       # compress runs some checks prior to compressing
done

There is indeed not much more to say about it.

The compress routine

local arch="$(echo $1 | sed -rne '/320\/(light|dark)/s@/@_@gp')"

$1 is the argument passed to compressed, i.e. the directory to check and compress. What sed does first ('/320\/(light|dark)/') is check whether the path is of the form

320/light
320/dark

and returns the path with the slash converted to an underscore: 's@/@_@g'. Using the at sign as a separator for readability to avoid escaping the slash. Other valid notations are 's:/:_:g' or 's#/#_#g' or the classical 's/\//_/g'. Note how the slash to transform is escaped with an anti slash. I find the latter form less readable though.

The trailing p works together with sed -n; the latter option in this particular case means «don't echo anything unless told to by p whenever a match (i.e. in the enclosing slashes) is found». Therefore arch will contain a path with the slash transformed into an underscore if and only if the regular expression 320\/(light|dark) is found in the path. Otherwise it will echo nothing, making arch a blank variable.

Option -r is a convenient way to tell sed to use canonical regular expressions, more readable. Otherwise you would need to escape the parenthesis and pipe characters. I find '/320\/(light|dark)/s@/@_@gp' more readable than '/320\/\(light\|dark\)/s@/@_@gp' or '/320\/\(light\|dark\)/s/\//_/gp'.

: ${arch:=file}

This line sets arch to its default value "file" if it is blank. The weird construction is required because ${arch:=file} alone triggers a syntax error. The colon tells the shell to do nothing (:) but assign arch a value if it is blank.

    [ -f "${arch}.zip" ] || ( cd -- $1 && zip -0rq "${PWD}/${arch}.zip ." )

Here I used -f instead of -e because the former also checks for zero-length files. The test will succeed only if a ZIP archive exists and has a non zero length.

If there's no archive file in the parent (or the script's current) directory or if its size is null, then the script spawns a sub-shell that changes to the directory passed as an argument to compress and zips its content. Note that variables $1 and $PWD are evaluated before the sub-shell executes its instructions, i.e. $PWD is equal to the parent directory $Z in your initial version.

Expanding the filter

You can act upon sed the following way:

sed -rne '/320\/(light|dark|play)/s@/@_@gp'

to check directory names such as

320/light
320/dark
320/play

You may also consider /(320|480|540)\/(light|dark|play)/, which gives you 9 combinations, or a more generic form /[0-9]+\/(light|dark|play)/ (any number followed by a slash and one of "light", "dark" and "play") or even /[0-9]+\/\w+/ (any number followed by a slash and a word aka [A-Za-z0-9_]+). It all depends how strict or broad you want the filter to be.

  • 2
    I was trying to stick with OPs theme, but I like your solution more. – cremefraiche Feb 7 '15 at 13:26
  • Wow! Seems quite promising. I would love to do it as soon as possible, but it'll take me some more time to understand and actually work on it, and right now, I've got only my android on which I can work. So, I'll possibly try it out by Monday. Also, can you explain me more about it? I'm confused where to add directory names. Yes sure it's sed , but what if I want to add more than 3 paths/names? How do I proceed? – Chinmay Kunkikar Feb 7 '15 at 17:31
  • I edited my post accordingly. Don't hesitate to ask if you have any doubt or question. – user86969 Feb 9 '15 at 8:59
1
#!/system/bin/sh -xv

Z=/absolute/path/to/WorkingDir
T=320
F=480 # ?
I=540 # ?
D=dark
L=light
P=play # ?

cd $Z

for dir in $Z*/*/; do

    if [ -d $Z/$T/$D ] && [ ! -f $T$D.zip ]; then
        zip -0rq $T$D.zip $dir;            
    elif [ -d $Z/$T/$L ] && [ ! -f $T$L.zip ]; then      # elif rules
        zip -0rq $T$L.zip $dir;
    # It's not very efficient to scale this up, but you could add more elif statements here.
    elif [ -d $Z/$F/$D ] && [ ! -f $F$D.zip]; then
        zip -0rq $F$D.zip $dir;
    # and so on..
    else
        exit
    fi;
done

I think this is all you need. Let me know if it needs any adjustments.

  • Thank you! But you forgot to change the directory after if [conditions] it zipped whole directory! Also, can you tell me how to add more elif conditions? Because there exist paths like 320/play 480/light 540/dark and so on. – Chinmay Kunkikar Feb 7 '15 at 17:23
  • 1
    I have adjusted my answer after reading your comment. Hopefully this can work for you – cremefraiche Feb 7 '15 at 19:27
  • Yes that did work. But one little problem again. When I open the zip, it doesn't show me the files under $Z/*/*/ directly. Instead it actually creates a path under it like /absolute/path/to/WorkingDir/540/light/ and then shows the actual contents. – Chinmay Kunkikar Feb 8 '15 at 16:05

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.