Python脚本中树莓派scp+SSH密钥免密传输失效问题求助
本地树莓派终端中使用scp配合SSH密钥对,可免密码将文件传输至远程树莓派pi@192.168.0.15。但在Python脚本中通过os.system()执行相同scp命令时,终端会中断脚本并提示输入远程账户的密码。已尝试多种SSH密钥对配置教程,问题仍未解决,求解决思路。
附上的Python代码如下:
# Simple example of reading the MCP3008 analog input channels and printing # them all out. # Author: Tony DiCola # License: Public Domain import time # Import SPI library (for hardware SPI) and MCP3008 library. import Adafruit_GPIO.SPI as SPI import Adafruit_MCP3008 import os # Software SPI configuration: # CLK = 18 # MISO = 23 # MOSI = 24 # CS = 25 # mcp = Adafruit_MCP3008.MCP3008(clk=CLK, cs=CS, miso=MISO, mosi=MOSI) # Hardware SPI configuration: SPI_PORT = 0 SPI_DEVICE = 0 mcp = Adafruit_MCP3008.MCP3008(spi=SPI.SpiDev(SPI_PORT, SPI_DEVICE)) num = 1 print('Reading MCP3008 values, press Ctrl-C to quit...') # Print nice channel column headers. print('| {0:>4} | {1:>4} | {2:>4} | {3:>4} | {4:>4} | {5:>4} | {6:>4} | {7:>4} |'.format(*range(8))) print('-' * 57) # Main program loop. while True: # Read all the ADC channel values in a list. values = [0]*8 for i in range(8): # The read_adc function will get the value of the specified channel (0-7). values[i] = mcp.read_adc(i) # Print the ADC values. print('| {0:>4} | {1:>4} | {2:>4} | {3:>4} | {4:>4} | {5:>4} | {6:>4} | {7:>4} |'.format(*values)) # Check if distance of object has increased, take a picture if it has. if values[0] < 100: num = num + 1 imagename = str(num) os.system("raspistill -vf -ex night -o "+imagename) os.system("scp /home/pi/Adafruit_Python_MCP3008/examples/"+imagename+" pi@192.168.0.15:/home/pi/Pictures") else: # Pause for half a second. time.sleep(0.5)
嘿,我之前也碰到过一模一样的情况——手动在终端用scp能免密传输,但Python脚本里跑就卡在密码输入环节,折腾了好一阵才搞明白。核心问题基本都是脚本运行的环境和你手动操作的终端环境不一致,或者scp在脚本里没正确识别到密钥配置。给你几个具体的排查和解决方向:
1. 确认脚本的运行用户身份
你手动执行scp时用的是pi用户对吧?那得先搞清楚Python脚本是不是也用同一个用户在运行。比如如果你是用sudo python3 your_script.py启动的脚本,那运行用户就是root,而root的SSH密钥配置和pi是完全分开的,自然会要求输入密码。
你可以在脚本里加几行代码验证当前用户:
import getpass print(f"当前运行脚本的用户: {getpass.getuser()}")
如果输出不是pi,有两个解决方向:
- 换用
pi用户启动脚本(直接运行python3 your_script.py,不要加sudo); - 给当前运行的用户(比如
root)配置SSH密钥:把该用户的~/.ssh/id_rsa.pub内容复制到远程pi@192.168.0.15的~/.ssh/authorized_keys里,同时确保该用户的.ssh目录权限是700,authorized_keys文件权限是600(执行chmod 700 ~/.ssh和chmod 600 ~/.ssh/authorized_keys)。
2. 显式指定密钥文件路径
有时候脚本运行时的环境变量没正确加载,导致scp找不到默认的私钥文件。这时候直接在scp命令里指定私钥路径就能解决问题,把你的scp命令改成:
os.system(f"scp -i /home/pi/.ssh/id_rsa /home/pi/Adafruit_Python_MCP3008/examples/{imagename} pi@192.168.0.15:/home/pi/Pictures")
这里/home/pi/.ssh/id_rsa是你本地私钥的路径,一定要确保路径正确,而且私钥文件的权限是600(执行chmod 600 /home/pi/.ssh/id_rsa)——SSH对密钥文件的权限要求很严格,权限太宽会直接拒绝使用。
3. 查看scp的详细日志定位问题
给scp命令加上-v参数,打印详细的执行日志,这样能精准定位问题出在哪一步:
os.system(f"scp -v /home/pi/Adafruit_Python_MCP3008/examples/{imagename} pi@192.168.0.15:/home/pi/Pictures")
运行脚本后,终端会输出大量日志,你可以重点关注:
- 是否尝试加载了你的私钥文件;
- 远程服务器是否接受了你的密钥;
- 是否出现了权限相关的错误信息。
根据日志就能快速找到问题的根源。
4. 替换os.system为subprocess模块(更可靠的选择)
os.system是比较老旧的模块,很难捕获命令的输出和错误信息。换成subprocess.run不仅能更灵活地控制命令执行,还能直接获取scp的错误输出,方便排查问题:
import subprocess # 替换原来的os.system(scp命令)为下面的代码 scp_cmd = [ "scp", "-i", "/home/pi/.ssh/id_rsa", f"/home/pi/Adafruit_Python_MCP3008/examples/{imagename}", "pi@192.168.0.15:/home/pi/Pictures" ] result = subprocess.run(scp_cmd, capture_output=True, text=True) print("SCP执行输出:", result.stdout) print("SCP错误信息:", result.stderr)
这样如果scp执行失败,你能直接看到具体的错误原因,比如密钥找不到、权限不足等。
5. 检查SSH_AUTH_SOCK环境变量
手动在终端运行echo $SSH_AUTH_SOCK,然后在脚本里也打印这个变量:
import os print(f"SSH_AUTH_SOCK环境变量: {os.environ.get('SSH_AUTH_SOCK')}")
如果脚本里的输出是空的,说明ssh-agent没在脚本的环境中运行,导致scp无法通过代理获取密钥。这种情况下,要么显式在脚本里启动ssh-agent并添加密钥,要么就用上面第2种方法直接指定密钥路径(后者更简单)。
内容的提问来源于stack exchange,提问作者Sean Dever

