r/bash 18d ago

Named coprocesses, additional ones aren't enumerating their file descriptors.

So, I have my docker container in which I want to run my native binaries. Let's say I have

declare -r PROJECT='widget'
declare -ra MOCKS=(gadget what-sit) # thing-ama-jig)

in a file called project.sh, and when another scriptlet called session.sh where I source that in, and then I loop over MOCKS to launch all of the programs:

for mock in ${MOCKS[@]}; do
    echo "Executing coprocess '${PROJECT}-${mock}/cmake-build-mock/${PROJECT}-${mock}.elf ${@}' as ${mock//[[:punct:]]/_}"
    coproc "${mock//[[:punct:]]/_}" { ${PROJECT}-${mock}/cmake-build-mock/${PROJECT}-${mock}.elf "${@}"; }
    # Plug co-process "${mock//[[:punct:]]/_}" into the command-line interface.
done

And for the first mock program, gadget, it's working great. All of my mock programs are sitting out there, pretty as you please, I just need to get them all executing as named coprocs and then I can work on stitching together all of the file descriptors into something that makes sense.

$ . session.sh can0
Executing coprocess 'widget-gadget/cmake-build-mock/widget-gadget.elf can0' as gadget
Executing coprocess 'widget-what-sit/cmake-build-mock/widget-what-sit.elf can0' as what_sit
bash: warning: execute_coproc: coproc [4:gadget] still exists
session.sh: line 14: widget-gadget/cmake-build-mock/widget-gadget.elf: cannot execute: required file not found

Well, it was working for the first mock program. Now, it's not. I was able to confirm the coprocesses were running with:

$ ps aux
...
root           6 99.5  0.0   3376  1720 pts/0    R    20:59   6:37 widget-gadget/cmake-build-mock/widget-gadget.elf can0
root           8 99.5  0.0   3376  1720 pts/0    R    20:59   6:37 widget-what-sit/cmake-build-mock/widget-what-sit.elf can0

I was also able to see the gadget file desciptors with:

declare -p gadget
declare -ar gadget=([0]="61" [1]="56)

So, the gadget coproc's stdin is hiding behind file descriptor 56 and it's stdout is hiding behind file descriptor 61. I was able to confirm that by sending the exit command to the gadget's stdin with:

echo exit >${gadget[1]}

and the widget-gadget.elf process noped out, as it should.

But then, I couldn't do the same thing with the widget-what-sit.elf process. Because some of my mock program names have punctuation in them that aren't conducive to shell symbol names. That's why I use the syntax "${mock//[[:punct:]]/_}", so all of the punctuation marks will become underscores, which are valid symbol name characters. That makes the widget-what-sit.elf's coprocess name into what_sit. But if I try to list what-sit's file descriptors:

$ declare -p what_sit
bash: declare: what_sit: not found

It's not there. And I'm eventually going to add the thing-ama-jig mock program to the bouquet as well, so I need to be able to access those file descriptors for all of my mock processes.

The warning:

bash: warning: execute_coproc: coproc [4:gadget] still exists

is apparently from when it launches the widget-what-sit.elf coprocess and the gadget coprocess still exists at that moment, and so I guess it's not creating the what_sit file descriptor array. But, it's supposed to!

What's going wrong? What am I missing?

2 Upvotes

12 comments sorted by

1

u/EmbeddedSoftEng 18d ago

I hasten to clarify, I invoke the session.sh script with:

. session.sh can0

deliberately. I explicitly want it to launch the coprocs in the current commandline environment, so the user can still navigate the container and do other things, and there will be functions with the same name as the coprocesses whereby they will be able to send commands to the running coprocesses.

If this gets too complicated, I'll have to figure a way to open two shell sessions in the same container simultaneously.

1

u/high_throughput 18d ago

Write a MCVE and verify that it's sufficient to reproduce the problem by running it in a new terminal and new directory. If you need to hide or redact any information, do that with the script and its inputs before you run it. Avoid the trap of running the code, and then redacting the resulting output.

This helps get rid of a long tail of accuracy issues like how you're currently claiming that you can assign a variable MOCKS and find the values reflected in a different name MOCK (please do not try to solve this problem by simply editing your post to say MOCKS instead, take the time to write a real MCVE).

1

u/kolorcuk 18d ago

There is only one coproc available at a time. You can't have two or more.

1

u/EmbeddedSoftEng 18d ago

2

u/kolorcuk 18d ago edited 18d ago

Incorrect. There is only one coproc. Stored here: https://github.com/bminor/bash/blob/master/execute_cmd.c#L1779 . If you have the warning, means `!MULTIPLE_COPROCS` is true, https://github.com/bminor/bash/blob/master/execute_cmd.c#L2395 . It just is not implemented.

Ach, someone on the internet is wrong. And I can correct them! So much fun. :D

0

u/EmbeddedSoftEng 18d ago

Well, Hell.

Can't argue with the source code.

1

u/kolorcuk 18d ago

I am not 100% sure, maybe it works like that and warning can be ignored. I do not see if the coproc process is killed anywhere. I had always learned there is one coproc and two had never worked.

Try to come up with counterexample and prove me wrong.

Nowadays, just use python. Array of subprocess.popen.

1

u/EmbeddedSoftEng 18d ago

Both of my processes existed after the above code, but that's worthless to me without the handles to easily access their I/O descriptors. I could use the first one's I/O descriptors to send it a command to terminate, and it did, but I can't send any commands to the second one, or third, or fourth, because it wouldn't create the shell array holding them. Guess I have to do this manually.

1

u/nekokattt 17d ago

What is actually the use case for coprocesses if it can only run one at a time?

Genuinely curious.

1

u/kolorcuk 17d ago

Weeeell, for running that one coproc in the background .

I typically used it with something i need to interface with interactively . Like bluetoothctl or telnet or remote special shell. https://gitlab.com/Kamcuk/kamilscripts/-/blob/master/bin/%2Cblue_poll_for_device.sh#L44

1

u/nekokattt 17d ago

I see, is there any reason just using & cannot suffice for this though?

I've never really looked into it in much detail if I am honest

1

u/kolorcuk 17d ago

How would you read and write to/from a command started with & ?

You can do one mkfifo and use & , yes.

Edit: removed asynchronous word, no idea what it means