-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathfrc
More file actions
executable file
·253 lines (212 loc) · 8.93 KB
/
frc
File metadata and controls
executable file
·253 lines (212 loc) · 8.93 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
#!/bin/bash
# frc -- Frequently Run Commands
# inspired by https://github.com/pvolok/mprocs, which is a damn fine tool
# which you should definitely try out
# however, after getting used to it, I found myself really missing it on a
# couple of machines where there are policies that dictate what is allowed and
# what is not (don't ask, but this is not uncommon)
# because of that, and partly as a challenge, I decided to do this in shell,
# using fzf for the UI and tmux itself for the windowing
# my first version used *panes*, but some kind soul on /r/tmux gave me a
# lightbulb moment: use a nested TMUX session, and indeed this is much better
# than what I had before!
# upsides:
# 1. use on machines where you're not allowed to install arbitrary binaries
# 2. use your own tmux shortcuts (muscle memory for the win!) to navigate
# windows, kill programs and so on
# 3. quitting does not kill the running processes or lose their window
# contents.
# downsides:
# 1. live/dead status does not automatically update -- it'll update when
# you move the arrow keys, or you can hit ctrl-r (see below)
# 2. WARNING: DO NOT USE "tmux kill-*" commands, or any tmux shortcut that
# runs those commands!
# 3. I only need to run it *once*, from $HOME, so there's only one rc file
# in a fixed location; you may need more than that
# usage:
# - create ~/.config/frc that has lines like this:
# w0 wg-quick down wg0
# w1 wg-quick up wg0
# phs nmcli dev wifi connect phs
# # phs == phone hotspot
# hwf nmcli dev wifi connect homewifi
# save cd ~/work; rclone sync -v . work-server:backup/work
# - note: these are just some silly examples I made up; they're not
# the ones I actually have in my rc file :)
# - once you have created the file, just run "frc" without arguments
# - (all invocations *with* arguments are "internal use only")
# notes on the rc file:
# - the format is "<name> <command> <args>"; NO LEADING SPACES PLEASE!
# - the "name" is a simple word you choose that reminds you what command
# it represents
# - the "command + args" is treated as a single string and run by "tmux
# neww", so you can include "cd" and such commands in it if you need to
# run something from a different directory
# - a "#" in column 1 marks a comment line
# - ~/.config/frc is the one you edit and is permanent. At runtime,
# however, all non-comment lines from this get copied to ~/.cache/frc;
# as you add and remove commands this is the file that gets updated.
# It gets overwritten every time you start frc
# the display is a typical fzf display on the left pane, and a *nested* TMUX
# session on the right, with the window corresponding to the current line
# visible.
# keys:
# - "enter" on a line to run the command (no-op if it is already running)
# - ctrl-d to kill a window and delete it from the list
# - ctrl-a to add a new command
# - ctrl-r to refresh the "live/dead" status
# - (marked by a "*" for programs that are running)
# - ctrl-e to "vimdiff" the permanent and running rc files; note that this
# does not reload anything -- it just helps you update the permanent
# file a bit more conveniently
# - ctrl-c to kill frc (but not the nested TMUX session)
# - next time you run frc, the session will come back to life
# - to kill the leftover session before next use, remove the "running"
# RC file (~/.cache/frc) and then run frc. Or use tmux commands.
# what mprocs has extra which I don't need (but you might)
# - pre-set env variables
# - array of words as command instead of just a single string passed to shell
# - kill/force kill (I just use tmux shortcuts to go to the pane and kill it)
# - focus/toggle (again, I just use tmux shortcuts)
# ----------------------------------------------------------------------
# User servicable parts
RC=$HOME/.config/frc
RRC=$HOME/.cache/frc
PERC=80 # view window takes 80% of the window
# ----------------------------------------------------------------------
[[ -n $TMUX ]] || { echo >&2 must run inside a tmux window; exit 1; }
[[ -f $HOME/.tmux-frc.conf ]] || cat <<EOF > $HOME/.tmux-frc.conf
# tmux conf for "frc" (frequently run commands)
# default settings saved to ~/.tmux-frc.conf if it does not exist; feel free to modify
set-window-option -g xterm-keys on
set -g default-terminal "xterm-256color"
set -g status-fg black
set -g status-bg white
set -g window-status-format "#I #W"
set -g window-status-current-format "#[fg=brightwhite bold bg=colour27]#I #W#[default]"
set -g status-justify left
set -g status-right "%H:%M:%S"
EOF
ME="$0"
TTY=`tty`
unset FZF_DEFAULT_OPTS
# ----------------------------------------------------------------------
[[ -z $1 ]] && {
# user invokes with no arguments
set -- START
[[ -f $RRC ]] || {
# if the RRC file is not found, it was manually deleted; we take that
# to indicate that a leftover session must be killed
env TMUX= tmux kill-session -t frc 2>/dev/null
sed -e 's/ */ /' < $RC | grep -v '^#' > $RRC
}
}
# ----------------------------------------------------------------------
new_s() {
echo
tmux set -g remain-on-exit on
tmux set -g renumber-windows on
new_w "$@"
}
new_w() {
echo
echo -n 'hit enter to run: '
echo "$@"
}
# ----------------------------------------------------------------------
start() {
i=0
if env TMUX= tmux list-sessions 2>/dev/null | grep ^frc: >/dev/null; then
tmux splitw -h -p $PERC "env TMUX= tmux attach-session -t frc"
tmux selectp -t 0
else
while read name cmd
do
if [[ $i == 0 ]]; then
tmux splitw -h -p $PERC "env TMUX= tmux -f $HOME/.tmux-frc.conf new -s frc $ME new_s $cmd"
sleep 1
# this sleep is *required* -- otherwise the whole thing exits before the "remain-on-exit" option is set!
else
env TMUX= tmux neww -t frc:$i $ME new_w "$cmd"
fi
(( i++ ))
done < $RRC
fi
tmux selectp -t 0
env TMUX= tmux selectw -t 0
# cut -f1 -d' ' < $RRC | fzf --layout=reverse --no-sort \
< $RRC fzf --nth 1 --with-nth 1 --layout=reverse --no-sort \
--bind "down:down+execute-silent($ME > $TTY 2>&1 view {n})+reload(cat $RRC)" \
--bind "up:up+execute-silent($ME > $TTY 2>&1 view {n})+reload(cat $RRC)" \
--bind "enter:execute($ME > $TTY 2>&1 enter {n})+reload(cat $RRC)" \
--bind "ctrl-d:execute($ME > $TTY 2>&1 del {n})+reload(cat $RRC)" \
--bind "ctrl-a:execute($ME > $TTY 2>&1 add )+reload(cat $RRC)" \
--bind "ctrl-r:execute($ME > $TTY 2>&1 lds )+reload(cat $RRC)" \
--bind "ctrl-e:execute(gvimdiff ~/.config/frc ~/.cache/frc)" \
--bind "ctrl-c:execute(tmux killp -a -t 0)+abort" \
--preview "echo {}|perl -pe 's/^\S+\s+//'" \
--preview-window "down,wrap" \
--info=inline \
--header "^d: delete; ^a: add"$'\n'"^r: update live/dead status"
}
view() {
line=$1 # fzf returns 0-based line number, and tmux also numbers windows from 0, so we're all good
env TMUX= tmux selectw -t frc:$line
# update live/dead status for all; comment out if screen looks glitchy
lds
}
enter() {
window=$1; line=$1
(( line++ )); # line needs to be 1-relative here
cmd="$(cat $RRC | sed -n -e ${line}p | cut -f2- -d' ')"
env TMUX= tmux respawnp -t frc:$window "$ME > $TTY 2>&1 showline $line; $cmd" 2>/dev/null
# the redirect is to avoid ugly "respawn pane failed: pane frc:2.0 still active" messages when you exit, hit ctrl-a, etc
# TODO: does respawnp have other common errors I am missing?
env TMUX= tmux selectw -t frc:$window
lds_up $window "*"
}
showline() {
line=$1
printf "\n+ %s\n\n" "$(cat $RRC | sed -n -e ${line}p | cut -f2- -d' ')" >&2
}
del() {
line=$1
env TMUX= tmux killw -t frc:$line
(( line++ ))
sed -i -e ${line}d $RRC
}
add() {
window=`wc -l < $RRC` # last window
tmux selectp -t 0
echo -n >&2 "name: "
read name
echo -n >&2 "command: "
read cmd
printf "*%s %s\n" "$name" "$cmd" >> $RRC
env TMUX= tmux neww -t $window "$cmd"
}
lds() {
# update "live" or "dead" status of all panes
seq 0 9999 | head -`wc -l < $RRC` | while read n
do
lds_1 $n
done
}
lds_1() {
read pd <<< $(env TMUX= tmux display -p -t frc:$1 "#{pane_dead}")
[[ $pd == 0 ]] && lds_up $1 "*"
[[ $pd == 1 ]] && lds_up $1 ""
}
lds_up() {
line=$1; (( line++ )) # needs to be 1-relative
prefix="$2"
perl -i -pe "s/^\*?/$prefix/ if \$. == $line" $RRC
}
case $1 in
START )
start
;;
* )
"$@"
;;
esac