Bash 子脚本 - 计时器可通过信号中断 - 读取、超时和睡眠不起作用

问题描述 投票:0回答:1

所以,我在 Arch Linux 上运行 i3 我有一个幻灯片脚本,每 5 秒更改一次背景。我有一堆信号,例如切换、下一个、上一个、更快、更慢。我将所有信号脚本绑定到一个键。

启动/终止幻灯片也绑定到一个键。事实上,我有一个父脚本列表,它可以实现

  1. 杀死所有其他父脚本
  2. 开始播放此目录的幻灯片。

这允许我使用不同的键开始不同的幻灯片。

我有一个主循环,在它的末尾,我想在循环重新运行之前放置一个“wait”$timer 命令并转到下一个循环。

但是我希望这个“等待”命令可以被其他脚本发送的信号中断。

睡眠不会被中断,所以除非我循环运行睡眠0.00001s,这会杀死信号点,否则当我按下一个或上一个时,它不会实时做出反应。

终端中的超时可以执行,但在脚本内,它甚至无法执行 : 命令,它缺少权限。

read -t $timer 从终端启动时运行良好 ,但一旦从其他事情开始,它就崩溃了。

我尝试从子 shell 中读取,读取 << /dev/null, none of it worked.

这就像如果读,读到一个空集,而不是真正什么都没有。

我不知道人们如何在脚本中将信号与计时器混合,但我确信它一定在 bash 的能力范围内。

我希望得到任何帮助。

这是代码。

#!/bin/bash
notify-send "Started Slideshow" -t 3000


timer=5 # seconds


# Function to change background using feh
change_background() {
    feh --bg-center --bg-fill --no-fehbg "$1"
}

# Default directory containing original images (if no path provided)
default_image_dir="Cool Pictures"

# Function to handle signals and manage natural cycling
signal_handler() {
    case $1 in
        "next")
            printf "Received next signal\n\n"
            next_image=true
            ;;
        "previous")
            printf "Received previous signal\n\n"
            prev_image=true
            ;;
        "toggle")
            notify-send 1
            if [ $slideshow_playing = true ] ; then
                slideshow_playing=false
            else
                slideshow_playing=true
            fi
            printf "Received toggle signal\n"
            printf "The slideshow is playing? $slideshow_playing \n\n"
            ;;

        "faster")
            printf "Recieved faster signal\n\n"
            timer=$(echo "$timer / 1.1" | bc -l)
            echo "new timer is $timer"
            ;;

        "slower")
            echo "Recieved slower signal"
            timer=$(echo "$timer * 1.1" | bc -l)
            echo "new timer is $timer"
            ;;
        *)
            echo "Unknown signal: $1"
            ;;
    esac
}


time_step=0.1
wait_until_timer() {
    local start_time=$(date +%s)
    local timer=$1
    local current_time
    local elapsed_time

    while true; do
        current_time=$(date +%s)
        elapsed_time=$((current_time - start_time))

        if [ $elapsed_time -ge $timer ]; then
            break
        fi

        sleep $time_step  # Adjust sleep duration as needed
    done
}


# Set up signal handlers
trap 'signal_handler "next"'     SIGRTMIN+1
trap 'signal_handler "previous"' SIGRTMIN+2
trap 'signal_handler "toggle"'   SIGRTMIN+3
trap 'signal_handler "faster"'   SIGRTMIN+4
trap 'signal_handler "slower"'   SIGRTMIN+5


# Arguments and directories setup
echo "arg 0: $0"
echo "arg 1: $1"
echo "arg 2: $2"
echo "arg 3: $3"

resized_dir=$2
true_dir="$HOME/Pictures/Desktop Background Slideshow/${1:-$default_image_dir}/$resized_dir"

# Initial setup or any other initialization logic can go here
printf "Reading from $true_dir \n\n"

# Shuffle images and store in an array
shuffle_images() {
    mapfile -d '' shuffled_images < <(find "$true_dir" -type f -print0 | shuf -z)
    total_images=${#shuffled_images[@]}
}

shuffle_images
i=0
slideshow_playing=true

# Main loop for natural cycling
while true; do
    if [ "$next_image" == true ]; then
        next_image=false
        single_play=true
        ((i++))
        if [ $i -ge $total_images ]; then
            i=0
        fi
    elif [ "$prev_image" == true ]; then
        printf "Previous image received, i=$i\n"
        prev_image=false
        single_play=true
        ((i--))
        if [ $i -lt 0 ]; then
            i=$((total_images - 1))
        fi
    elif [ $slideshow_playing = false ]; then
        #we pause here. double check to avoid indentation
        : # do nothing
    else
        ((i++))
        if [ $i -ge $total_images ]; then
            i=0
        fi
    fi

    if [ $slideshow_playing = true ] || [ $single_play = true ] ; then
        image="${shuffled_images[$i]}"

        if [ "$3" == "none" ]; then
            echo "Setting background to: $image"
        fi
        if ! [ -n "$2" ]; then
            printf "Changing to $image \n\n"
        fi
        change_background "$image"
        single_play=false
    fi 
    # Use read with timeout for natural cycling and signal interruption
    #wait_until_timer $timer
    wait $timer
done

linux bash timer signals sleep
1个回答
0
投票

如果我理解正确的话,您的脚本中有一个主循环,并且您希望它在计时器上或在调用信号时执行任务。

这确实可以使用 read 来完成,但是您需要将其连接到管道。 以下是如何使用 FIFO 执行此操作的示例:

#!/bin/bash

set -eu
shopt -s failglob

FIFO=test.FIFO  # can be changed to whatever

mkfifo "${FIFO}"         # create a fifo
trap 'rm "${FIFO}"' EXIT # clean fifo at end of script

trap 'echo > "${FIFO}"' SIGUSR1  # write to fifo: this will unblock read from fifo

while true
do
    echo hello!  # do stuff here


    # this will wait for either 1s or for someone to write to fifo
    read -t 1 <> "${FIFO}"  || true # remark:  <> "${FIFO}" => because any process connected to fifo cannot start until the fifo is opened for both read and write
done                                # remark2: || true      => because read will fail on timeout and I always use set -e in my scripts
© www.soinside.com 2019 - 2024. All rights reserved.