【问题标题】:executing shell command with | (pipe) using NSTask使用 | 执行 shell 命令(管道)使用 NSTask
【发布时间】:2023-05-23 08:37:01
【问题描述】:

我正在尝试使用 NSTask 执行此命令 ps -ef | grep test,但我无法获得 |将包含在 NSTask 中的 grep 测试:

这是我目前用来将 ps -ef 的输出转换为字符串然后我需要以某种方式获取进程测试的 pid

NSTask *task;
task = [[NSTask alloc] init];
[task setLaunchPath: @"/bin/ps"];

NSArray *arguments;
arguments = [NSArray arrayWithObjects: @"-ef", nil];
[task setArguments: arguments];    
NSPipe *pipe;
pipe = [NSPipe pipe];
[task setStandardOutput: pipe];

NSFileHandle *file;
file = [pipe fileHandleForReading];

[task launch];

NSData *data;
data = [file readDataToEndOfFile];

NSString *string;
string = [[NSString alloc] initWithData: data
                               encoding: NSUTF8StringEncoding];
NSLog (@"got\n%@", string);

【问题讨论】:

  • 为什么不在程序中进行过滤呢?到 grep 的管道会更慢。

标签: objective-c cocoa


【解决方案1】:

管道是 shell 提供的一项功能,例如 /bin/sh。您可以尝试通过这样的 shell 启动您的命令:

/* ... */
[task setLaunchPath: @"/bin/sh"];
/* ... */
arguments = [NSArray arrayWithObjects: @"-c", @"ps -ef | grep test", nil];

但是,如果您让用户提供一个值(而不是硬编码,例如 test),您会使程序容易受到外壳注入攻击,这有点像 SQL 注入。另一种不会遇到此问题的方法是使用管道对象将ps 的标准输出与grep 的标准输入连接起来:

NSTask *psTask = [[NSTask alloc] init];
NSTask *grepTask = [[NSTask alloc] init];

[psTask setLaunchPath: @"/bin/ps"];
[grepTask setLaunchPath: @"/bin/grep"];

[psTask setArguments: [NSArray arrayWithObjects: @"-ef", nil]];
[grepTask setArguments: [NSArray arrayWithObjects: @"test", nil]];

/* ps ==> grep */
NSPipe *pipeBetween = [NSPipe pipe];
[psTask setStandardOutput: pipeBetween];
[grepTask setStandardInput: pipeBetween];

/* grep ==> me */
NSPipe *pipeToMe = [NSPipe pipe];
[grepTask setStandardOutput: pipeToMe];

NSFileHandle *grepOutput = [pipeToMe fileHandleForReading];

[psTask launch];
[grepTask launch];

NSData *data = [grepOutput readDataToEndOfFile];

/* etc. */

这使用内置的 Foundation 功能来执行与 shell 在遇到 | 字符时相同的步骤。

最后正如其他人指出的那样,grep 的使用是多余的。只需将其添加到您的代码中:

NSArray *lines = [string componentsSeparatedByString:@"\n"];
NSArray *filteredLines = [lines filteredArrayUsingPredicate: [NSPredicate predicateWithFormat: @"SELF contains[c] 'test'"]];

【讨论】:

  • +1 提到“管道是 shell 提供的一项功能,...”。我多年来一直在寻找原因!
  • 如果输出量很小,将输出捕获成字符串是可以的,但是如果任务输出的数据大于缓冲区,则进程可能会死锁。在这些情况下,管道到 grep 更好。我就是这样来到这里的!管道到 grep 解决了我的问题,我正在搜索子字符串的单个实例。 (见lists.apple.com/archives/cocoa-dev/2003/Jan/msg01106.html
【解决方案2】:

您可能需要在启动任务之前调用 [task waitUntilExit],以便进程可以在您读取输出之前完成运行。

【讨论】:

  • 进程确实完成了,我在字符串中得到了 ps -ef 的输出。但我还需要对该输出进行 grep 测试。