-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathquickzip
More file actions
executable file
·311 lines (264 loc) · 10 KB
/
Copy pathquickzip
File metadata and controls
executable file
·311 lines (264 loc) · 10 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
#!/bin/bash
## Bash settings
set -uo pipefail
## Script globals
script_name=$(basename "${BASH_SOURCE[0]}")
script_dir=$({ cd "$(dirname "${BASH_SOURCE[0]}")" || { echo "Failed to access script file directory" >&2; exit; } } && pwd)
script_dir_abs=$({ cd "$(dirname "${BASH_SOURCE[0]}")" || { echo "Failed to access script file directory" >&2; exit; } } && pwd -P)
script_file="${script_dir}/${script_name}"
if [ -L "${BASH_SOURCE[0]}" ]; then
script_file_abs=$(readlink "${BASH_SOURCE[0]}")
else
script_file_abs="${script_dir_abs}/${script_name}"
fi
export CURRENT_PARENT_BASH_SCRIPT_FILE="$script_file"
script_args=("$@")
## Script imports
lib_dir="${script_dir}/../lib"
bash_functions_script="${lib_dir}/bash_script_func.sh"
## Source imports
source "$bash_functions_script"
## Arguments
src_path_arr=()
zipfile=''
force_glob=false
zip_on_ext=false
zip_ext='.zip'
fwd_args_arr_1=()
fwd_args_arr_2=()
dryrun=false
## Custom globals
## Script usage
read -r -d '' script_usage << EOM
Usage: ${script_name} [OPTION]... SRC_PATH... ['zip' OPTION]...
Quickly zip batches of files and/or folders.
SRC_PATH may be a relative or absolute path to a file/folder,
or a path leading up and including the common filename/foldername
prefix of multiple files/folders in the same source directory.
If the --zipfile location is a file path, files/folders from all
SRC_PATH arguments are combined in a single output zipfile created
at the indicated file path.
If the --zipfile location is an existing directory, separate
zipfiles are created for each SRC_PATH argument with filnames like
"\$(basename SRC_PATH).zip" within the indicated directory.
If the --zipfile option is not provided, separate zipfiles are
created for each SRC_PATH argument with filenames like
"\$(basename SRC_PATH).zip", each zipfile placed in the same
directory as the SRC_PATH.
If SRC_PATH is a directory and SRC_PATH does not end with a
forward slash (/), the folder is placed at the root of the zipfile
-- if it does end with a forward slash, the immediate contents of
the folder are placed at the root of the zipfile.
For 'zip' options, note that --recurse-paths (-r) is automatically
provided, and --junk-paths (-j) is not advised.
Options:
-o,--zipfile
Location of output zipfile.
-e,--zip-ext=<extension> (default='${zip_ext}')
File extension for automatically-determined output zipfile(s).
Cannot be used with --zipfile option.
-x,--zip-on-ext
Automatically group SRC_PATH arguments that have the same
base filename with different dot extensions.
-g,--glob
When SRC_PATH is the full path to an existing file/folder,
force treating all SRC_PATH arguments as prefixes matching
additional files/folders in the same directories.
-db,--debug
-dr,--dryrun
Print command(s) used to zip files/folders, without executing.
EOM
if (( $# < 1 )); then
echo_e -e "$script_usage"
exit_script_with_status 1
fi
## Parse arguments
set +u
parsing_fwd_args=false
while (( $# > 0 )); do
arg="$1"
if [ "$parsing_fwd_args" = true ]; then
# Accept critical script optional arguments
# specified at end of command.
is_fwd_arg=true
if [ "$(string_startswith "$arg" '-')" = true ]; then
arg_opt="$(string_lstrip "$arg" '-')"
is_fwd_arg=false
if [ "$arg_opt" = 'h' ] || [ "$arg_opt" = 'help' ]; then
arg_opt_nargs=0
echo "$script_usage"
exit 0
elif [ "$arg_opt" = 'db' ] || [ "$arg_opt" = 'debug' ]; then
dryrun=true
elif [ "$arg_opt" = 'dr' ] || [ "$arg_opt" = 'dryrun' ]; then
dryrun=true
else
is_fwd_arg=true
fwd_arg_pos=1
if [ "$arg_opt" = 'i' ] || [ "$arg_opt" = 'include' ]; then
fwd_arg_pos=2
elif [ "$arg_opt" = 'x' ] || [ "$arg_opt" = 'exclude' ]; then
fwd_arg_pos=2
fi
fi
fi
if [ "$is_fwd_arg" = true ]; then
if [ "$(string_contains "$arg" '*')" = true ] || [ "$(string_contains "$arg" ' ')" = true ]; then
arg="'${arg}'"
fi
if (( fwd_arg_pos == 1 )); then
fwd_args_arr_1+=( "$arg" )
elif (( fwd_arg_pos == 2 )); then
fwd_args_arr_2+=( "$arg" )
fi
fi
elif [ "$(string_startswith "$arg" '-')" = false ]; then
src_path_arr+=( "$arg" )
else
arg_opt="$(string_lstrip "$arg" '-')"
arg_opt_nargs=''
if [ "$(string_contains "$arg_opt" '=')" = true ]; then
arg_val=$(printf '%s' "${arg_opt#*=}" | sed -r -e "s|^['\"]+||" -e "s|['\"]+$||")
arg_opt="${arg_opt%%=*}"
arg_opt_nargs_do_shift=false
else
arg_val="$2"
arg_opt_nargs_do_shift=true
fi
arg_val_can_start_with_dash=false
if [ "$arg_opt" = 'h' ] || [ "$arg_opt" = 'help' ]; then
arg_opt_nargs=0
echo "$script_usage"
exit 0
elif [ "$arg_opt" = 'o' ] || [ "$arg_opt" = 'zipfile' ]; then
arg_opt_nargs=1
zipfile="$arg_val"
elif [ "$arg_opt" = 'e' ] || [ "$arg_opt" = 'zip-ext' ]; then
arg_opt_nargs=1
zip_ext="$arg_val"
elif [ "$arg_opt" = 'x' ] || [ "$arg_opt" = 'zip-on-ext' ]; then
arg_opt_nargs=0
zip_on_ext=true
elif [ "$arg_opt" = 'g' ] || [ "$arg_opt" = 'glob' ]; then
arg_opt_nargs=0
force_glob=true
elif [ "$arg_opt" = 'db' ] || [ "$arg_opt" = 'debug' ]; then
arg_opt_nargs=0
dryrun=true
elif [ "$arg_opt" = 'dr' ] || [ "$arg_opt" = 'dryrun' ]; then
arg_opt_nargs=0
dryrun=true
else
arg_opt_nargs=0
if (( ${#src_path_arr[@]} > 0 )); then
parsing_fwd_args=true
continue
else
echo_e "Unexpected argument: ${arg}"
exit_script_with_status 1
fi
fi
if [ -z "$arg_opt_nargs" ]; then
echo_e "Developer error! "'$arg_opt_nargs'" was not set for argument: ${arg}"
exit_script_with_status 1
fi
if [ "$arg_opt_nargs_do_shift" = true ] && (( arg_opt_nargs >= 1 )); then
for arg_num in $(seq 1 $arg_opt_nargs); do
shift
arg_val="$1"
if [ -z "$arg_val" ]; then
echo_e "Missing expected value (#${arg_num}) for argument: ${arg}"
exit_script_with_status 1
elif [ "$arg_val_can_start_with_dash" = false ] && [ "$(string_startswith "$arg_val" '-')" = true ]; then
echo_e "Unexpected argument value: ${arg} ${arg_val}"
exit_script_with_status 1
fi
done
fi
fi
shift
done
set -u
## Validate and adjust arguments
num_src_path_args="${#src_path_arr[@]}"
if (( num_src_path_args == 0 )); then
echo_e "At least one SRC_PATH argument must be provided"
exit_script_with_status 1
fi
if [ -n "$zipfile" ]; then
zipfile=$(fullpath "$zipfile")
fi
src_path_zoe_arr=()
if [ "$zip_on_ext" = true ]; then
for src_path in "${src_path_arr[@]}"; do
src_path_dirname=$(dirname "$src_path")
src_path_basename=$(basename "$src_path")
src_path_basename_noext="${src_path_basename%%.*}"
if [ "$src_path_basename_noext" != "$src_path_basename" ]; then
src_path_basename_noext="${src_path_basename_noext}."
fi
src_path_noext="${src_path_dirname}/${src_path_basename_noext}"
if [ "$(itemOneOf "$src_path_noext" ${src_path_zoe_arr[@]+"${src_path_zoe_arr[@]}"})" = false ]; then
src_path_zoe_arr+=( "$src_path_noext" )
fi
done
src_path_arr=("${src_path_zoe_arr[@]}")
fi
src_fullpath_arr=()
for src_path in "${src_path_arr[@]}"; do
if [ ! -e "$src_path" ] || [ "$(string_endswith "$src_path" '/')" = true ] || [ "$force_glob" = true ]; then
shopt -s nullglob
src_dirent_arr=( "$src_path"* )
shopt -u nullglob
if (( ${#src_dirent_arr[@]} == 0 )); then
echo_e "Cannot find any files/dirs matching SRC_PATH argument: $(string_rstrip "$src_path" '*')*"
exit_script_with_status 1
fi
fi
src_fullpath_arr+=( "$(fullpath_preserve_trailing_slash "$src_path")" )
done
zip_opt_args_1="${fwd_args_arr_1[*]+${fwd_args_arr_1[*]}}"
zip_opt_args_2="${fwd_args_arr_2[*]+${fwd_args_arr_2[*]}}"
## Perform zipping
for src_path in "${src_fullpath_arr[@]}"; do
if [ -n "$zipfile" ]; then
if [ -d "$zipfile" ]; then
dst_zipfile="${zipfile}/$(string_rstrip "$(basename "$src_path")" '.')${zip_ext}"
else
dst_zipfile="$zipfile"
fi
else
dst_zipfile="$(fullpath "$(string_rstrip "$src_path" '.')")${zip_ext}"
fi
if [ "$(string_endswith "$src_path" '/')" = true ]; then
working_dir="$src_path"
src_path_basename=""
else
working_dir=$(dirname "$src_path")
src_path_basename=$(basename "$src_path")
fi
echo "Writing to zipfile: ${dst_zipfile}"
echo "Working in directory: ${working_dir}"
cd "$working_dir"
cd_status=$?
if (( cd_status != 0 )); then
echo_e "Cannot 'cd' to directory: ${working_dir}"
exit_script_with_status 1
fi
if [ ! -e "$src_path" ] || [ "$(string_endswith "$src_path" '/')" = true ] || [ "$force_glob" = true ]; then
src_dirent_arr=( "$src_path_basename"* )
else
src_dirent_arr=( "$src_path_basename" )
fi
cmd="zip -r ${zip_opt_args_1} \"${dst_zipfile}\" $(printf ' "%s"' "${src_dirent_arr[@]}") ${zip_opt_args_2}"
if [ "$dryrun" = true ]; then
echo "$cmd"
else
eval "$cmd"
cmd_status=$?
if (( cmd_status != 0 )); then
echo_e "Processing command failed with non-zero exit status (${cmd_status}): ${cmd}"
exit_script_with_status 1
fi
fi
done