【问题标题】:How do I capture output from a shell command?如何捕获 shell 命令的输出?
【发布时间】:2011-11-16 04:55:35
【问题描述】:

可可新手警告!

我发现以下 shell 命令是确定进程是否正在运行的好方法(1 = 正在运行,0 = 未运行):

if [ $(ps -Ac | egrep -o 'ProcessName') ]; then echo 1; else echo 0; fi;

我可以使用“系统”命令将其合并到 Cocoa 中:

system("if [ $(ps -Ac | egrep -o 'Finder') ]; then echo 1; else echo 0; fi;");

但是,输出被定向到运行日志,我不知道如何在我的 Cocoa 代码中捕获结果(1 或 0)。

我尝试使用 NSTask 来实现它,如下所示:

NSTask *task = [[NSTask alloc] init];
[task setLaunchPath:@"/bin/sh"];
[task setArguments:[NSArray arrayWithObject:@"if [ $(ps -Ac | egrep -o 'Finder') ]; then echo 1; else echo 0; fi;"]];
NSPipe *pipe = [NSPipe pipe];
[task setStandardOutput:pipe];
[task launch];
NSData *data = [[pipe fileHandleForReading] readDataToEndOfFile];
[task waitUntilExit];
[task release];
NSString *output = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog (@"%@", output);
[output release];

但是,这会生成以下错误消息:

if [ $(ps -Ac | egrep -o 'Finder') ];然后回显 1;否则回显0; fi;: 没有这样的文件或目录

你能告诉我如何正确地实现这个 shell 命令,让我可以捕获代码中的输出(1 或 0)吗? (我知道确定进程是否正在运行的其他方法,但我提出问题的部分原因是学习如何在 Cocoa 中实现一般的 shell 脚本。)

非常感谢您对此问题的任何帮助。

【问题讨论】:

  • 看看它是如何工作的,为什么要依赖一个相对复杂的表达式作为初学者。看看你能不能让echo 1 工作。也因为`没有这样的文件或目录`消息,system() 直接调用本地目录,寻找文件名if ...。我猜你需要用shell的路径和-e(执行选项?)来包装你的“脚本”,产生简单的案例测试[task setArguments:[NSArray arrayWithObject:@"/bin/bash -e 'echo 1'"]]。为什么不直接使用您的代码调用保存到文件系统的脚本呢?祝你好运。
  • shell脚本由shell执行;要从 system() 运行 shell,您可以使用 system("sh -c 'echo moo'") 之类的东西。你最好只捕获来自system() 的结果代码而不是命令的输出,然后你不需要ifechos:system("sh -c 'ps -Ac | egrep -o Finder'") 和来自egrep 的退出代码将是返回,指示是否存在匹配项。
  • ps的输出不用测试 |鹭鸶改为测试 egrep 的返回值。 IOW,只需写“if ps -Ac | egrep -o 'Finder' > /dev/null”(或者只使用 pgrep!)
  • 威廉,谢谢。我在下面的工作答案中纳入了您的建议。

标签: cocoa shell capture


【解决方案1】:

你做错了。与其问“我如何运行外部进程来执行 X”,不如问“我如何编写代码来执行 X”。

您无需使用外部脚本来获取进程列表。通常,出于性能和安全原因,您应该始终尝试使用 API 而不是启动外部任务。

在这种情况下,您需要的 API 是 sysctl C 接口。

JongAm Park 写了一个 Objective-C wrapper 用于使用 sysctl 获取正在运行的进程列表。他的帖子中的 cmets 也有很好的观点。

或者,您可以通过查看 the source code for the ps command. 来了解 Apple 是如何做到的

【讨论】:

    【解决方案2】:

    Rob,感谢您提供有关尽可能在代码中使用直接解决方案的一般指示。我查看了 JongAm Park 的 sysctl 包装器,以及 Apple 的 ps 源代码。合并需要一段时间,但我现在知道在哪里可以找到获取进程列表的直接解决方案。

    shellter 和 Tripleee,感谢您对与 shell 命令交互的建议。根据您的建议,我得到了三种工作方法(!):

    方法一使用system命令的返回码(不需要egrep -o):

    BOOL processIsRunning = system("ps -Ac | grep 'ProcessName' > /dev/null") == 0;
    

    方法 2 使用带有 shell -c 选项的 NSTask(注意 -c 而不是 -e):

    NSTask *task = [[NSTask alloc] init];
    [task setLaunchPath:@"/bin/sh"];
    [task setArguments:[NSArray arrayWithObjects:@"-c",@"ps -Ac | grep 'ProcessName'",nil]];
    NSPipe *pipe = [NSPipe pipe];
    [task setStandardOutput:pipe];
    [task launch];
    NSData *data = [[pipe fileHandleForReading] readDataToEndOfFile];
    [task waitUntilExit];
    [task release];
    NSString *result = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
    BOOL processIsRunning = [result length] > 0;
    

    方法 3 也将 NSTask 与 shell -c 选项一起使用,但在这种情况下,要执行的命令是另一个带有自己的 -c 选项的 shell(这是经过多次试验和错误后我能找到的唯一方法在要执行的命令中加入if 构造;当然,对于当前问题来说,方式是矫枉过正):

    NSTask *task = [[NSTask alloc] init];
    [task setLaunchPath:@"/bin/sh"];
    [task setArguments:[NSArray arrayWithObjects:@"-c",@"/bin/sh -c 'if [ \"$(ps -Ac | egrep -o 'ProcessName')\" = \"ProcessName\" ]; then echo 1; else echo 0; fi;'",nil]];
    NSPipe *pipe = [NSPipe pipe];
    [task setStandardOutput:pipe];
    [task launch];
    NSData *data = [[pipe fileHandleForReading] readDataToEndOfFile];
    [task waitUntilExit];
    [task release];
    NSString *result = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
    BOOL processIsRunning = [result intValue] == 1;
    

    感谢大家的大力帮助。

    【讨论】:

      【解决方案3】:

      这是一个完全不涉及 Cocoa 的一般答案:

      如果你想从system()获取输出,不要使用system(),使用popen()

      【讨论】:

      • 谢谢。我将研究 popen()。解决问题的方法有很多!
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-06-28
      • 2022-01-14
      • 2010-12-19
      相关资源
      最近更新 更多