Ostatnio aktywny 1 month ago

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

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