Bash、Xprintidle和 Firefox
我在尝试做一个脚本:一旦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导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。