Project

General

Profile

1
#!/bin/bash -e
2
realpath () { readlink -f -- "$1"; }
3

    
4
include_guard_var () { realpath "$1"|sed 's/[^a-zA-Z0-9_]/_/g'; }
5

    
6
self_not_included () # usage: if self_not_included; then ... fi
7
{
8
	test "$#" -ge 1 || set -- "${BASH_SOURCE[1]}"
9
	local include_guard="$(include_guard_var "$1")"
10
	alias self_being_included=false
11
	test -z "${!include_guard+t}" && \
12
	{ eval "$include_guard"=1; alias self_being_included=true; }
13
}
14

    
15
# to load newly-defined aliases for use in functions in the same file:
16
## fi # load new aliases
17
## if self_being_included; then
18
# this is needed because aliases defined inside an if statement are not
19
# available inside that if statement
20

    
21
if self_not_included "${BASH_SOURCE[0]}"; then
22

    
23
shopt -s expand_aliases
24

    
25
unalias () { builtin unalias "$@" 2>&- || true; } # no error if undefined
26

    
27
#### exceptions
28

    
29
# usage: try cmd...; ignore status; if catch status; then ...; fi; end_try
30

    
31
try_ () { { "$@"; e="$?";} || true; }
32
alias try='declare e; try_ ' # trailing space alias-expands next word
33

    
34
catch () { test "$e" -eq "$1"; e=0; }
35

    
36
ignore () { catch "$@" || true; }
37

    
38
alias end_try='return "$e"'
39

    
40
#### integers
41

    
42
let () { builtin let "$@" || true; }
43
	# "If the last ARG evaluates to 0, let returns 1" (`help let`)
44

    
45
#### arrays
46

    
47
join () { local IFS="$delim"; echo "$*"; } # usage: delim=... join elems...
48

    
49
reverse () # usage: array=($(reverse args...))
50
{
51
	local i
52
	for (( i=$#; i >= 1; i-- )); do printf '%q ' "${!i}"; done
53
}
54

    
55
#### verbose output
56

    
57
: "${verbosity:=$verbose}" "${verbosity:=0}"
58

    
59
can_log () { test "$verbosity" -gt 0; } # verbosity=0 turns off all logging
60

    
61
# usage: in func:      inc_log_level; ...
62
#        outside func: inc_log_level; ...; dec_log_level
63
alias inc_log_level='declare verbosity="$verbosity"; let verbosity--'
64
alias dec_log_level='declare verbosity="$verbosity"; let verbosity++'
65

    
66
echo_cmd () { if can_log; then echo "$PS4$*" >&2; fi; }
67

    
68
echo_run () { echo_cmd "$@"; "$@"; }
69

    
70
if test "$verbosity" -ge 1; then
71
	alias env="echo_run env" # automatically echo commands that use env
72
fi
73

    
74
canon_rel_path ()
75
{
76
	local path="$1"
77
	path="$(realpath "$path")" # canonicalize
78
	path="${path#$(pwd -P)/}" # remove any shared prefix with the current dir
79
	echo "$path"
80
}
81

    
82
fi # allow unalias to take effect
83
unalias echo_func
84
if self_being_included; then
85

    
86
echo_func ()
87
{
88
	inc_log_level
89
	local script="$(canon_rel_path "${BASH_SOURCE[1]}")"
90
	echo_cmd "$script:${BASH_LINENO[0]}" "${FUNCNAME[1]}" "$@"
91
}
92

    
93
fi # always restore the alias
94
alias echo_func='"echo_func" "$@"' # usage: func () { echo_func; ...; }
95
if self_being_included; then
96

    
97
echo_stdin () # usage: input|echo_stdin|cmd
98
{
99
	inc_log_level
100
	if can_log; then
101
		echo ----- >&2
102
		tee -a /dev/stderr;
103
		echo ----- >&2
104
	else cat
105
	fi
106
}
107

    
108
echo_vars () # usage: echo_vars var...
109
{
110
	inc_log_level
111
	if can_log; then { echo -n "$PS4"; declare -p "${@%%=*}";} >&2; fi
112
}
113

    
114
echo_export ()
115
{
116
	builtin export "$@"
117
	echo_vars "$@"
118
}
119

    
120
if test "$verbosity" -ge 2; then
121
	alias export="echo_export" # automatically echo env vars when they are set
122
fi
123

    
124
usage () { echo "Usage: $1" >&2; (exit 2); }
125

    
126
fi # load new aliases
127
if self_being_included; then
128

    
129
#### strings
130

    
131
sed_ere_flag="$(test "$(uname)" = Darwin && echo E || echo r)"
132

    
133
sed () { env sed -"$sed_ere_flag" "$@";}
134

    
135
#### vars
136

    
137
set_var () { eval "$1"'="$2"'; }
138

    
139
set_inv () { set_var no_"$1" "$(test -n "${!1}" || echo 1)"; }
140

    
141
# usage: local var=...; local_inv
142
alias local_inv='declare "no_$var=$(test -n "${!var}" || echo 1)"'
143

    
144
# usage: local prefix=..._; import_vars
145
alias import_vars="$(cat <<'EOF'
146
: "${prefix:?}"
147
local src_var dest_var
148
for src_var in $(eval echo '${!'$prefix'*}'); do
149
	dest_var="${src_var#$prefix}"
150
	local "$dest_var=${!src_var}"; echo_vars "$dest_var"
151
done
152
EOF
153
)"
154

    
155
#### commands
156

    
157
top_dir="$(dirname "$0")" # outermost script
158

    
159
run_args_cmd () # runs the command line args command
160
{
161
	test "$?" -eq 0 || return
162
	eval set -- "$(reverse "${BASH_ARGV[@]}")"
163
	test "$#" -ge 1 || set -- all
164
	echo_cmd "$(canon_rel_path "$0")" "$@"; "$@"
165
}
166

    
167
fwd () # usage: subdirs=(...); fwd "$FUNCNAME" "$@"
168
{
169
	echo_func
170
	: "${subdirs?}"
171
	
172
	for subdir in "${subdirs[@]}"; do
173
		"$(dirname "${BASH_SOURCE[1]}")"/"$subdir"/run "$@"
174
	done
175
}
176

    
177
#### make
178

    
179
# usage: target_filename/command () { echo_func; set_make_vars; ...; }
180
alias set_make_vars="$(cat <<'EOF'
181
local command="${FUNCNAME##*/}"; echo_vars command
182
local target_filename="${FUNCNAME%/*}"; echo_vars target_filename
183
local target="$top_dir/$target_filename"; echo_vars target
184
EOF
185
)"
186

    
187
make ()
188
{
189
	echo_func
190
	env make --directory="$top_dir" "$@"
191
}
192

    
193
if false; then ## usage:
194
inline_make 3<<'EOF'
195
target:
196
	$(self_dir)/cmd >$@
197
EOF
198
# target will be run automatically because it's first in the makefile
199
fi ##
200
inline_make ()
201
{
202
	echo_func
203
	local self="$(readlink -f "${BASH_SOURCE[1]}")"
204
	local self_dir="$(dirname "$self")"
205
	export self self_dir
206
	
207
	make --makefile=<((
208
		cat /dev/fd/3
209
		echo -n "
210
.SUFFIXES: # turn off built-in suffix rules
211
.SECONDARY: # don't automatically delete intermediate files
212
.DELETE_ON_ERROR: # delete target if recipe fails
213
"
214
	)|echo_stdin) "$@"
215
}
216

    
217
#### compression
218

    
219
### zip
220

    
221
zip ()
222
{
223
	try env zip "$@"
224
	ignore 12 # "zip has nothing to do" (`man zip`)
225
	end_try
226
}
227

    
228
alias unzip="echo_run unzip"
229
set_inv force
230
alias zip_newer="zip${no_force:+ -u}"
231
alias unzip_newer="unzip${no_force:+ -u} -o"
232
	# -o is safe because -u only extracts newer files
233

    
234
#### databases
235

    
236
# using prefixed connection vars
237
alias use_local="declare prefix=local_; import_vars"
238
alias use_remote="declare prefix=remote_; import_vars"
239
alias use_local_remote="use_local; use_remote"
240

    
241
quote='"'
242

    
243
esc_name () { echo "$quote${1//$quote/$quote$quote}$quote"; }
244

    
245
mk_esc_name () { set_var "$1"_esc "$(esc_name "${!1}")"; }
246

    
247
alias mk_schema_esc="declare schema_esc; mk_esc_name schema"
248
alias mk_table_esc="declare table_esc; mk_esc_name table"
249

    
250
fi # load new aliases
251
if self_being_included; then
252

    
253
log_sql () { test "$verbosity" -ge 2; }
254

    
255
### MySQL
256

    
257
# auto-adds connection/login opts when specified
258
mysql_cmd () # usage: mysql* () { ...; mysql_cmd "$@"; }
259
{
260
	echo_func
261
	if test _"$ssh_server" = _"$(hostname -f)"; then local ssh_server=; fi
262
	if test -n "$ssh_server"; then
263
		local ssh_dest="${ssh_dest-${ssh_user:+$ssh_user@}$ssh_server}"
264
	fi
265
	if test -n "$schema"; then local database="${database-$schema}"; fi
266
	
267
	local var=ssh_dest; local_inv
268
	echo_run ${no_ssh_dest:+env }${ssh_dest:+ssh "$ssh_dest" }"${FUNCNAME[1]}" \
269
${server:+ --host="$server" }${user:+--user="$user" } --password\
270
${password+="$password"} ${database:+--databases "$database" --tables } "$@"
271
}
272

    
273
mysql ()
274
{
275
	echo_func
276
	mysql_cmd --verbose "$@"
277
}
278

    
279
mysql_ANSI ()
280
{
281
	echo_func
282
	(echo "SET sql_mode = 'ANSI';"; cat)|mysql "$@"
283
}
284

    
285
mysqldump () # usage: [schema=1 | data=1] mysqldump db [table...]
286
{
287
	echo_func
288
	mysql_cmd --quick --lock-tables=false --set-charset \
289
${postgres_compat:+--compatible=postgresql --add-locks=false }\
290
${schema:+--no-data }${data:+--no-create-info }"$@"
291
}
292

    
293
mysqldump_diffable ()
294
{
295
	echo_func
296
	mysqldump "$@"|sed 's/^(-- Dump completed).*$/\1/'
297
}
298

    
299
### PostgreSQL
300

    
301
pg_copy_to ()
302
{
303
	echo_func
304
	if test -z "$source"; then
305
		: "${table:?}"; mk_table_esc
306
		if test -z "$limit"; then local source="$table_esc"
307
		else local source="(SELECT * FROM $table_esc LIMIT $limit)"
308
		fi
309
	fi
310
	local pg_copy_format="${pg_copy_format-CSV HEADER}"
311
	
312
	psql "$@" <<<"COPY $source TO STDOUT $pg_copy_format;"
313
}
314

    
315
pg_header ()
316
{
317
	echo_func
318
	local pg_copy_format="CSV HEADER" limit=0
319
	pg_copy_to "$@"|echo_stdin
320
}
321

    
322
pg_export_table_no_header ()
323
{
324
	echo_func
325
	local pg_copy_format="CSV"
326
	pg_copy_to "$@"
327
}
328

    
329
pg_export_table_to_dir_no_header ()
330
{
331
	echo_func
332
	local table="$1"; shift; mk_table_esc
333
	local cols="$(pg_header)"
334
	pg_export_table_no_header "$@" >"$exports_dir/$table.no_header.cols=$cols.csv"
335
}
336

    
337
fi
(46-46/51)