小能豆

`Subprocess.run` 中的 for 循环导致 `语法错误:“do” 意外`

py

我正在尝试for通过 python 在 shell 中运行循环。os.popen运行良好,但在 3.x 上已弃用,我想要 stderr。遵循关于如何在 Subprocess.run 命令中使用 for 循环的最高投票答案会导致Syntax error: "do" unexpected,shellcheck 对此表示同意:

import subprocess
proc = subprocess.run(
    "bash for i in {1..3}; do echo ${i}; done",
    shell=True,
    stdout=subprocess.PIPE,
    stderr=subprocess.PIPE, )

print(proc.stderr)

我最终尝试通过 python 调用此 shell 代码https://unix.stackexchange.com/a/611305/362437来重置所有 usb ,因此任何其他方法都将受到赞赏。


阅读 31

收藏
2024-12-06

共1个答案

小能豆

您面临的问题是由于您使用subprocess.runshell 命令的方式以及您尝试传递循环的方式所致。Syntax error: "do" unexpected发生这种情况的原因是,您传递的 shell 脚本不包含用于运行循环的正确 shell 语法bash

您可以通过确保执行bashshell 并在调用中使用正确的 shell 语法来修复此问题subprocess.run。 应该正确转义内部命令,subprocess.run以便 Python 将其正确传递给 shell。

解决方法如下:

解决方案:

import subprocess

# Using subprocess to run a shell command with a for loop
proc = subprocess.run(
    "for i in {1..3}; do echo ${i}; done",  # Correct bash syntax for the for loop
    shell=True,  # Run in the shell
    stdout=subprocess.PIPE,  # Capture standard output
    stderr=subprocess.PIPE,  # Capture standard error
)

# Print the captured stdout and stderr
print("Standard Output:")
print(proc.stdout.decode())  # Decode byte output to string
print("\nStandard Error:")
print(proc.stderr.decode())  # Decode byte error output to string

要点:

  1. shell=True:这允许将命令传递给 shell 执行。
  2. stdout=subprocess.PIPEstderr=subprocess.PIPE:这些参数捕获来自命令的标准输出和错误输出。
  3. proc.stdout.decode()proc.stderr.decode():由于以subprocess.run字节形式返回输出,我们在打印之前将其解码为字符串。

解释:

  • for i in {1..3}; do echo ${i}; done有效的 Bash 命令。{1..3}是 Bash 特定的序列扩展,它创建从 1 到 3 的数字序列。
  • 打印echo ${i}序列中的每个数字。
  • subprocess.run执行 Bash 命令并捕获输出(标准输出和错误)。

如果您正在运行 shell 脚本来重置 USB 设备(如您提到的),您可以将其中的命令调整subprocess.run为您需要的任何 Bash 命令。只需确保您传递的命令subprocess.run具有正确的 Bash 语法和任何必要的转义字符即可。

USB 重置案例示例:

import subprocess

# Command to reset USB devices (as per your example)
reset_command = """
for device in $(lsusb); do
    bus=$(echo $device | awk '{print $2}')
    device_id=$(echo $device | awk '{print $4}' | sed 's/://')
    echo "Resetting USB device $device_id on bus $bus"
    sudo usbreset /dev/bus/usb/$bus/$device_id
done
"""

# Run the command using subprocess
proc = subprocess.run(
    reset_command,
    shell=True,
    stdout=subprocess.PIPE,
    stderr=subprocess.PIPE,
)

# Capture and print output
print("Standard Output:")
print(proc.stdout.decode())
print("\nStandard Error:")
print(proc.stderr.decode())

这将运行循环reset_command遍历 USB 设备并用于usbreset重置每个设备的。确保usbreset已安装,并且运行此脚本的用户具有必要的权限(例如,sudo用于设备访问)。

2024-12-06