Skip to main content
A few explanation as per reqeuested by the O.P.
Source Link
user86969
user86969

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.


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.

$dir is a global variable, use argument instead
Source Link
user86969
user86969

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 -- $dir$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".

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 -- $dir && 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".

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

Source Link
user86969
user86969

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 -- $dir && 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".