Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

👻emmmm..... Can I realtime print the output line by line when executing ios_popen() #80

Open
Chihpin opened this issue Feb 26, 2019 · 4 comments

Comments

@Chihpin
Copy link

Chihpin commented Feb 26, 2019

I am a newbie about this.

Here's my code

It waits for the command to finish before printing the output together


void my_popen(NSString *input, void(^output)(NSString *output, BOOL isEnd)) {
    
    const char *command = input.UTF8String;
    
    char result_buf[1024];
    int rc = 0;
    __block FILE *fp;
    
    fflush(stdout);
    fp = ios_popen(command, "r");
    
    if(NULL == fp)
    {
        perror("popen failed!");
        output(@"popen failed!", YES);
        return;
    }
    while(fgets(result_buf, sizeof(result_buf), fp) != NULL)
    {
        if('\n' == result_buf[strlen(result_buf)-1])
        {
            result_buf[strlen(result_buf)-1] = '\0';
        }
        output([NSString stringWithFormat:@"%s", result_buf], NO);
    }
    
    rc = pclose(fp);
    if(-1 == rc)
    {
        printf("【%s】【%d】\r\n", command, rc);
    }
    else
    {
        printf("【%s】【%d】【%d】\r\n", command, rc, WEXITSTATUS(rc));
    }
}
@Chihpin Chihpin changed the title 👻Can I realtime print the output line by line when executing a command?I‘m a newbie 👻emmmm..... Can I realtime print the output line by line when executing "ios_popen()"? Feb 26, 2019
@Chihpin Chihpin changed the title 👻emmmm..... Can I realtime print the output line by line when executing "ios_popen()"? 👻emmmm..... Can I realtime print the output line by line when executing ios_popen() Feb 26, 2019
@holzschu
Copy link
Owner

So, does that work?
You could also do it with the getline() function:
getline(result_buf, 1024, fp);

@Chihpin
Copy link
Author

Chihpin commented Feb 26, 2019

@holzschu
I'm sorry I didn't describe it clearly. The piece of code in my question doesn't work for realtime print

I hit a breakpoint and found it blocked on this line of code

fp = ios_popen(command, "r");

It took a few seconds to continue., so getline() is not work well.

Then I tried to change the internal implementation of ios_popen

void ios_popen2(const char* inputCmd, const char* type, FILE **output) {
    // Save existing streams:
    int fd[2] = {0};
    const char* command = inputCmd;
    // skip past all spaces
    while ((command[0] == ' ') && strlen(command) > 0) command++;
    // TODO: skip past "/bin/sh -c" and "sh -c"
    if (pipe(fd) < 0) { return; } // Nothing we can do if pipe fails
    // NOTES: fd[0] is set up for reading, fd[1] is set up for writing
    // fpout = fdopen(fd[1], "w");
    // fpin = fdopen(fd[0], "r");
    if (type[0] == 'w') {
        // open pipe for reading
        child_stdin = fdopen(fd[0], "r");
        // launch command:
        *output = fdopen(fd[1], "w");
        ios_system(command);
    } else if (type[0] == 'r') {
        // open pipe for writing
        // set up streams for thread
        child_stdout = fdopen(fd[1], "w");
        // launch command:
        *output = fdopen(fd[0], "r");
        ios_system(command);
    }
//    return NULL;
}

And then I call this in my app

__block FILE *fp = NULL;
__block BOOL end_flag = NO;
dispatch_async(dispatch_get_global_queue(0, 0), ^{
    char result_buf[MAXLINE];
    fpos_t pos = 0;
    while(1)
    {
        if (fp != NULL) {
            fsetpos(fp, &pos);
            if (fgets(result_buf, sizeof(result_buf), fp) != NULL) {
                fgetpos(fp, &pos);
                if('\n' == result_buf[strlen(result_buf)-1])
                {
                    result_buf[strlen(result_buf)-1] = '\0';
                }
                output([NSString stringWithFormat:@"%s", result_buf], NO);
            }
        }
        if (end_flag) {
            break;
        }
        sleep(0.5);
    }
});
ios_popen2(input.UTF8String, "r", &fp);
end_flag = YES;

In this way, the real-time printout is realized.
It's fantastic!

I don't know much about ios_system framework.
emmm..... may I ask if there are any danger in my solution?

@holzschu
Copy link
Owner

Ah, now I understand the question.
Yes, by default, ios_system commands run in the foreground if they are the first command to be called. Meaning the command runs entirely, then gives back control to the app. The solution to have interactive communication between the command and the application is to run them in queues or threads, as you have done.

You could (probably) also make it work by sending the ios_popen2 command in an async queue, and have the reading in the main loop.

You could also simplify your code with:

    thread_stdout = fdopen(fd[1], "w");
    thread_stdin =  fdopen(fd[0], "r");
    ios_system(command); 

and then listen to thread_stdout in the async queue. By default, ios_system commands write their standard output to a stream called thread_stdout and listen to a stream called thread_stdin.

@Chihpin
Copy link
Author

Chihpin commented Mar 1, 2019

@holzschu
I see. thx

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants