我设法创建了一个 Python 脚本来接受来自 USB 读卡器的磁卡刷卡。它似乎按预期工作。该例程的一部分是卷曲命令将数据推送到另一台服务器。由于 Ubuntu 服务器没有显示器/键盘/鼠标,因此我响应用户的唯一方式是使用服务器扬声器的声音。
通常情况下,卷曲命令从其他应用服务器获得标准 200 响应。但有时它会获得 500 错误响应,我需要向用户传达一些信息以提醒他们“这次没有成功,请再次扫描您的卡”。
一个简单的机制是成功时发出一次哔哔声,失败时不发出任何声音。按照编写并从终端运行,它可以工作。按照计划从rc.本地,系统提示音不响。
...
args = 'card=' + trackone.group(2)
r = requests.get('http://apiserver/api/', args)
if r.status_code == 200:
# First attempt which doesn't work from rc.local
# print("\a")
# Second attempt, wrap the beep in a shell script
os.system("sh /home/myuser/beep.sh")
else
print(r.status_code)
我尝试创建一个简单的 shell 脚本 beep.sh:
#!/bin/bash
echo "\a"
如上所述,两次尝试都可以从终端运行,但不能从 root 运行rc.本地控制。而且我知道 API 正在被调用,因为我可以看到日志条目。
rc.local 中的行如下所示:
python '/home/myuser/scancards.py'
有没有办法让这个命令(在 init.d 进程中运行)能够使扬声器发出蜂鸣声?
答案1
安装 beep sudo apt install beep
(对于 16.04 之前的版本,请使用 apt-get 而不是 apt)
由于 beep 支持不同的频率,您可以简单地beep -f "$r.statuscode" or beep once for yes
beep -r1 and twice for no
beep -r2`。
如果由于某些未知原因您无法获得多于一声哔声,您可以利用 -f 开关来调整频率,例如,获得表示“否”的较低音调和表示“是”的较高音调。
例子:
不:beep -f 250
是的:beep -f 2500
是的,我知道这是一个简短的回答,但有时这就是所需要的全部。
进一步的研究表明,您必须在 /etc/modprobe.d/blacklist.conf 中注释掉黑名单 pcspkr 才能使其工作。您可能还必须在 beep 上设置 suid 位才能使其工作(我曾经这样做过,sudo chmod 4755 /usr/bin/beep
因为我不担心其他人在这个系统上使用 beep),您可能希望使用组来调整权限,以便只有有资格的用户才能执行 beep。
注意:正如@JdeBP在他的回答中指出的那样,你可能必须打开文件描述符到终端设备
资料来源:
man beep
答案2
在 init.d 进程中运行
不存在“init.d 进程”这样的东西。
rc.local
rc
是已被取代三次的系统的一部分。该系统已被 van Smoorenburg rc
、upstart(十年前)和(自 Ubuntu 版本 15 起)systemd 取代。您使用的是第三代向后兼容垫片。
在 systemd 操作系统(例如 Ubuntu 15 及更高版本)上,shim 是一个名为 的 systemd 服务rc-local.service
。您可以使用以下命令查找其服务定义
systemctl cat rc-local.service
可以看出,它没有定义为将服务附加到终端设备。服务进程不是通过控制终端运行的,其标准输入和输出也没有连接到终端。
该 Python 代码和 Shell 脚本不要“使 PC 扬声器发出哔哔声”。它们将字符 #7 写入其标准输出。它正好当您在登录会话中以交互方式运行它们时作为其标准输出的设备将字符 #7 解释为发出噪音的指令。将交互式命令的标准输出重定向到,/dev/null
并观察代码变得多么安静。
rc.local
这就是为什么(非常)向后兼容的垫片与终端设备没有连接这一事实很重要。
您可以使用beep
实用程序解决此问题。该实用程序尝试(如果正确调用)显式打开终端设备的文件描述符,并将字符 #7 发送到ioctl()
该设备(或在该设备上使用 console 或 evdev ),而不是简单地假设标准输出是终端设备。
但同样值得我们认真思考是否不使用rc.local
。
进一步阅读
答案3
/etc/rc.local
通过 Python 脚本让 PC 扬声器发出蜂鸣声Linux 控制台,你可以使用console_ioctl(4)
:KDMKTONE
,KIOCSOUND
:
#!/usr/bin/env python
import os
from fcntl import ioctl
CLOCK_TICK_RATE = 1193180 # magic https://github.com/johnath/beep/blob/0d790fa45777896749a885c3b93b2c1476d59f20/beep.c#L31-L49
KDMKTONE = 0x4B30 # generate tone include/uapi/linux/kd.h#L25
def beep(console_fd, frequency=440, length_millis=200):
period = CLOCK_TICK_RATE // frequency
ioctl(console_fd, KDMKTONE, (length_millis << 16) | period) # start beeping
# return immediately
beep(console_fd=os.open('/dev/tty0', os.O_RDONLY | os.O_NOCTTY)) # I'm [G]root
看beep.py
。它之所以有效是因为/etc/rc.local
由 执行root
。你可以以普通用户身份运行它如果你拥有终端例如,在Ctrl++Alt虚拟F1控制台中(按Alt+F7切换回您的 GUI 窗口管理器)。
确保pcspkr
内核模块没有被列入黑名单(注释掉默认值):
$ sudo sed -i 's/^blacklist pcspkr/#blacklist pcspkr/' /etc/modprobe.d/blacklist.conf
确保它可以无错误地加载:
$ sudo modprobe pcspkr
确保/etc/rc.local
可执行:
$ sudo chmod +x /etc/rc.local
print('\a')
(将U+0007 BELL*
字符写入标准输出)导致取决于你的环境。
它可以播放bell.ogg
使用pactl
命令(用于 PulseAudio 声音服务器):
$ pactl upload-sample /usr/share/sounds/ubuntu/stereo/bell.ogg bell.ogg
PulseAudio 很可能不使用电脑扬声器播放声音。
在我的 Ubuntu 16.04 系统上,print('\a')
从/etc/rc.local
写入并#007
发出/var/log/syslog
蜂鸣声到电脑扬声器(systemctl cat rc-local.service
显示StandardOutput=journal+console
即输出到日志文件和控制台)。
答案4
要通过 Python 脚本让 PC 扬声器发出蜂鸣声,你可以使用Linux evdev API:
#!/usr/bin/env python
import ctypes
import math
import os
import time
EV_SND = 0x12 # linux/input-event-codes.h
SND_TONE = 0x2 # ditto
time_t = suseconds_t = ctypes.c_long
class Timeval(ctypes.Structure):
_fields_ = [('tv_sec', time_t), # seconds
('tv_usec', suseconds_t)] # microseconds
class InputEvent(ctypes.Structure):
_fields_ = [('time', Timeval),
('type', ctypes.c_uint16),
('code', ctypes.c_uint16),
('value', ctypes.c_int32)]
frequency = 440 # Hz, A440 in ISO 16
device = "/dev/input/by-path/platform-pcspkr-event-spkr"
pcspkr_fd = os.open(device, os.O_WRONLY) # root! + modprobe pcspkr
fsec, sec = math.modf(time.time()) # current time
ev = InputEvent(time=Timeval(tv_sec=int(sec), tv_usec=int(fsec * 1000000)),
type=EV_SND,
code=SND_TONE,
value=frequency)
os.write(pcspkr_fd, ev) # start beep
try:
time.sleep(0.2) # 200 milliseconds
finally:
ev.value = 0 # stop
os.write(pcspkr_fd, ev)
跑步beep-evdev.py
/etc/rc.local
以 root 身份运行。 因此脚本应该按原样工作。
如果文件/dev/input/by-path/platform-pcspkr-event-spkr
不存在,请确保pcspkr
模块已加载:
root# modprobe pcspkr
该脚本除了其自身之外没有其他依赖项python
。