Как запустить командную строку в терминале (Terminal, iterm2 и т. Д.)

#c #macos #terminal #iterm2

#c #macos #терминал #iterm2

Вопрос:

Я ищу общий и безопасный способ запуска строки скрипта в терминалах. В stack exchange есть десятки вопросов и ответов, Но все они не могут правильно разделять или экранировать поля, не глобализировать, заключать в кавычки и т. Д., Поскольку большинство из них передают строку через несколько уровней сценариев (оболочки, osascript).

Мне это нужно в приложении на C , поэтому objc / cocoa также является опцией. Любое решение, которое выполняет эту работу, является хорошим решением. Наиболее близкими на данный момент являются командные файлы для Terminal.app, но он выводит путь к командному файлу в первой строке, а также работает только для Terminal.app.

Комментарии:

1. Часть вашей проблемы здесь заключается в том, что вам нужна оболочка для запуска. Вы не просто пытаетесь запустить исполняемые файлы с переданными аргументами, вы пытаетесь фактически интерпретировать другой язык. Для этого вам понадобится что-то вроде Terminal.app , XQuartz.app и т. Д., Если вы не хотите реализовать свою собственную оболочку на C или включить исходный код из оболочки с открытым исходным кодом (например, оболочки Bourne-Again ) в свой проект. Но в нынешнем виде этот вопрос кажется слишком широким.

2. Возможно, я был неясен. Я полностью хочу, чтобы оболочка интерпретировала то, что я отправляю. Проблема всех решений заключается в том, что строка не пересылается идеально.

3. Итак, вы хотите открыть приложение терминала Mac OS и запустить какой-нибудь скрипт bash построчно в пользовательском интерфейсе? Похоже, вы не хотели зависеть от Terminal.app из вашего вопроса.

4. Может быть, я просто не понимаю вашего вопроса, но, возможно, простое std::system — это то, что вы ищете?

5. @scohe001 да. (хотя не уверен, что вы подразумеваете под построчно). «Возьмите строку и запустите ее в терминале, как если бы я ввел ее самостоятельно».

Ответ №1:

Это немного грубо по краям, но это сделает то, что вы хотите.

Как это работает?

Поскольку вы хотите что-то делать в пользовательском интерфейсе Mac, самым простым ответом будет Applescript. Эта программа создаст файл Applescript по адресу /tmp/terminal_runner.scpt . Затем он запустит скрипт с osascript помощью прямого вызова исполняемого файла при /usr/bin/osascript использовании execv .

Поскольку execv это C-код, все немного запутано. Это также предполагает, что строка cmd , которую вы запускаете, правильно экранирована, чтобы хорошо вписаться в сценарий Applescript.

 #include <iostream>
#include <string>
#include <fstream>
#include <cstring>
#include <unistd.h>
#include <errno.h>

void run_cmd_(std::string cmd) {
    const std::string executable = "osascript";
    const std::string tmp_script_loc = "/tmp/terminal_runner.scpt";

    std::ofstream fhand(tmp_script_loc);
    fhand << "tell application "Terminal"" << std::endl
        << "activate" << std::endl
        << "if (exists window 1) and not busy of window 1 then" << std::endl
        // Assumes cmd is nice and escaped
        //                   vvv
        << "do script "" << cmd << "" in window 1" << std::endl
        << "else" << std::endl
        //                   vvv
        << "do script "" << cmd << """ << std::endl
        << "end if" << std::endl
        << "end tell";

    fhand.close();

    char **argv = new char*[3];
    argv[0] = new char[executable.size()   1];
    std::strcpy(argv[0], executable.c_str());
    argv[1] = new char[tmp_script_loc.size()   1];
    std::strcpy(argv[1], tmp_script_loc.c_str());
    argv[2] = NULL;

    execv("/usr/bin/osascript", argv);
}


int run_cmd(std::string cmd) {
    int pid = fork();
    if(pid == -1) {
        perror("fork fail");
        return -1;
    } else if(pid == 0) {
        run_cmd_(cmd);
    } else {
        int status;
        if(waitpid(pid, amp;status, WUNTRACED) == -1) {
            perror("Error running command");
            return -1;
        }
        return status;
    }
    return 0;
}

int main() {
    std::string bash_cmd = "echo 'THIS IS A TEST'";
    int ret = run_cmd(bash_cmd);
    if(ret != 0) {
        perror("Oh dear...");
    }
}
 

Комментарии:

1. Может bash_cmd содержать двойные кавычки?

2. @Phil да, но они должны быть экранированы. ie bash_cmd = "echo \"example\"";