swift - 进程无法从 Swift 中的标准输入读取

标签 swift macos process stdin sudo

我正在尝试通过使用 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(有关更多详细信息,请参阅 thisthis),至少在 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/

相关文章:

linux - 从 semop 获取无效参数

c# - 如何等待C#中的CreateProcessAsUser完成

ios - 快速将 View Controller 解除到另一个 View Controller

linux - OSX 上的 "pkg-config script could not be found"

ios - 请求时间过长时取消 NSURLSession dataTask

macos - 如何获取另一个进程的已加载符号列表

macos - 使用 NSWorkspace.openURL 打开 URI 时捕获弹出窗口

process - 是否有一种面向描述代理之间交互的编程语言?

swift - 如何在 iOS 推送通知中合并 loc-args 和 loc-key 字符串

swift - react swift : How to observe UIView isHidden?