-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy path.functionrc
executable file
·364 lines (316 loc) · 9.84 KB
/
.functionrc
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
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
# vim: set filetype=sh :
## script helpers
# exit script with given stderr msg and (optionally) statuscode
exit_error() {
local errcode=${2:-$?}
echo "$1" >&2
exit "$errcode"
}; export -f exit_error
# check if first argument appears among the rest
# for use with arrays, like: in_array "$var" "${arr[@]}"
in_array() {
local needle=${1:?Missing needle}; shift
for arg; do
[[ $arg = $needle ]] && return
done; return 1
}; export -f in_array
# test for glob existance in current dir (use with literals, like: exists '*')
glob_exists() (
shopt -s nullglob dotglob; shopt -u failglob
a=($1); [[ ${a##*/} != . && ${a##*/} != .. && -e $a ]]
); export -f glob_exists
# reverse find, looks for <target> in specified dir or current then on parents up until /
# outputs location on stdin, returns 1 when not found
# usage: rfind <target> [<start>]
rfind() {
local target=${1:?Missing target} cwd=${2:-$PWD}
while true; do
if [[ -e "$cwd"/"$target" ]]; then
echo "$cwd"/"$target"
return 0
fi
if [[ "$cwd" ]]; then
cwd="${cwd%/*}"
else
break
fi
done
return 1
}; export -f rfind
# given a separator, a string and a variable name, creates an array with the parts of the string
# example:
# $ str_split / a/b/c parts
# equivalent to: parts=(a b c)
str_split() {
local OLDIFS=$IFS
IFS=${1:?"Missing separator"}
local srcval=${2:?"Missing value to split"}
if [[ -n $3 ]]; then
local dstname=$3
eval "$dstname=(\$srcval)" # hail satan!
else
for part in $srcval; do
echo "$part"
done
fi
IFS=$OLDIFS
}; export -f str_split
# given a separator and a list of items echoes a string joined
# example:
# $ list_join , foo bar
# outputs on stdout: foo,bar
list_join() {
local OLDIFS=$IFS
IFS=${1:?"Missing separator"}
echo "${*:2}"
IFS=$OLDIFS
}; export -f list_join
# shortcuts (newline-join, colon-join)
nl_join() {
list_join $'\n' "$@"
}; export -f nl_join
cl_join() {
list_join : "$@"
}; export -f cl_join
# non-blocking unique filter
filter_uniq() {
awk '!a[$0]++' "$@"
}; export -f filter_uniq
## directory navigation helpers
# make and chdir
mcd() {
mkdir -p "$1" && cd "$1"
}; export -f mcd
# rm and chdir
rcd() {
rmdir "$PWD" && cd ..
}; export -f rcd
# in-place file redirection (usage: with <I/O-file> <cmd> <args>...)
with() {
local input=${1?:Missing input file} output; shift
if [[ -r $input ]]; then
echo "Cannot read from input file" >&2
return 1
fi
output=$(mktemp)
"$@" <"$input" >"$output" && mv "$output" "$input"; st=$?
if (($st)); then
echo "Command failed, partial output: $output" >&2
return $st
fi
}; export -f with
# run commands inside dir without moving (usage: within <dir> <cmd> <args>...)
within() (
local cwd=$PWD
cd "$1" && shift && "$@"; st=$?
cd "$cwd" && return "$st"
); export -f within
## interactive-mode helpers
# run command until exit status is zero (usage: retry [<delay>] <cmd> <args>...)
retry() {
local delay=1 n
if ! [[ $1 = *[^0-9]* ]]; then
#TODO allow delay=0 (prevents Ctrl-C)
if (($1 > 0)); then
delay=$1
fi
shift
fi
# run command
while ! "$@"; do
echo "retrying in ${delay}s" >&2
for ((n=delay; n>0; n--)); do
sleep 1 || return
done
done
}; export -f retry
# run lines of commands from stdin prepending prefix from args while they return true
# example:
# $ chain git <<EOF
# > checkout master
# > pull
# > checkout -
# > merge master
# > EOF
# runs: git checkout master && git pull && git checkout - && git merge master
chain() {
local prefix=("$@")
local cmd raw_cmd raw_cmds=()
# you're not allowed to do this... but I do.
while read -r raw_cmd; do
raw_cmds+=("$raw_cmd")
done && for raw_cmd in "${raw_cmds[@]}"; do
eval "cmd=($raw_cmd)" && "${prefix[@]}" "${cmd[@]}" || break # cookies...
done
}; export -f chain
## misc
# creates small test environments for scripting
mklab() {
local d=$(mktemp -d) && cd "$d" || return 1
clear; bash --login --noprofile --norc
cd - && rm -r "$d"
}; export -f mklab
# helper to show background tasks on prompt
jobs_prompt() {
local a running_jobs stopped_jobs
a=($(jobs -pr)); [[ $a ]] && running_jobs=${#a[@]}
a=($(jobs -ps)); [[ $a ]] && stopped_jobs=${#a[@]}
if [[ $running_jobs || $stopped_jobs ]]; then
if [[ $running_jobs && $stopped_jobs ]]; then a=+; else a=; fi
echo "(${running_jobs:+${running_jobs}r}$a${stopped_jobs:+${stopped_jobs}s})& "
fi
}; export -f jobs_prompt
## command helpers
# open vim's help directly
hvim() {
local h=$1; shift
vim -c ":h $h | bd1 | normal zt" "$@"
}; export -f hvim
# creates a vim session based on git's branch
svim() {
local parent session branch a target
if [[ $1 = git ]]; then
shift
branch=$(branch_name)
fi
parent=${PWD%/*}
session=${PWD##*/}_${parent##*/}${branch:+:$branch}
if [[ $1 = ++ ]]; then
shift
#TODO allow to run in a different dir other than .
target=.
a=(); while IFS= read -r -d '' file; do
a+=("$file")
done < <(find "$target" -type f \( -empty -o -exec fgrep -qI '' {} \; \) -print0 | sort -z)
vim -c "chdir $target | $(printf 'SessionSaveAs %q' "$session")" "$@" -- "${a[@]}"
elif [[ $1 = + ]]; then
shift
vim -c "$(printf 'SessionSaveAs %q' "$session")" "$@"
else
vim -c "$(printf 'SessionOpen %q' "$session")" "$@"
fi
}; export -f svim
# stupid wrapper for stupid debian-like systems
# adds `apt` command that connects to the appropiate apt-* variant
if hash apt-get >/dev/null 2>&1; then
apt() {
local sed_expr='/Commands:/,/^$/{//d;s/^ *\([^ ]\+\).*/\1/p;}'
local get_cmds=($(apt-get | sed -n "$sed_expr")) cache_cmds=($(apt-cache | sed -n "$sed_expr"))
local cmd=$1; shift
for get_cmd in "${get_cmds[@]}"; do
if [[ $cmd = $get_cmd ]]; then
sudo apt-get "$cmd" "$@"; return
fi
done
for cache_cmd in "${cache_cmds[@]}"; do
if [[ $cmd = $cache_cmd ]]; then
apt-cache "$cmd" "$@"; return
fi
done
echo "Unknown command: $cmd"
echo
echo "Commands (apt-get):"
printf " %s\n" "${get_cmds[@]}"
echo
echo "Commands (apt-cache):"
printf " %s\n" "${cache_cmds[@]}"
return 1
}; export -f apt
fi
# wget-download entire page and all necessary assets
wget_fullpage() {
wget --page-requisites --span-hosts --convert-links "$@"
}; export -f wget_fullpage
wget_fullpage_no_robots() {
wget_fullpage -e robots=off "$@"
}; export -f wget_fullpage_no_robots
# wget-download and backup all navigable links from and below target
wget_mirror() {
wget --timestamping --recursive --level=inf --no-parent --page-requisites --convert-links --backup-converted "$@"
}; export -f wget_mirror
wget_mirror_no_robots() {
wget_mirror -e robots=off "$@"
}; export -f wget_mirror_no_robots
# similar to `chain git` but adds interactive repl
git_repl() {
while IFS= read -rep "git$(__git_ps1)> " line; do
if [[ $line = \!* ]]; then
line=${line#\!}
elif [[ $line != git\ * ]]; then
line="git $line"
fi
eval "$line"; history -s "$line";
done
}; export -f git_repl
# intelligent version of `git stash` that converts numbers to "stash@{<number>}" strings
stash() {
local i args=()
for arg do
if [[ $arg != *[!0-9]* ]]; then
args+=("stash@{$arg}")
else
args+=("$arg")
fi
done
git stash "${args[@]}"
}; export -f stash
# shourtcut to get current branch name
branch_name() {
git rev-parse --abbrev-ref HEAD
}; export -f branch_name
# lookup branch based on prefix (uses / as hierarchy)
# outputs matching branch on stdin
# returns 1 and output original when not found
lookup_branch_prefix() {
local target=${1:?Missing target prefix} ref refparts n
if command git rev-parse -q --verify "$target" 2>/dev/null; then
echo "$target"
return
fi
while read ref; do
ref=${ref#\* } ref=${ref%% *} ref=${ref#remotes/}
str_split / "$ref" refparts
for ((n=${#refparts[@]}; --n>=0;)); do
if [[ $(list_join / "${refparts[@]:$n}") == $target* ]]; then
echo "$(list_join / "${refparts[@]}")"
return
fi
done
done < <(command git branch -a)
echo "$target"
return 1
}; export -f lookup_branch_prefix
# make `git` recognize b:<prefix> branch notations
git() {
local args=("$@") n
for ((n=0; n<$#; n++)); do
#TODO support all notations specified by `man gitrevisions`
if [[ ${args[$n]} == b:* ]]; then
if branch=$(lookup_branch_prefix "${args[$n]:2}"); then
args[$n]=$branch
else
echo "WARN: Unknown branch: ${args[$n]}" >&2
fi
fi
done
command git "${args[@]}"
}; export -f git
# improved version of `gi` from gitignore.io (see: <https://github.com/joeblau/gitignore.io/issues/20>)
stdout_gi() (
gi_args=(); for arg; do
if [[ $arg = -- ]]; then
wget_args=("${gi_args[@]}")
gi_args=()
else
gi_args+=("$arg")
fi
done
IFS=,; wget -O- "${wget_args[@]}" http://gitignore.io/api/"${gi_args[*]}"
); export -f stdout_gi
# have the default command automatically update current .gitignore
gi() {
stdout_gi "$@" > .gitignore
}; export -f gi
# run command outside shell history
skip() { local last; read last _ <<<$(history | tail -n1); history -d "$last"; "$@"; }
[[ -e ~/.functionrc_local ]] && . ~/.functionrc_local