commit 3002777e4e001441a39dbc88a2fd9e10ce30fe76 Author: PhatPhuckDave Date: Fri Nov 21 00:39:45 2025 +0100 Add tmux scripts diff --git a/tmux-daemon.sh b/tmux-daemon.sh new file mode 100644 index 0000000..fc174e3 --- /dev/null +++ b/tmux-daemon.sh @@ -0,0 +1,153 @@ +#!/bin/bash +# tmux-daemon.sh +# Idempotent tmux daemon manager with config file support + +# Load config file if it exists +SCRIPT_DIR=$(dirname "$0") +SCRIPT_NAME=$(basename "$0" .sh) +CONFIG_FILE="${SCRIPT_DIR}/${SCRIPT_NAME}.sh.conf" +if [ ! -f "$CONFIG_FILE" ]; then + CONFIG_FILE="${SCRIPT_DIR}/${SCRIPT_NAME}.conf" +fi +if [ -f "$CONFIG_FILE" ]; then + echo "Loading config from $CONFIG_FILE" + source "$CONFIG_FILE" +else + CONFIG_FILE="${SCRIPT_DIR}/${SCRIPT_NAME}.sh.conf" + echo "Config file $CONFIG_FILE not found, generating template..." + cat > "$CONFIG_FILE" << 'EOF' +# tmux-daemon.conf +# Configuration file for tmux-daemon.sh + +# Session name +SESSION="example" + +# Whether to attach to session after setup (0 or 1) +ATTACH_SESSION=0 + +# Commands to run (array format) +# Format: "workdir:::command" OR just "command" +CMDS=( + "ping google.com" + "ping google.com" + "ping google.com" + "ping google.com" + "ping google.com" + "ping google.com" + "ping google.com" +) +EOF + echo "Generated $CONFIG_FILE with default values. Please edit and run again." + exit 0 +fi + +# Validate required variables +if [ -z "$SESSION" ]; then + echo "Error: SESSION must be set in $CONFIG_FILE" >&2 + exit 1 +fi + +if [ ${#CMDS[@]} -eq 0 ]; then + echo "Error: CMDS array must be set in $CONFIG_FILE" >&2 + exit 1 +fi + +echo "[main] SESSION: $SESSION" +echo "[main] Commands: ${CMDS[*]}" + +# Create session if missing +if ! tmux has-session -t $SESSION 2>/dev/null; then + echo "[main] Creating tmux session: $SESSION" + tmux new-session -d -s $SESSION -n win0 +else + echo "[main] Session $SESSION exists, reusing..." +fi + +win=0 +pane=0 + +for i in "${!CMDS[@]}"; do + entry="${CMDS[$i]}" + echo "[loop:$i] entry: $entry" + if [[ "$entry" == *":::"* ]]; then + workdir="${entry%%:::*}" + echo "[loop:$i] workdir set from entry: $workdir" + cmd="${entry#*:::}" + echo "[loop:$i] cmd set from entry: $cmd" + else + workdir="$PWD" + echo "[loop:$i] workdir set to PWD: $workdir" + cmd="$entry" + echo "[loop:$i] cmd set to entry: $cmd" + fi + echo "[loop:$i] Processing command: $cmd (workdir: $workdir)" + + # Determine target window/pane + win=$((i / 4)) + echo "[loop:$i] win updated: $win" + pane=$((i % 4)) + echo "[loop:$i] pane updated: $pane" + + # Create window if missing + if ! tmux list-windows -t $SESSION | grep -q "^$win:"; then + echo "[loop:$i] Creating window $win" + tmux new-window -t $SESSION:$win -n win$win + fi + + # Get current pane count in window + panes=($(tmux list-panes -t $SESSION:$win -F '#P')) + echo "[loop:$i] panes in window $win: ${panes[*]}" + + # Create missing pane if needed + if [ $pane -ge ${#panes[@]} ]; then + echo "[loop:$i] Splitting pane $((pane-1)) to create pane $pane in window $win" + if [ $pane -eq 1 ]; then + tmux split-window -h -t $SESSION:$win + elif [ $pane -eq 2 ]; then + tmux split-window -v -t $SESSION:$win + elif [ $pane -eq 3 ]; then + tmux split-window -v -t $SESSION:$win.1 + fi + fi + + # Always apply appropriate layout based on current pane count + current_panes=($(tmux list-panes -t $SESSION:$win -F '#P')) + if [ ${#current_panes[@]} -eq 2 ]; then + tmux select-layout -t $SESSION:$win even-horizontal + else + tmux select-layout -t $SESSION:$win tiled + fi + + # Check if command is already running in the pane + running=$(tmux list-panes -t $SESSION:$win -F '#{pane_current_command}' | sed -n "$((pane+1))p") + echo "[loop:$i] running in pane $pane: $running" + if [[ "$running" == "$(basename "$cmd" | awk '{print $1}')" ]]; then + echo "[loop:$i] Command already running in pane $pane, skipping..." + continue + fi + + # Create temporary script to run command in auto-restart loop + tmpfile=$(mktemp) + echo "[loop:$i] tmpfile created: $tmpfile" + workdir_escaped=$(printf %q "$workdir") + { + printf '#!/bin/bash\n' + printf 'cd %s || exit 1\n' "$workdir_escaped" + printf 'while true; do\n' + printf " echo '[tmux-daemon] Starting: %s (in %s)'\n" "$cmd" "$workdir" + printf ' %s\n' "$cmd" + printf " echo '[tmux-daemon] Command exited with code \$?']\n" + printf ' sleep 2\n' + printf 'done\n' + } >"$tmpfile" + chmod +x "$tmpfile" + echo "[loop:$i] Sending command to pane $pane in window $win" + tmux send-keys -t $SESSION:$win.$pane "$tmpfile" C-m +done + +echo "[main] Done." + +if [ $ATTACH_SESSION -eq 1 ]; then + echo "[main] Attaching to tmux session: $SESSION" + tmux attach -t $SESSION +fi \ No newline at end of file diff --git a/tmux-oneshot.sh b/tmux-oneshot.sh new file mode 100644 index 0000000..b6ac477 --- /dev/null +++ b/tmux-oneshot.sh @@ -0,0 +1,117 @@ +#!/bin/bash +# tmux-oneshot.sh +# Run a single command in a tmux session + +# Built-in prompt patterns (command_pattern:prompt_pattern:timeout_seconds) +PROMPT_PATTERNS=( + "ssh*:Password|password:10" + "sudo*:Password|password|\\[sudo\\] password for:10" + "su*:Password|password:10" +) + +# Generic function to wait for prompts based on command patterns +wait_for_prompt() { + local cmd="$1" + + # Find matching pattern for this command + for pattern in "${PROMPT_PATTERNS[@]}"; do + IFS=':' read -r cmd_pattern prompt_pattern timeout <<< "$pattern" + if [[ "$cmd" == $cmd_pattern ]]; then + echo "Waiting for prompt: $prompt_pattern (timeout: ${timeout}s)" + timeout=$((timeout * 10)) # Convert to 0.1s intervals + while [ $timeout -gt 0 ] && ! tmux capture-pane -t $SESSION -p | tail -3 | grep -E "($prompt_pattern)" >/dev/null 2>&1; do + sleep 0.1 + timeout=$((timeout - 1)) + done + if [ $timeout -eq 0 ]; then + echo "Timeout waiting for prompt: $prompt_pattern" + fi + return + fi + done + + # No pattern matched, brief pause + sleep 0.5 +} + +# Load config file if it exists +SCRIPT_DIR=$(dirname "$0") +SCRIPT_NAME=$(basename "$0" .sh) +CONFIG_FILE="${SCRIPT_DIR}/${SCRIPT_NAME}.sh.conf" +if [ ! -f "$CONFIG_FILE" ]; then + CONFIG_FILE="${SCRIPT_DIR}/${SCRIPT_NAME}.conf" +fi +if [ -f "$CONFIG_FILE" ]; then + echo "Loading config from $CONFIG_FILE" + source "$CONFIG_FILE" +else + CONFIG_FILE="${SCRIPT_DIR}/${SCRIPT_NAME}.sh.conf" + echo "Config file $CONFIG_FILE not found, generating template..." + cat > "$CONFIG_FILE" << 'EOF' +# tmux-oneshot.conf +# Configuration file for tmux-oneshot.sh + +# Session name (leave empty to use first word of command) +SESSION="" + +# For multiple commands, use an array: +# COMMANDS=("ssh root@server" "sudo su" "password123") +COMMANDS=() + +# Whether to attach to session after running command (0 or 1) +ATTACH_SESSION=0 +EOF + echo "Generated $CONFIG_FILE with default values. Please edit and run again." + exit 0 +fi + +# Validate required variables +if [ -z "$SESSION" ] && [ ${#COMMANDS[@]} -eq 0 ]; then + echo "Error: Either SESSION or COMMANDS must be set in $CONFIG_FILE" >&2 + exit 1 +fi + +# Use command line arguments if provided, otherwise use config +if [ $# -gt 0 ]; then + COMMANDS=("$@") + echo "Using command line arguments: $*" +else + if [ ${#COMMANDS[@]} -eq 0 ]; then + echo "Error: No commands provided and no commands set in config" >&2 + exit 1 + fi + echo "Using config commands: ${COMMANDS[*]}" +fi + +# Create session if missing +if ! tmux has-session -t $SESSION 2>/dev/null; then + echo "Creating tmux session: $SESSION" + tmux new-session -d -s $SESSION +else + echo "Session $SESSION exists, reusing..." +fi + +# Send commands individually with proper timing +echo "Running commands: ${COMMANDS[*]}" +for i in "${!COMMANDS[@]}"; do + cmd="${COMMANDS[$i]}" + echo "Running: $cmd" + tmpfile=$(mktemp) + { + printf '#!/bin/bash\n' + printf '%s\n' "$cmd" + } >"$tmpfile" + chmod +x "$tmpfile" + tmux send-keys -t $SESSION -l "$tmpfile" + tmux send-keys -t $SESSION Enter + # Only wait if there is a subsequent command + if [ $i -lt $((${#COMMANDS[@]} - 1)) ]; then + # Wait for command to complete or prompt for input with timeout + wait_for_prompt "$cmd" + fi +done + +if [ $ATTACH_SESSION -eq 1 ]; then + echo "Attaching to tmux session: $SESSION" + tmux attach -t $SESSION +fi \ No newline at end of file