我正在尝试通过使用 Process
生成 sudo
子进程来运行具有提升权限的命令:
let process = Process()
process.executableURL = URL(fileURLWithPath: "/usr/bin/sudo")
process.arguments = ["ls"]
try process.run()
process.waitUntilExit()
但是,编译并运行上面的程序后,我无法输入 sudo
密码,并且该过程似乎挂起。
按 Ctrl+Z 后,然后使用 kill -9 %1
手动终止进程,我得到以下信息:
% sudo: a password is required
通过添加 -n
参数以非交互模式运行 sudo
会产生以下结果:
sudo: a password is required
但是,在 Python(和其他语言,我尝试过 Golang 和 Rust)中执行相同操作时,sudo
可以毫无问题地读取输入:
import subprocess
subprocess.run(["sudo", "ls"])
最佳答案
这很可能发生,因为 Swift 的 Process
使用posix_spawn(2) ,它不支持设置终端进程组 TPGID(有关更多详细信息,请参阅 this 和 this),至少在 Darwin 上是这样。
这有效地后台了生成的进程(用 job control 术语来说),并导致它在子进程尝试从终端读取数据后停止(sudo
在尝试读取您的密码时执行此操作) .
最好使用 ps/top/htop 实用程序来诊断:
% ps xao pid,ppid,pgid,tpgid,state,comm | { head -1 ; grep sudo }
PID PPID PGID TPGID STAT COMM
30061 29995 30061 29995 T /usr/bin/sudo
30081 30073 30073 30073 S+ /usr/bin/sudo
第一个进程是通过 Swift 的 Process
启动的,您可以看到它已经被停止 (T
),因为它在尝试从终端读取数据后收到了 SIGTTIN。
第二个进程是通过Python的subprocess
module启动的并具有正确的 TPGID
。
一个简单的解决方法是调用 tcsetpgrp(3) process.run()
之后如下:
tcsetpgrp(STDIN_FILENO, process.processIdentifier)
我们已经完成了此解决方法 in Tart并且它运行完美。
关于swift - 进程无法从 Swift 中的标准输入读取,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/76088356/