Bash、Xprintidle和 Firefox

前端开发 2026-07-10

我在尝试做一个脚本:一旦Firefox关闭就重新启动它,同时在一段闲置时间后将Firefox终止。

场景是一个只能访问一个网站的自助机,但网站中有指向其他站点的链接,结果最终打开了一个被阻止的页面且无法返回。

杀死(结束)部分应终止Firefox,前面的部分会重新启动它,以返回到该网站的主页。

脚本:

while true; do
    firefox --kiosk --start-fullscreen --noerrdialogs --no-first-run --disable-infobars thesite.com
    sleep 1
done &
while true; do
    if($(xprintidle)>120000);
        then pkill -f firefox
        sleep 120
    fi
    sleep 1
done

第一个while自身工作良好,但加入第二个之后,每秒都会打开一个新的thesite.com标签页。

我缺少了什么,如何才能让它正常工作?

解决方案

关于桌面空闲时间的启动/停止进程

你需要将浏览器作为一个后台作业运行,并把它视为一个子进程管理器:

#!/bin/bash

url="https://thesite.com/"
browserCmd=(firefox)
browserCmd+=(--kiosk --start-fullscreen --noerrdialogs)
browserCmd+=(--no-first-run --disable-infobars)
browserCmd+=("$url")

while :;do
    if (( $(xprintidle) > 120000 )); then
        if (( browserPid )); then
            kill $browserPid
            browserPid=
        fi
    else
        if ! (( browserPid )); then
            "${browserCmd[@]}" &
            browserPid=$!
        fi
    fi
    sleep .5
done

带有日志和中断捕获的更完整版本

#!/bin/bash

url="https://thesite.com/"
browserCmd=(firefox)
browserCmd+=(--kiosk --start-fullscreen --noerrdialogs)
browserCmd+=(--no-first-run --disable-infobars)
browserCmd+=("$url")

log() {
    local ms
    printf -v ms '%.3f' $EPOCHREALTIME
    printf '%(%F %T)T.%s %s %s (pid: %s).\n' ${ms%.*} ${ms#*.} "$1" \
        "${browserCmd[0]}" "$2"
}
trap '(( browserPid )) && kill $browserPid; exit' INT
while :;do
    if (( $(xprintidle) > 120000 )); then
        if (( browserPid )); then
            log Stopping $browserPid
            kill $browserPid
            browserPid=
        fi
    else
        if ! (( browserPid )); then
            "${browserCmd[@]}" &
            browserPid=$!
            log Starting $browserPid
        fi
    fi
    sleep .5
done

避免代码重复

因为我不喜欢重复进行相同的条件判断,这里给出一个版本,使用一个 case 代替三个 if

#!/bin/bash

maxIdleMs=120000
url="https://thesite.com/"
browserCmd=(firefox)
browserCmd+=(--kiosk --start-fullscreen --noerrdialogs)
browserCmd+=(--no-first-run --disable-infobars)
browserCmd+=("$url")

log() {
    local ms
    printf -v ms '%.3f' $EPOCHREALTIME
    printf '%(%F %T)T.%s %s %s (pid: %s).\n' ${ms%.*} ${ms#*.} "$1" \
        "${browserCmd[0]}" "$2"
}
trap '(( browserPid )) && kill $browserPid; exit' INT
while :;do
    case Idl:$(( $(xprintidle) > maxIdleMs )),Run:$(( browserPid > 0 )) in
        Idl:0,Run:0 )  # Not idle, but not running
            "${browserCmd[@]}" &
            browserPid=$!
            log Starting $browserPid
            ;;
        Idl:1,Run:1 )  # Idle, but running
            log Stopping $browserPid
            kill $browserPid
            browserPid=
            ;;
    esac
    sleep .5
done

关于 Machin Truc的评论 的新版本

如果你想在闲置时启动浏览器,且在用户交互之前,你需要在闲置时再多执行一个步骤来实现这一点:

#!/bin/bash

maxIdleMs=120000
url="https://thesite.com/"
browserCmd=(    firefox )
browserCmd+=(   --kiosk         --start-fullscreen      --noerrdialogs  )
browserCmd+=(   --no-first-run  --disable-infobars      )
browserCmd+=(   "$url"  )

log() {
    local ms
    printf -v ms '%.3f' $EPOCHREALTIME
    printf '%(%F %T)T.%s %s%s.\n' ${ms%.*} ${ms#*.} "$1" \
        "${2+ ${browserCmd[0]} (pid: $2)}"
}

trap '(( browserPid )) && kill $browserPid; exit' INT
declare -i yes=1 no=0
while :;do
    case " Idl:$(( $(xprintidle) > maxIdleMs )) Run:$(( browserPid > 0 )) " in
        " Idl:$no Run:$no " )  # Not idle, but not running
            "${browserCmd[@]}" &
            browserPid=$!
            log Starting $browserPid
            ;;
        " Idl:$yes Run:$yes " )  # Idle, but running
            log Stopping $browserPid
            kill $browserPid
            browserPid=

            "${browserCmd[@]}" &
            browserPid=$!
            log Starting $browserPid

            log 'Waiting for user interaction'
            while (( $(xprintidle) > maxIdleMs )); do
                sleep .5
            done
            log 'User interaction detected, back to main loop'
            ;;
    esac
    sleep .5
done

使用 getopt 作为可选参数的最终版本

#!/bin/bash

maxIdleMs=120000
url="https://thesite.com/"
browserCmd() {
    exec firefox --kiosk --start-fullscreen --noerrdialogs --no-first-run \
        --disable-infobars "$url"
}
usage() {
    printf 'Usage: %s [-i <float>] [-u <url>]\n' "$0"
    printf '\t%s %-5s %s\n' i float "Max idle time in seconds," \
           u url 'Target URL,' h '' 'Show this help.'
}
log() {
    local ms
    printf -v ms '%.3f' $EPOCHREALTIME
    printf '%(%F %T)T.%s %s%s.\n' ${ms%.*} ${ms#*.} "$1" \
        "${2+ ${browserCmd[0]} (pid: $2)}"
}
while getopts "hi:u:" opt; do
    case $opt in
        i ) printf -v maxIdleMs '%.3f' $OPTARG
            maxIdleMs=${maxIdleMs/.} ;;
        u ) url=$OPTARG ;;
        h ) usage; exit 0 ;;
        * ) printf >&2 'ERROR %s: Unknow arg.\n' "${0##*/}"
            usage; exit 1 ;;
    esac
done
trap '(( browserPid )) && kill $browserPid; exit' INT
declare -i yes=1 no=0
while :;do
    case " Idl:$(( $(xprintidle) > maxIdleMs )) Run:$(( browserPid > 0 )) " in
        " Idl:$no Run:$no " )  # Not idle, but not running
            browserCmd &
            browserPid=$!
            log Starting $browserPid
            ;;
        " Idl:$yes Run:$yes " )  # Idle, but running
            log Stopping $browserPid
            kill $browserPid
            browserPid=

            browserCmd &
            browserPid=$!
            log Starting $browserPid

            log 'Waiting for user interaction'
            while (( $(xprintidle) > maxIdleMs )); do
                sleep .5
            done
            log 'User interaction detected, back to main loop'
            ;;
    esac
    sleep .5
done

输出示例:

./manageFF.sh -u https://stackoverflow.com/a/79921474/1765658 -i 12
2026-04-08 15:12:54.680 Starting  (pid: 992610).
2026-04-08 15:14:59.920 Stopping  (pid: 992610).
2026-04-08 15:14:59.920 Starting  (pid: 1004525).
2026-04-08 15:14:59.921 Waiting for user interaction.
Exiting due to channel error.
2026-04-08 15:15:15.198 User interaction detected, back to main loop.
[GFX1-]: RenderCompositorSWGL failed mapping default framebuffer, no dt
2026-04-08 15:18:51.999 Stopping  (pid: 1004525).
2026-04-08 15:18:52.000 Starting  (pid: 1026897).
2026-04-08 15:18:52.000 Waiting for user interaction.
Exiting due to channel error.
2026-04-08 15:18:52.531 User interaction detected, back to main loop.
2026-04-08 15:19:11.771 Stopping  (pid: 1026897).
2026-04-08 15:19:11.772 Starting  (pid: 1028844).
2026-04-08 15:19:11.772 Waiting for user interaction.
Exiting due to channel error.
2026-04-08 15:19:56.607 User interaction detected, back to main loop.
^C user@host:~$ Exiting due to channel error.
Exiting due to channel error.

Ctrl + C 结束。

站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章