kovidgoyal/kitty

Support receiving stdout/stderr of `kitten @ launch --type=background some_script`

amosbird opened this issue · 15 comments

When executing kitten @ launch --type=background some_script, the stdout and stderr of some_script are currently directed to the running terminal of kitty, with only the exit status being returned to the calling location.

I am seeking an option to redirect the stdout and stderr back to the calling location. This helps implement workflows in remote that interact with the local host machine.

kitten @ launch --type=background sh -c "stdout_and_stderr=$(some_script 2>&1)

kitten @ launch --type=background sh -c "stdout_and_stderr=$(some_script 2>&1)

It seems I didn't describe the FR clearly. I'd like to have the stdout/stderr redirects to the tty which executes the kitten command. For instance:

Current behavior:

$ kitten @ launch --type=background echo hello world
0   <---  currently it prints the exit status zero

Expected behavior:

$ kitten @ launch --type=background echo hello world
hello world   <---  I'd like to get the stdout here

Run

echo hello world &

echo hello world &

Sorry I don't quite follow. How does this help?

The echo hello world is just a demo script used to describe the FR. Real scripts will be only available on host machine and the kitten command will run on remote machine.

If you had said so in the first place, it would have saved some time.

Run:

kitten @ launch --type=background sh -c "exec whatever > /tmp/output 2> /tmp/output" && kitten transfer --direction=upload /tmp/output temp-output-file && cat temp-output-file && rm temp-output-file

You will get a permission prompt from the transfer kitten, read its docs
to see how to avoid that.

Sorry for wasting your time. I'll do more preparation before submit FR next time :)

While the provided solution addresses certain scenarios, it falls short in cases where synchronization is necessary. I understand that additional "hacks," such as waiting for temporary result files to appear in the remote shell, can be implemented. However, wouldn't it be more straightforward to extend rc.Launch.response_from_kitty with is_asynchronous = True and utilize subprocess.Popen.communicate() to wait for the output? I did some tests, and it seems working.

You are most welcome to send a PR to add that functionality, but note
that to be acceptable it has to:

  1. Allow sending stdin from the kitten invocation as stdin to the
    background process. See @ send-text for an example of how to implement
    this. You can possibly make it simpler to implement by only doing it
    when stdin is not a tty.

  2. Return the stdout, stderr and exit status in the response which the
    kitten should then forward to its stdout, stderr and exit status.

Also note that you cannot use subprocess.communicate() as that will
block the main thread of kitty. Instead you need to run the background
process redirecting the output to unnamed temp files and in your
response read from them. There is also the question of what to do when
there is a lot of output, I dont recall if the kitten is able to read
arbitrary sized responses from kitty, you will need to test.

Also, in the --no-response case it should work as it does currently,
with no response.

Oh and just for completeness, your synchronized usecase can be
implemented like this:

kitten @ launch --no-response --type=background --allow-remote-control sh -c "echo -e 'hello\nworld\nEOF' | kitten @ send-text --stdin --match id:$KITTY_WINDOW_ID"; stty_orig=`stty -g`; stty raw -echo; python -c "import sys; [sys.exit(0) if line.rstrip() == 'EOF' else print(line, end='\r') for line in sys.stdin]"; stty "$stty_orig"

In production you should of course use something more unique than EOF to
signify end of input.

And because I'm on a roll, here's a version without EOF and stty

kitten @ launch --no-response --type=background --allow-remote-control sh -c "echo -e 'hello\nworld\nagain' | kitten @ send-text --stdin --match id:$KITTY_WINDOW_ID; kitten @ signal-child --match id:$KITTY_WINDOW_ID"; python -c "import sys, signal, os, tty, termios; attr=termios.tcgetattr(1); tty.setraw(1); signal.signal(signal.SIGINT, lambda *a: (termios.tcsetattr(1, termios.TCSANOW, attr), os._exit(0))); [print(line, end='\r') for line in sys.stdin]";

This should be completely robust and synchronous. It will even stream
data from your command to STDOUT, not reading everything in a batch.
Which your proposed changes to --type=background wont do.

You can make it not line buffered by replacing the python one-liner with a
proper script that reads from stdin in non-blocking mode and writes to
stdout, replacing all newlines by \n\r.

kitten @ launch --no-response --type=background --allow-remote-control sh -c "echo -e 'hello\nworld\nagain' | kitten @ send-text --stdin --match id:$KITTY_WINDOW_ID; kitten @ signal-child --match id:$KITTY_WINDOW_ID"; python -c "import sys, signal, os, tty, termios; attr=termios.tcgetattr(1); tty.setraw(1); signal.signal(signal.SIGINT, lambda *a: (termios.tcsetattr(1, termios.TCSANOW, attr), os._exit(0))); [print(line, end='\r') for line in sys.stdin]";

Interesting. But this doesn't work when kitten is running inside an ssh session. Signaling ssh will directly kill the connection instead of passing it to the underlying foreground process.

Then use a terminator like EOF.