结合GUI和命令行OS X应用程序

时间:2012-08-30 18:40:15

标签: macos

是否可以创建一个可以从* nix命令行严格运行的Mac OS X应用程序(使用stdin / stdout,从终端控制台或通过ssh等运行),但也可以从应用程序图标并使用Mac GUI进行所有用户交互(无需终端)?如果是这样,怎么样?

我已经可以使用两个可执行文件实现这样的功能,一个是另一个GUI前端,管道,套接字,共享内存或Applescript(et.al.)用于通信。这个问题是我是否可以使用单个可执行文件和单个应用程序进程空间来执行此操作。

有没有办法在不将新的命令行参数传递给命令行可执行文件的情况下执行此操作? (因为存在与使用命令行应用程序相关的遗留问题)。

7 个答案:

答案 0 :(得分:3)

我相信,您正在寻找的是一种确定是从GUI还是从命令行启动的方法。一种简单的方法是获取父进程标识符。如果您是GUI进程,则由launchd启动(启动用户会话)。如果您是命令行,那么shell或脚本已启动您(无论如何,不​​启动)。

现在,该怎么做 - 一个简单的方法是使用proc_info系统调用(#336),它被libproc方便地包装(检查<libproc.h>。具体来说,你需要proc_bsdinfo(参见{{ 1}})其中有pbi_ppid字段来指定你的父节点(父节点的pbi_comm会告诉你它的名字 - 看它是否已经启动)。你可以通过在进入{{1之前'检查你的父节点来实现命令行功能。 }。

(与其他答案一样,你的二进制文件最终会出现在你的<sys/proc_info.h>中,所以是的,符号链接是有意义的)

答案 1 :(得分:1)

是, 只需在程序中添加一个开关--command-line即可。除非--command-line在运行时作为参数传递,否则启动正常的GUI。

./myprog --command-line

这就是它的全部内容。

答案 2 :(得分:1)

我不是100%确定这是否有效,但LSEnvironmentInfo.plist键的文档似乎表明只有在启动应用程序时才会定义指定的环境变量通过Finder。

如果要在LSEnvironment中定义变量(让我们称之为MYAPP_GUI)并在启动时检查其值,则在使用Finder时应该存在,而在使用终端时则不存在。那将告诉你是否要显示GUI或使用stdio

答案 3 :(得分:1)

尽管@ 3doubloons提供了正确的答案,但我仍然想分享演示这一点的代码段。

首先,在您的Info.plist中添加一个LSEnvironment键。它的值是一个env-var-names(作为键)及其字符串(仅允许字符串!)值的字典。就我而言,我添加了LaunchUI键,并添加了值"Yup"

enter image description here

然后,我的main()函数如下所示:

int main(int argc, const char * argv[]) {
    int status = -1;
    @autoreleasepool {
        
        do {
            NSProcessInfo *procInfo = [NSProcessInfo processInfo];

            // deterine "UI" launch versus "command-line
            id uiEnvVar = [[procInfo environment] objectForKey:@"LaunchUI"];
            if([uiEnvVar isKindOfClass:[NSString class]] && [uiEnvVar isEqualToString:@"Yup"] ) {    
                status = NSApplicationMain(argc, argv); //launch app normally with UI
                break;
            }
            
            id arguments = [procInfo arguments];
            if (arguments == nil) {
                printUsage();
                break;
            }
            if ([arguments count] == 1 || [arguments containsObject:@"-h"] || [arguments containsObject:@"-help"]) {
                printUsage();
                status = 0;
                break;
            }
            
            //process further arguments
            if(processArgs(arguments) == NO) {
                printUsage();
                break;
            }

            // kick off protection.

            if(YES != do_work()) {
                break;
            }
            
            status = 0; //

            // if your command-line should stay alive and handle events - you can do the following to block it from exiting.
            [[NSRunLoop currentRunLoop] run];
        } while(false);
    }
    return status;
}

我认为可以解决这个问题。享受吧!

答案 4 :(得分:0)

这是我在.Net应用程序中所做的事情。我认为这个想法可以在其他平台上用其他语言实现。

我会创建一个控制台应用程序,然后仍然使用所有GUI类。当程序启动时,根据确定它是GUI实例而不是命令行实例的任何标准,我要么启动GUI,要么相应地开始进入命令行界面。

主要的是你有一些条件决定你何时想要显示GUI而不是命令行。也许通过一个关于会话的系统变量(它是一个ssh会话,然后是命令行,否则是GUI)。

答案 5 :(得分:0)

我会说你不可能按照预期的方式行事。 GUI OSX应用程序实际上是应用程序包,即它们实际上是包含除实际可执行文件之外的 lot 资源的目录。你肯定不能在shell中执行这些bundle就好像它们是命令行可执行文件一样(例如你不能在shell提示符下执行./MyApplication.app --some-option,因为你的shell不知道如何执行一个目录),虽然您可能知道可以通过open MyApplication.app将它们作为GUI运行。现在,您可以到达捆绑包内的实际可执行文件,可能可以 可执行文件作为CLI可执行文件 - 说话因为我没有使用Xcode工具及其构建系统的实际经验,所以我可以在这里工作,但也许可以在CLI和GUI调用中使用实际的可执行文件。

但是,这不会非常有用,因为那些可执行文件都隐藏在应用程序包中,并且通常不能直接在shell中访问(即它们肯定不在你的$ PATH中,所以你必须调用它们像./MyApplication.app/Contents/MacOS/actualbinaryname这样的东西。我在野外看到的常用方法是提供与您的应用程序捆绑在一起的单独CLI可执行文件,通常由安装程序或通过单击应用程序中的按钮安装在合适的位置(例如/ usr / local / bin)喜好。那些CLI可执行文件负责与GUI应用程序的接口,如果这是您打算做的(从您的问题中不清楚)。

答案 6 :(得分:0)

道歉,如果我错过了这一点,但如果你使用的是Objective-C,则不是在objective-C中使用NSProcessInfo类并检查'arguments'变量以确定是否有任何参数的答案的一部分通过,然后在代码中决定下一步做什么?