kovidgoyal / kitty

Cross-platform, fast, feature-rich, GPU based terminal
https://sw.kovidgoyal.net/kitty/
GNU General Public License v3.0
24.41k stars 980 forks source link

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

Closed amosbird closed 5 months ago

amosbird commented 5 months ago

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.

kovidgoyal commented 5 months ago

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

amosbird commented 5 months ago

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
kovidgoyal commented 5 months ago

Run

echo hello world &

amosbird commented 5 months ago

echo hello world &

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

kovidgoyal commented 5 months ago

It runs echo hello world in the background with its stdout/stderr connected to the tty.

amosbird commented 5 months ago

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.

kovidgoyal commented 5 months ago

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.

amosbird commented 5 months ago

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.

kovidgoyal commented 5 months ago

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.

kovidgoyal commented 5 months ago

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.

kovidgoyal commented 5 months ago

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

kovidgoyal commented 5 months ago

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.

kovidgoyal commented 5 months ago

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.

amosbird commented 5 months ago

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.

kovidgoyal commented 5 months ago

Then use a terminator like EOF.