Asked  7 Months ago    Answers:  5   Viewed   53 times

How to wait in a bash script for several subprocesses spawned from that script to finish and return exit code !=0 when any of the subprocesses ends with code !=0 ?

Simple script:

#!/bin/bash
for i in `seq 0 9`; do
  doCalculations $i &
done
wait

The above script will wait for all 10 spawned subprocesses, but it will always give exit status 0 (see help wait). How can I modify this script so it will discover exit statuses of spawned subprocesses and return exit code 1 when any of subprocesses ends with code !=0?

Is there any better solution for that than collecting PIDs of the subprocesses, wait for them in order and sum exit statuses?

 Answers

57

wait also (optionally) takes the PID of the process to wait for, and with $! you get the PID of the last command launched in background. Modify the loop to store the PID of each spawned sub-process into an array, and then loop again waiting on each PID.

# run processes and store pids in array
for i in $n_procs; do
    ./procs[${i}] &
    pids[${i}]=$!
done

# wait for all pids
for pid in ${pids[*]}; do
    wait $pid
done
Tuesday, June 1, 2021
 
codingb
answered 7 Months ago
95

Your current execution thread will be blocked on process.waitFor() until process is terminated (i.e. execution finished). Source here

Also note that if process is already terminated : waitFor() will not be blocked. I don't know if the code you put in your question is exactly what you run... but you must be careful and re-create a new instance of Process for every execution of your script (i.e. not just calling start multiple times on the same Process: it won't work after first execution)

Tuesday, August 3, 2021
 
Alan Clark
answered 4 Months ago
85

The simple answer is there is no other way. This is how it's mean to be done in Android. The only thing, I believe, you're missing is passing a request code to activity B. Without it, you wouldn't be able to differentiate which other activity returned result to activity A.

If you're invoking different activities from your A, use different requestCode parameter when starting activity. Furthermore, you can pass any data back to activity B using the same Intent approach (ok, almost any):

public final static int REQUEST_CODE_B = 1;
public final static int REQUEST_CODE_C = 2;
...

Intent i = new Intent(this, ActivityB.class);
i.putExtra(...);    //if you need to pass parameters
startActivityForResult(i, REQUEST_CODE_B);

...

//and in another place:
Intent i = new Intent(this, ActivityC.class);
i.putExtra(...);    //if you need to pass parameters
startActivityForResult(i, REQUEST_CODE_C);

Then in your on ActivityResult:

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    switch(requestCode) {
        case REQUEST_CODE_B:
            //you just got back from activity B - deal with resultCode
            //use data.getExtra(...) to retrieve the returned data
            break;
        case REQUEST_CODE_C:
            //you just got back from activity C - deal with resultCode
            break;
    }
}

OnActivityResult is executed on the GUI thread, therefore you can make any updates you want straight in here.

Finally, in Activity B, you'd have:

Intent resultIntent = new Intent();
resultIntent.putExtra(...);  // put data that you want returned to activity A
setResult(Activity.RESULT_OK, resultIntent);
finish();

I'm not sure why you need AsyncTask to handle results.

Tuesday, August 10, 2021
 
Isky
answered 4 Months ago
69

I would store the process handles in a dictionary of script vs process handle, and then later wait for all processes to exit. Below is the code:

var processes = new Dictionary<string,Process>();

foreach (string script in scriptList)
{
    ProcessStartInfo myProcess = new ProcessStartInfo();
     myProcess.FileName = accoreconsolePath;
     myProcess.Arguments = "/s "" + script + """;
     myProcess.CreateNoWindow = true;
     myWait = Process.Start(myProcess);
     processes.Add(script, myWait);
}

foreach(var script in processes.Keys)
{
    Process process = processes[script];

    process.WaitForExit();
    process.Close();

    File.Delete(script);
}
Saturday, August 14, 2021
 
Smandoli
answered 4 Months ago
100

Your question is a bit odd. You have impossibly constrained the issue. You cannot have a line of code "wait" for a process to finish w/o it blocking something, in this case whatever thread the loop is running in.

You can use a synchronous call if you wanted to, it doesn't block your app, it only blocks the thread it is executed on. In your example, you have a loop that is continually getting remote data and you want your UI to reflect that until it is done. But you don't want your UI blocked. That means, this thread with your loop already MUST be on a background thread so you can feel free to do a synchronous call in the loop w/o blocking your UI thread. If the loop is on the UI thread you need to change this to do what you want.

You could also do this using an asynchronous connection. In that case, your operation may actual complete faster b/c you can have multiple requests in progress at the same time. If you do it that way, your loop can remain on the UI thread and you just need to track all of the connections so that when they are finished you can communicate that status to the relevant controllers. You'll need iVars to track the loading state and use either a protocol or NSNotification to communicate when loading is done.

EDIT: ADDED EXAMPLE OF SYNCHRONOUS CALL ON BACKGROUND THREAD

If you want the loop to finish completely only when all requests are finishes and not block your UI thread here's a simple example:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    // post an NSNotification that loading has started
    for (x = 0; x < numberOfRequests; x++) {
        // create the NSURLRequest for this loop iteration
        NSURLResponse *response = nil;
        NSError *error = nil;
        NSData *data = [NSURLConnection sendSynchronousRequest:request
                                             returningResponse:&response
                                                         error:&error];
        // do something with the data, response, error, etc
    }
    // post an NSNotification that loading is finished
});

Whatever objects need to show loading status should observe and handle the notifications you post here. The loop will churn through and make all your requests synchronously on a background thread and your UI thread will be unblocked and responsive. This isn't the only way to do this, and in fact, I would do it using async connections myself, but this is a simple example of how to get what you want.

Monday, September 6, 2021
 
Cavyn VonDeylen
answered 3 Months ago
Only authorized users can answer the question. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :  
Share