Python脚本subprocess调用C程序作为systemctl服务运行失败
问题:Systemd服务运行Python脚本时调用C程序无输出导致报错
我是技术新手,还请包涵。我在树莓派的Ubuntu Server 22.04上写了个Python脚本,按下遥控器按键时会通过subprocess调用一个C程序,这个C程序用来测量音频信号采样率,以二进制形式把值传给Python脚本,Python拿到值后乘以10再做后续处理。相关Python代码如下:
def oled_stringSR(): a = subprocess.run("gcc freq_count_2.c -lpigpio -lrt -o out1;./out1", capture_output = True, shell = True) d = 10*int(a.stdout) if d < 40001: sample_rate = 32 elif 40000 < d < 46001: sample_rate = 44.1
用sudo在终端运行脚本一切正常,但做成systemctl服务运行,触发遥控器逻辑时脚本就挂了,journalctl里的报错是:
May 17 23:19:00 raspcamilla oled[1076]: Traceback (most recent call last): May 17 23:19:00 raspcamilla oled[1076]: File "/home/marcosch/remote.py", line 2231, in <module> May 17 23:19:00 raspcamilla oled[1076]: main() May 17 23:19:00 raspcamilla oled[1076]: File "/home/marcosch/remote.py", line 1267, in main May 17 23:19:00 raspcamilla oled[1076]: oled_stringSR() May 17 23:19:00 raspcamilla oled[1076]: File "/home/marcosch/remote.py", line 2084, in oled_stringSR May 17 23:19:00 raspcamilla oled[1076]: d = 10*int(a.stdout) May 17 23:19:00 raspcamilla oled[1076]: ValueError: invalid literal for int() with base 10: b''
看起来是C程序没运行或者没输出,而且我不用sudo在终端跑脚本也会出一模一样的错,怀疑是权限问题。我知道systemd服务一般以root身份运行,这到底是为啥?
当前的systemd服务文件如下:
[Unit] After=syslog.target StartLimitIntervalSec=10 StartLimitBurst=10 [Service] Type=simple ExecStart=python3 /home/marcosch/remote.py Restart=always RestartSec=1 StandardOutput=journal StandardError=journal SyslogIdentifier=oled User=root Group=root [Install] WantedBy=graphical.target
问题分析与解决办法
核心原因
虽然服务配置了以root运行,但systemd服务的默认工作目录是根目录/,不是你脚本所在的/home/marcosch/。你的subprocess命令直接编译运行freq_count_2.c和out1,在根目录下找不到这些文件,自然编译失败、没有输出,导致Python解析空字符串报错。
另外还有几个潜在问题:
- 每次触发都重新编译C程序,效率极低且易产生冗余文件
- 未捕获subprocess的错误输出,无法排查编译/运行失败的具体原因
- 服务的
WantedBy=graphical.target在无图形界面的Ubuntu Server环境下,可能无法自动启动
解决步骤
1. 预编译C程序,避免重复编译
在终端用sudo编译好程序:
cd /home/marcosch sudo gcc freq_count_2.c -lpigpio -lrt -o out1 sudo chmod +x out1
2. 修改Python脚本的subprocess调用
指定程序绝对路径,同时添加错误处理方便排查:
import sys import subprocess def oled_stringSR(): # 调用预编译好的程序,用绝对路径 a = subprocess.run("/home/marcosch/out1", capture_output=True, shell=False) # 先检查命令执行状态 if a.returncode != 0: print(f"C程序运行失败,错误信息: {a.stderr.decode('utf-8')}", file=sys.stderr) return # 处理输出,先去除空白字符再判断是否为空 stdout_str = a.stdout.decode('utf-8').strip() if not stdout_str: print("C程序无输出", file=sys.stderr) return try: d = 10 * int(stdout_str) except ValueError as e: print(f"解析输出失败: {e},输出内容: {stdout_str}", file=sys.stderr) return if d < 40001: sample_rate = 32 elif 40000 < d < 46001: sample_rate = 44.1 # 后续逻辑...
3. 修改systemd服务文件
- 指定工作目录到脚本所在路径
- 把
WantedBy改为multi-user.target(适配无图形的Server环境)
修改后的服务文件:
[Unit] After=syslog.target network.target StartLimitIntervalSec=10 StartLimitBurst=10 [Service] Type=simple WorkingDirectory=/home/marcosch ExecStart=python3 /home/marcosch/remote.py Restart=always RestartSec=1 StandardOutput=journal StandardError=journal SyslogIdentifier=oled User=root Group=root [Install] WantedBy=multi-user.target
4. 重新加载服务并重启
sudo systemctl daemon-reload sudo systemctl restart oled.service
额外排查点
如果问题依旧,用以下命令查看服务详细日志,包括C程序的错误输出:
journalctl -u oled.service -f
内容的提问来源于stack exchange,提问作者MrRamone
相关产品推荐
相关产品推荐

