Ultima attività 1 month ago

cmdctl е интерактивен мениджър за полезни команди с красив, модерен интерфейс.

Revisione ac66d9547cb5e6960bd9ef1e47fc9f1832cf703b

cmdctl Raw
1#!/usr/bin/env bash
2
3COMMANDS_FILE="$HOME/.fedya_commands"
4EDITOR_CMD="${EDITOR:-nano}"
5
6# Модерна цветна палитра
7C_RESET=$'\033[0m'
8C_BOLD=$'\033[1m'
9C_DIM=$'\033[2m'
10
11# Основни цветове
12C_PRIMARY=$'\033[38;5;81m' # Светло синьо
13C_SUCCESS=$'\033[38;5;82m' # Зелено
14C_WARNING=$'\033[38;5;214m' # Оранжево
15C_ERROR=$'\033[38;5;196m' # Червено
16C_INFO=$'\033[38;5;147m' # Лавандула
17
18# Акцентни цветове
19C_TITLE=$'\033[38;5;213m' # Розово-лилаво
20C_KEY=$'\033[38;5;226m' # Жълто
21C_VAL=$'\033[38;5;153m' # Светло синьо
22C_DESC=$'\033[38;5;249m' # Светло сиво
23C_BORDER=$'\033[38;5;240m' # Сиво
24
25# Box drawing символи
26BOX_TL="╭"
27BOX_TR="╮"
28BOX_BL="╰"
29BOX_BR="╯"
30BOX_H="─"
31BOX_V="│"
32BOX_VR="├"
33BOX_VL="┤"
34
35ensure_file() {
36 [ -f "$COMMANDS_FILE" ] || touch "$COMMANDS_FILE"
37}
38
39print_header() {
40 local width=70
41 clear
42 echo
43 printf "%b" "${C_PRIMARY}${C_BOLD}"
44 printf "%s%${width}s%s\n" "$BOX_TL" "" "$BOX_TR" | tr ' ' "$BOX_H"
45 printf "%s%-${width}s%s\n" "$BOX_V" " ⚡ CMDCTL - Мениджър за Полезни Команди" "$BOX_V"
46 printf "%s%${width}s%s\n" "$BOX_BL" "" "$BOX_BR" | tr ' ' "$BOX_H"
47 printf "%b\n" "${C_RESET}"
48}
49
50list_commands() {
51 ensure_file
52 print_header
53
54 local count=$(grep -c '^#CMD:' "$COMMANDS_FILE" 2>/dev/null || echo 0)
55
56 printf "%b%s %s%b\n" "${C_INFO}${C_BOLD}" "📦 Общо команди:" "$count" "${C_RESET}"
57 printf "%b%s%b\n\n" "${C_DIM}" "Файл: $COMMANDS_FILE" "${C_RESET}"
58
59 if [ "$count" -eq 0 ]; then
60 printf "%b%s%b\n\n" "${C_WARNING}" " ⚠️ Няма запазени команди" "${C_RESET}"
61 else
62 local i=1
63 while IFS= read -r line; do
64 if [[ "$line" =~ ^#CMD: ]]; then
65 local name="${line#\#CMD: }"
66 read -r desc
67 desc="${desc#\#DESC: }"
68 read -r cmd
69
70 printf "%b[%d]%b %b%s%b\n" "${C_KEY}${C_BOLD}" "$i" "${C_RESET}" "${C_PRIMARY}${C_BOLD}" "$name" "${C_RESET}"
71 printf " %b%s%b\n" "${C_DESC}${C_DIM}" "$desc" "${C_RESET}"
72 printf " %b➜%b %b%s%b\n\n" "${C_SUCCESS}" "${C_RESET}" "${C_VAL}" "$cmd" "${C_RESET}"
73
74 ((i++))
75 fi
76 done < "$COMMANDS_FILE"
77 fi
78}
79
80add_command() {
81 ensure_file
82 print_header
83
84 printf "%b%s%b\n\n" "${C_SUCCESS}${C_BOLD}" "➕ Добавяне на нова команда" "${C_RESET}"
85
86 printf "%b%s%b\n" "${C_DIM}" "Съвет: Използвай кратко и ясно име (напр: mikrotik-backup, ssh-keygen)" "${C_RESET}"
87 printf "%b%s%b" "${C_INFO}" "Име на командата: " "${C_RESET}"
88 read -r name
89
90 if [ -z "$name" ]; then
91 printf "\n%b%s%b\n" "${C_ERROR}" "❌ Името не може да бъде празно!" "${C_RESET}"
92 sleep 2
93 return
94 fi
95
96 # Проверка дали съществува
97 if grep -q "^#CMD: $name$" "$COMMANDS_FILE" 2>/dev/null; then
98 printf "\n%b%s%b\n" "${C_ERROR}" "❌ Команда '$name' вече съществува!" "${C_RESET}"
99 sleep 2
100 return
101 fi
102
103 printf "\n%b%s%b\n" "${C_DIM}" "Напиши кратко описание какво прави командата" "${C_RESET}"
104 printf "%b%s%b" "${C_INFO}" "Описание: " "${C_RESET}"
105 read -r desc
106
107 if [ -z "$desc" ]; then
108 printf "\n%b%s%b\n" "${C_ERROR}" "❌ Описанието не може да бъде празно!" "${C_RESET}"
109 sleep 2
110 return
111 fi
112
113 printf "\n%b%s%b\n" "${C_DIM}" "Въведи пълната команда (може да е многоредова)" "${C_RESET}"
114 printf "%b%s%b" "${C_INFO}" "Команда: " "${C_RESET}"
115 read -r cmd
116
117 if [ -z "$cmd" ]; then
118 printf "\n%b%s%b\n" "${C_ERROR}" "❌ Командата не може да бъде празна!" "${C_RESET}"
119 sleep 2
120 return
121 fi
122
123 # Запазване
124 {
125 echo "#CMD: $name"
126 echo "#DESC: $desc"
127 echo "$cmd"
128 echo ""
129 } >> "$COMMANDS_FILE"
130
131 printf "\n%b%s%b %b%s%b\n" "${C_SUCCESS}${C_BOLD}" "✅ Успешно добавена:" "${C_RESET}" "${C_KEY}" "$name" "${C_RESET}"
132 sleep 2
133}
134
135remove_command() {
136 ensure_file
137 print_header
138
139 printf "%b%s%b\n\n" "${C_WARNING}${C_BOLD}" "🗑️ Премахване на команда" "${C_RESET}"
140
141 local count=$(grep -c '^#CMD:' "$COMMANDS_FILE" 2>/dev/null || echo 0)
142 if [ "$count" -eq 0 ]; then
143 printf "%b%s%b\n" "${C_WARNING}" "⚠️ Няма команди за премахване" "${C_RESET}"
144 sleep 2
145 return
146 fi
147
148 printf "%b%s%b" "${C_INFO}" "Име на командата за премахване: " "${C_RESET}"
149 read -r name
150
151 if [ -z "$name" ]; then
152 printf "\n%b%s%b\n" "${C_ERROR}" "❌ Не е въведено име!" "${C_RESET}"
153 sleep 2
154 return
155 fi
156
157 if grep -q "^#CMD: $name$" "$COMMANDS_FILE" 2>/dev/null; then
158 # Изтриване на командата и следващите 3 реда (описание, команда, празен ред)
159 sed -i "/^#CMD: $name$/,+3d" "$COMMANDS_FILE"
160 printf "\n%b%s%b %b%s%b\n" "${C_SUCCESS}${C_BOLD}" "✅ Премахната:" "${C_RESET}" "${C_KEY}" "$name" "${C_RESET}"
161 else
162 printf "\n%b%s%b\n" "${C_ERROR}" "❌ Команда '$name' не съществува!" "${C_RESET}"
163 fi
164 sleep 2
165}
166
167view_command() {
168 ensure_file
169 print_header
170
171 printf "%b%s%b\n\n" "${C_INFO}${C_BOLD}" "🔍 Преглед на конкретна команда" "${C_RESET}"
172
173 local count=$(grep -c '^#CMD:' "$COMMANDS_FILE" 2>/dev/null || echo 0)
174 if [ "$count" -eq 0 ]; then
175 printf "%b%s%b\n" "${C_WARNING}" "⚠️ Няма запазени команди" "${C_RESET}"
176 sleep 2
177 return
178 fi
179
180 printf "%b%s%b" "${C_INFO}" "Име на командата: " "${C_RESET}"
181 read -r name
182
183 if [ -z "$name" ]; then
184 printf "\n%b%s%b\n" "${C_ERROR}" "❌ Не е въведено име!" "${C_RESET}"
185 sleep 2
186 return
187 fi
188
189 local found=0
190 while IFS= read -r line; do
191 if [[ "$line" == "#CMD: $name" ]]; then
192 found=1
193 echo
194 printf "%b%s%b\n" "${C_PRIMARY}${C_BOLD}" "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" "${C_RESET}"
195 printf "%b%s%b %b%s%b\n" "${C_KEY}${C_BOLD}" "Име:" "${C_RESET}" "${C_PRIMARY}" "$name" "${C_RESET}"
196
197 read -r desc
198 desc="${desc#\#DESC: }"
199 printf "%b%s%b %b%s%b\n" "${C_KEY}${C_BOLD}" "Описание:" "${C_RESET}" "${C_DESC}" "$desc" "${C_RESET}"
200
201 read -r cmd
202 printf "%b%s%b\n" "${C_KEY}${C_BOLD}" "Команда:" "${C_RESET}"
203 printf " %b%s%b\n" "${C_VAL}" "$cmd" "${C_RESET}"
204 printf "%b%s%b\n\n" "${C_PRIMARY}${C_BOLD}" "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" "${C_RESET}"
205
206 printf "%b%s%b" "${C_WARNING}" "Копирай командата в клипборда? (y/n): " "${C_RESET}"
207 read -r copy
208 if [[ "$copy" == "y" || "$copy" == "Y" ]]; then
209 if command -v xclip >/dev/null 2>&1; then
210 echo "$cmd" | xclip -selection clipboard
211 printf "%b%s%b\n" "${C_SUCCESS}" "✅ Копирано в клипборда!" "${C_RESET}"
212 elif command -v xsel >/dev/null 2>&1; then
213 echo "$cmd" | xsel --clipboard
214 printf "%b%s%b\n" "${C_SUCCESS}" "✅ Копирано в клипборда!" "${C_RESET}"
215 else
216 printf "%b%s%b\n" "${C_WARNING}" "⚠️ xclip/xsel не са инсталирани" "${C_RESET}"
217 fi
218 fi
219 break
220 fi
221 done < "$COMMANDS_FILE"
222
223 if [ "$found" -eq 0 ]; then
224 printf "\n%b%s%b\n" "${C_ERROR}" "❌ Команда '$name' не съществува!" "${C_RESET}"
225 fi
226
227 echo
228 read -rp "Натисни Enter за продължаване..."
229}
230
231edit_commands() {
232 ensure_file
233 "$EDITOR_CMD" "$COMMANDS_FILE"
234}
235
236fzf_menu() {
237 if ! command -v fzf >/dev/null; then
238 print_header
239 printf "%b%s%b\n" "${C_ERROR}" "❌ fzf не е инсталиран!" "${C_RESET}"
240 printf "%b%s%b\n" "${C_INFO}" "Инсталирай с: sudo dnf install fzf" "${C_RESET}"
241 sleep 3
242 return
243 fi
244
245 ensure_file
246
247 # Проверка дали има команди
248 local count=$(grep -c '^#CMD:' "$COMMANDS_FILE" 2>/dev/null || echo 0)
249 if [ "$count" -eq 0 ]; then
250 print_header
251 printf "%b%s%b\n" "${C_WARNING}" "⚠️ Няма запазени команди за преглед" "${C_RESET}"
252 sleep 2
253 return
254 fi
255
256 # Подготовка: Създаваме два файла - един за display, един за команди
257 local display_file=$(mktemp)
258 local cmd_file=$(mktemp)
259 local line_num=0
260
261 while IFS= read -r line; do
262 if [[ "$line" =~ ^#CMD: ]]; then
263 local name="${line#\#CMD: }"
264 IFS= read -r desc_line
265 local desc="${desc_line#\#DESC: }"
266 IFS= read -r cmd_line
267
268 # Display file - това виждаме в списъка
269 printf "%3d) %-25s - %s\n" "$((++line_num))" "$name" "$desc" >> "$display_file"
270
271 # Command file - съхраняваме командата на същия ред номер
272 echo "$cmd_line" >> "$cmd_file"
273 fi
274 done < "$COMMANDS_FILE"
275
276 # FZF избор
277 local selected=$(cat "$display_file" | fzf \
278 --height=60% \
279 --border=rounded \
280 --prompt="🔍 Търси: " \
281 --header="↑↓ Навигация | Enter = Избор | ESC = Изход" \
282 --color="fg:#d0d0d0,bg:#1e1e1e,hl:#5fd7ff" \
283 --color="fg+:#ffffff,bg+:#2e2e2e,hl+:#ffaf00" \
284 --color="info:#af87ff,prompt:#5fd7ff,pointer:#ff87d7" \
285 --color="marker:#87ff00,spinner:#ff87d7,header:#87afaf")
286
287 if [ -n "$selected" ]; then
288 # Извличаме номера на реда
289 local num=$(echo "$selected" | awk '{print $1}' | tr -d ')')
290
291 # Взимаме командата от command file
292 local cmd=$(sed -n "${num}p" "$cmd_file")
293
294 rm -f "$display_file" "$cmd_file"
295
296 print_header
297 printf "%b%s%b\n\n" "${C_SUCCESS}${C_BOLD}" "✅ Избрана команда:" "${C_RESET}"
298 printf "%b%s%b\n\n" "${C_VAL}" "$cmd" "${C_RESET}"
299
300 printf "%b%s%b" "${C_WARNING}" "Копирай в клипборда? (y/n): " "${C_RESET}"
301 read -r copy
302 if [[ "$copy" == "y" || "$copy" == "Y" ]]; then
303 if command -v xclip >/dev/null 2>&1; then
304 echo "$cmd" | xclip -selection clipboard
305 printf "\n%b%s%b\n" "${C_SUCCESS}" "✅ Копирано в клипборда!" "${C_RESET}"
306 sleep 1
307 elif command -v xsel >/dev/null 2>&1; then
308 echo "$cmd" | xsel --clipboard
309 printf "\n%b%s%b\n" "${C_SUCCESS}" "✅ Копирано в клипборда!" "${C_RESET}"
310 sleep 1
311 else
312 printf "\n%b%s%b\n" "${C_WARNING}" "⚠️ Инсталирай xclip или xsel за копиране" "${C_RESET}"
313 sleep 2
314 fi
315 fi
316
317 echo
318 read -rp "Натисни Enter за продължаване..."
319 else
320 rm -f "$display_file" "$cmd_file"
321 fi
322}
323
324search_commands() {
325 ensure_file
326 print_header
327
328 printf "%b%s%b\n\n" "${C_INFO}${C_BOLD}" "🔎 Търсене в командите" "${C_RESET}"
329
330 printf "%b%s%b" "${C_INFO}" "Търси (име/описание/команда): " "${C_RESET}"
331 read -r search_term
332
333 if [ -z "$search_term" ]; then
334 printf "\n%b%s%b\n" "${C_ERROR}" "❌ Въведи текст за търсене!" "${C_RESET}"
335 sleep 2
336 return
337 fi
338
339 echo
340 printf "%b%s%b\n\n" "${C_SUCCESS}" "Резултати за: $search_term" "${C_RESET}"
341
342 local found=0
343 while IFS= read -r line; do
344 if [[ "$line" =~ ^#CMD: ]]; then
345 local name="${line#\#CMD: }"
346 read -r desc
347 desc="${desc#\#DESC: }"
348 read -r cmd
349
350 if [[ "$name" =~ $search_term ]] || [[ "$desc" =~ $search_term ]] || [[ "$cmd" =~ $search_term ]]; then
351 found=1
352 printf "%b●%b %b%s%b\n" "${C_SUCCESS}" "${C_RESET}" "${C_PRIMARY}${C_BOLD}" "$name" "${C_RESET}"
353 printf " %b%s%b\n" "${C_DESC}${C_DIM}" "$desc" "${C_RESET}"
354 printf " %b➜%b %b%s%b\n\n" "${C_KEY}" "${C_RESET}" "${C_VAL}" "$cmd" "${C_RESET}"
355 fi
356 fi
357 done < "$COMMANDS_FILE"
358
359 if [ "$found" -eq 0 ]; then
360 printf "%b%s%b\n" "${C_WARNING}" "⚠️ Няма намерени резултати" "${C_RESET}"
361 fi
362
363 echo
364 read -rp "Натисни Enter за продължаване..."
365}
366
367show_help() {
368 print_header
369
370 printf "%b%s%b\n\n" "${C_INFO}${C_BOLD}" "📖 Помощна информация" "${C_RESET}"
371
372 printf "%b%s%b\n" "${C_PRIMARY}" "Файл с команди:" "${C_RESET}"
373 printf " %b%s%b\n\n" "${C_VAL}" "$COMMANDS_FILE" "${C_RESET}"
374
375 printf "%b%s%b\n" "${C_PRIMARY}" "Редактор:" "${C_RESET}"
376 printf " %b%s%b\n\n" "${C_VAL}" "$EDITOR_CMD" "${C_RESET}"
377
378 printf "%b%s%b\n" "${C_PRIMARY}" "Формат на командите:" "${C_RESET}"
379 printf " %b%s%b\n" "${C_DESC}" "Всяка команда се съхранява с име, описание и самата команда" "${C_RESET}"
380 printf " %b%s%b\n\n" "${C_DESC}" "Можете да търсите, прегледате и копирате команди лесно" "${C_RESET}"
381
382 printf "%b%s%b\n" "${C_PRIMARY}" "Препоръки:" "${C_RESET}"
383 printf " %b%s%b\n" "${C_DESC}" "• Използвай кратки и ясни имена" "${C_RESET}"
384 printf " %b%s%b\n" "${C_DESC}" "• Пиши подробни описания" "${C_RESET}"
385 printf " %b%s%b\n" "${C_DESC}" "• Групирай сходни команди с общ префикс (напр: mikrotik-*, docker-*)" "${C_RESET}"
386 printf " %b%s%b\n\n" "${C_DESC}" "• За копиране в клипборда инсталирай xclip или xsel" "${C_RESET}"
387
388 read -rp "Натисни Enter за продължаване..."
389}
390
391main_menu() {
392 print_header
393
394 printf "%b%s%b\n" "${C_BORDER}${C_DIM}" "Избери опция:" "${C_RESET}"
395 echo
396
397 printf " %b[1]%b 📋 Преглед на всички команди\n" "${C_KEY}${C_BOLD}" "${C_RESET}"
398 printf " %b[2]%b ➕ Добавяне на нова команда\n" "${C_KEY}${C_BOLD}" "${C_RESET}"
399 printf " %b[3]%b 🔍 Преглед на конкретна команда\n" "${C_KEY}${C_BOLD}" "${C_RESET}"
400 printf " %b[4]%b 🔎 Търсене в командите\n" "${C_KEY}${C_BOLD}" "${C_RESET}"
401 printf " %b[5]%b 🗑️ Премахване на команда\n" "${C_KEY}${C_BOLD}" "${C_RESET}"
402 printf " %b[6]%b ✏️ Редактиране на файла\n" "${C_KEY}${C_BOLD}" "${C_RESET}"
403 printf " %b[7]%b ⚡ FZF интерактивен преглед\n" "${C_KEY}${C_BOLD}" "${C_RESET}"
404 printf " %b[8]%b 📖 Помощ\n" "${C_KEY}${C_BOLD}" "${C_RESET}"
405 echo
406 printf " %b[q]%b 🚪 Изход\n" "${C_ERROR}${C_BOLD}" "${C_RESET}"
407 echo
408
409 printf "%b%s%b " "${C_PRIMARY}" "➤" "${C_RESET}"
410 read -r choice
411
412 case "$choice" in
413 1) list_commands; read -rp "Натисни Enter за продължаване...";;
414 2) add_command;;
415 3) view_command;;
416 4) search_commands;;
417 5) remove_command;;
418 6) edit_commands;;
419 7) fzf_menu;;
420 8) show_help;;
421 q|Q)
422 clear
423 printf "\n%b%s%b\n\n" "${C_SUCCESS}${C_BOLD}" "👋 Довиждане!" "${C_RESET}"
424 exit 0
425 ;;
426 *)
427 printf "\n%b%s%b\n" "${C_ERROR}" "❌ Невалидна опция!" "${C_RESET}"
428 sleep 1
429 ;;
430 esac
431}
432
433# Главен цикъл
434while true; do
435 main_menu
436done