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 .
7
  • 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? Commented Feb 7, 2015 at 11:30
  • @Nasha I've edited the question. Please check now. Commented Feb 7, 2015 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 Commented Feb 7, 2015 at 12:02
  • @cremefraiche Yes I get you, but that's working too. So I'm not quite bothered about that. Commented Feb 7, 2015 at 12:33
  • @cremefraiche : it makes no difference whether // or / as shells properly eliminate the dupe slash evaluating expressions. Commented Feb 7, 2015 at 12:38

2 Answers 2

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.

3
  • 2
    I was trying to stick with OPs theme, but I like your solution more. Commented Feb 7, 2015 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? Commented Feb 7, 2015 at 17:31
  • I edited my post accordingly. Don't hesitate to ask if you have any doubt or question. Commented Feb 9, 2015 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.

3
  • 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. Commented Feb 7, 2015 at 17:23
  • 1
    I have adjusted my answer after reading your comment. Hopefully this can work for you Commented Feb 7, 2015 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. Commented Feb 8, 2015 at 16:05

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.