Fix: subshell capture bug - interactive menus invisible in $()

- All UI output (echo, printf) in interactive functions now goes to stderr (>&2)
- Only return values go to stdout for $() capture
- Affected: select_quick_domain, select_port, select_category, select_template, show_template_preview
- Also fixed: log_*, confirm, select_option in common.sh
This commit is contained in:
anten-ka
2026-04-06 22:05:20 +03:00
parent 84bc15090a
commit cc3f547273
3 changed files with 52 additions and 52 deletions

View File

@@ -37,12 +37,12 @@ DIM='\033[2m'
NC='\033[0m' NC='\033[0m'
# ── Логирование ────────────────────────────────────────────────────────────── # ── Логирование ──────────────────────────────────────────────────────────────
log_info() { echo -e " ${CYAN}${NC} $*"; } log_info() { echo -e " ${CYAN}${NC} $*" >&2; }
log_success() { echo -e " ${GREEN}${NC} $*"; } log_success() { echo -e " ${GREEN}${NC} $*" >&2; }
log_warning() { echo -e " ${YELLOW}${NC} $*"; } log_warning() { echo -e " ${YELLOW}${NC} $*" >&2; }
log_error() { echo -e " ${RED}${NC} $*"; } log_error() { echo -e " ${RED}${NC} $*" >&2; }
log_step() { echo -e "\n${BOLD}${WHITE} $*${NC}"; } log_step() { echo -e "\n${BOLD}${WHITE} $*${NC}" >&2; }
log_dim() { echo -e " ${DIM}$*${NC}"; } log_dim() { echo -e " ${DIM}$*${NC}" >&2; }
log_to_file() { log_to_file() {
local ts; ts=$(date '+%Y-%m-%d %H:%M:%S') local ts; ts=$(date '+%Y-%m-%d %H:%M:%S')
@@ -409,7 +409,7 @@ migrate_v1_to_v2() {
# ── Подтверждение ──────────────────────────────────────────────────────────── # ── Подтверждение ────────────────────────────────────────────────────────────
confirm() { confirm() {
local msg="${1:-Продолжить?}" local msg="${1:-Продолжить?}"
echo -ne " ${msg} [Y/n]: " echo -ne " ${msg} [Y/n]: " >&2
read -r ans read -r ans
[[ ! "$ans" =~ ^[Nn] ]] [[ ! "$ans" =~ ^[Nn] ]]
} }
@@ -420,16 +420,16 @@ select_option() {
shift shift
local options=("$@") local options=("$@")
echo "" echo "" >&2
echo -e " ${BOLD}${WHITE}${title}${NC}" echo -e " ${BOLD}${WHITE}${title}${NC}" >&2
echo -e " ${DIM}$(printf '─%.0s' {1..50})${NC}" echo -e " ${DIM}$(printf '─%.0s' {1..50})${NC}" >&2
local i=1 local i=1
for opt in "${options[@]}"; do for opt in "${options[@]}"; do
echo -e " ${CYAN}${i})${NC} ${opt}" echo -e " ${CYAN}${i})${NC} ${opt}" >&2
((i++)) ((i++))
done done
echo -e " ${DIM}$(printf '─%.0s' {1..50})${NC}" echo -e " ${DIM}$(printf '─%.0s' {1..50})${NC}" >&2
echo -ne " ${WHITE}Выбор:${NC} " echo -ne " ${WHITE}Выбор:${NC} " >&2
read -r choice read -r choice
echo "$choice" echo "$choice"
} }

View File

@@ -160,23 +160,23 @@ validate_telemt_config() {
# ── Выбор домена (интерактивный) ───────────────────────────────────────────── # ── Выбор домена (интерактивный) ─────────────────────────────────────────────
select_quick_domain() { select_quick_domain() {
echo "" echo "" >&2
echo -e " ${BOLD}${WHITE}🌐 Выберите домен для маскировки (Fake TLS):${NC}" echo -e " ${BOLD}${WHITE}🌐 Выберите домен для маскировки (Fake TLS):${NC}" >&2
echo -e " ${DIM}$(printf '─%.0s' {1..50})${NC}" echo -e " ${DIM}$(printf '─%.0s' {1..50})${NC}" >&2
local i=1 local i=1
local row="" local row=""
for d in "${QUICK_DOMAINS[@]}"; do for d in "${QUICK_DOMAINS[@]}"; do
printf " ${CYAN}%2d)${NC} %-25s" "$i" "$d" printf " ${CYAN}%2d)${NC} %-25s" "$i" "$d" >&2
if (( i % 2 == 0 )); then if (( i % 2 == 0 )); then
echo "" echo "" >&2
fi fi
((i++)) ((i++))
done done
if (( (i-1) % 2 != 0 )); then echo ""; fi if (( (i-1) % 2 != 0 )); then echo "" >&2; fi
echo -e " ${DIM}$(printf '─%.0s' {1..50})${NC}" echo -e " ${DIM}$(printf '─%.0s' {1..50})${NC}" >&2
echo -ne " ${WHITE}Выбор (1-${#QUICK_DOMAINS[@]}):${NC} " echo -ne " ${WHITE}Выбор (1-${#QUICK_DOMAINS[@]}):${NC} " >&2
read -r choice read -r choice
if [[ "$choice" =~ ^[0-9]+$ ]] && [ "$choice" -ge 1 ] && [ "$choice" -le ${#QUICK_DOMAINS[@]} ]; then if [[ "$choice" =~ ^[0-9]+$ ]] && [ "$choice" -ge 1 ] && [ "$choice" -le ${#QUICK_DOMAINS[@]} ]; then
@@ -190,8 +190,8 @@ select_quick_domain() {
# ── Выбор порта (интерактивный) ────────────────────────────────────────────── # ── Выбор порта (интерактивный) ──────────────────────────────────────────────
select_port() { select_port() {
echo "" echo "" >&2
echo -e " ${BOLD}${WHITE}🔌 Выберите порт:${NC}" echo -e " ${BOLD}${WHITE}🔌 Выберите порт:${NC}" >&2
# Проверяем стандартные порты # Проверяем стандартные порты
local busy_443 busy_8443 local busy_443 busy_8443
@@ -203,22 +203,22 @@ select_port() {
[ -n "$busy_443" ] && label_443="443 ⚠️ занят" [ -n "$busy_443" ] && label_443="443 ⚠️ занят"
[ -n "$busy_8443" ] && label_8443="8443 ⚠️ занят" [ -n "$busy_8443" ] && label_8443="8443 ⚠️ занят"
echo -e " ${CYAN}1)${NC} $label_443" echo -e " ${CYAN}1)${NC} $label_443" >&2
echo -e " ${CYAN}2)${NC} $label_8443" echo -e " ${CYAN}2)${NC} $label_8443" >&2
echo -e " ${CYAN}3)${NC} Свой порт" echo -e " ${CYAN}3)${NC} Свой порт" >&2
if [ -n "$busy_443" ]; then if [ -n "$busy_443" ]; then
echo -e " ${DIM} ⚠ Порт 443 занят: $(echo "$busy_443" | head -c 60)${NC}" echo -e " ${DIM} ⚠ Порт 443 занят: $(echo "$busy_443" | head -c 60)${NC}" >&2
fi fi
echo -ne " ${WHITE}Выбор:${NC} " echo -ne " ${WHITE}Выбор:${NC} " >&2
read -r choice read -r choice
case "$choice" in case "$choice" in
1) echo "443" ;; 1) echo "443" ;;
2) echo "8443" ;; 2) echo "8443" ;;
3) 3)
echo -ne " Введите порт (1-65535): " echo -ne " Введите порт (1-65535): " >&2
read -r custom_port read -r custom_port
if [[ "$custom_port" =~ ^[0-9]+$ ]] && [ "$custom_port" -ge 1 ] && [ "$custom_port" -le 65535 ]; then if [[ "$custom_port" =~ ^[0-9]+$ ]] && [ "$custom_port" -ge 1 ] && [ "$custom_port" -le 65535 ]; then
echo "$custom_port" echo "$custom_port"

View File

@@ -46,9 +46,9 @@ get_template_field() {
select_category() { select_category() {
load_catalog || return 1 load_catalog || return 1
echo "" echo "" >&2
echo -e " ${BOLD}${WHITE}📂 Категории шаблонов сайтов:${NC}" echo -e " ${BOLD}${WHITE}📂 Категории шаблонов сайтов:${NC}" >&2
echo -e " ${DIM}$(printf '─%.0s' {1..55})${NC}" echo -e " ${DIM}$(printf '─%.0s' {1..55})${NC}" >&2
local cats=() local cats=()
local i=1 local i=1
@@ -67,14 +67,14 @@ select_category() {
chart-bar) emoji="🔧" ;; chart-bar) emoji="🔧" ;;
*) emoji="📄" ;; *) emoji="📄" ;;
esac esac
printf " ${CYAN}%2d)${NC} ${emoji} %-30s ${DIM}(%d шаблонов)${NC}\n" "$i" "$name" "$count" printf " ${CYAN}%2d)${NC} ${emoji} %-30s ${DIM}(%d шаблонов)${NC}\n" "$i" "$name" "$count" >&2
cats+=("$id") cats+=("$id")
((i++)) ((i++))
done < <(get_categories) done < <(get_categories)
printf " ${CYAN}%2d)${NC} 🎲 Случайный шаблон\n" "$i" printf " ${CYAN}%2d)${NC} 🎲 Случайный шаблон\n" "$i" >&2
echo -e " ${DIM}$(printf '─%.0s' {1..55})${NC}" echo -e " ${DIM}$(printf '─%.0s' {1..55})${NC}" >&2
echo -ne " ${WHITE}Выбор:${NC} " echo -ne " ${WHITE}Выбор:${NC} " >&2
read -r choice read -r choice
# Случайный # Случайный
@@ -99,14 +99,14 @@ select_template() {
local cat_name local cat_name
cat_name=$(get_category_name "$cat_id") cat_name=$(get_category_name "$cat_id")
echo "" echo "" >&2
echo -e " ${BOLD}${WHITE}📋 $cat_name — доступные шаблоны:${NC}" echo -e " ${BOLD}${WHITE}📋 $cat_name — доступные шаблоны:${NC}" >&2
echo -e " ${DIM}$(printf '─%.0s' {1..60})${NC}" echo -e " ${DIM}$(printf '─%.0s' {1..60})${NC}" >&2
local tpls=() local tpls=()
local i=1 local i=1
while IFS='|' read -r id name source preview; do while IFS='|' read -r id name source preview; do
printf " ${CYAN}%2d)${NC} %-30s ${DIM}[%s]${NC}\n" "$i" "$name" "$source" printf " ${CYAN}%2d)${NC} %-30s ${DIM}[%s]${NC}\n" "$i" "$name" "$source" >&2
tpls+=("$id") tpls+=("$id")
((i++)) ((i++))
done < <(get_templates_by_category "$cat_id") done < <(get_templates_by_category "$cat_id")
@@ -116,8 +116,8 @@ select_template() {
return 1 return 1
fi fi
echo -e " ${DIM}$(printf '─%.0s' {1..60})${NC}" echo -e " ${DIM}$(printf '─%.0s' {1..60})${NC}" >&2
echo -ne " ${WHITE}Выбор (1-$((i-1))):${NC} " echo -ne " ${WHITE}Выбор (1-$((i-1))):${NC} " >&2
read -r choice read -r choice
if [[ "$choice" =~ ^[0-9]+$ ]] && [ "$choice" -ge 1 ] && [ "$choice" -lt "$i" ]; then if [[ "$choice" =~ ^[0-9]+$ ]] && [ "$choice" -ge 1 ] && [ "$choice" -lt "$i" ]; then
@@ -147,21 +147,21 @@ show_template_preview() {
repo_url=$(echo "$info" | jq -r '.repo_url // empty') repo_url=$(echo "$info" | jq -r '.repo_url // empty')
description=$(echo "$info" | jq -r '.description // "—"') description=$(echo "$info" | jq -r '.description // "—"')
echo "" echo "" >&2
echo -e " ${BOLD}${WHITE}🔍 Превью шаблона:${NC}" echo -e " ${BOLD}${WHITE}🔍 Превью шаблона:${NC}" >&2
echo -e " ${DIM}$(printf '─%.0s' {1..55})${NC}" echo -e " ${DIM}$(printf '─%.0s' {1..55})${NC}" >&2
echo -e " ${WHITE}Название:${NC} $name" echo -e " ${WHITE}Название:${NC} $name" >&2
echo -e " ${WHITE}Источник:${NC} $source" echo -e " ${WHITE}Источник:${NC} $source" >&2
echo -e " ${WHITE}Описание:${NC} $description" echo -e " ${WHITE}Описание:${NC} $description" >&2
if [ -n "$preview_url" ]; then if [ -n "$preview_url" ]; then
echo "" echo "" >&2
echo -e " ${GREEN}👁 Превью:${NC} ${CYAN}${preview_url}${NC}" echo -e " ${GREEN}👁 Превью:${NC} ${CYAN}${preview_url}${NC}" >&2
echo -e " ${DIM}Откройте ссылку в браузере для просмотра шаблона${NC}" echo -e " ${DIM}Откройте ссылку в браузере для просмотра шаблона${NC}" >&2
fi fi
if [ -n "$repo_url" ]; then if [ -n "$repo_url" ]; then
echo -e " ${DIM}📦 Репо: ${repo_url}${NC}" echo -e " ${DIM}📦 Репо: ${repo_url}${NC}" >&2
fi fi
# Благодарность автору # Благодарность автору