How Can I Get System.cmd to End Normally when expecting input on STDIN? - shell

I've spotted something that I find very puzzling about the behavior of System.cmd. I just wanted to ask if anyone might have thoughts on what I may be doing wrong or what may be going on.
I've been trying to wrap an Elixir script around the ack programmer's grep. I tried this:
{_message, errlevel} = System.cmd("ack",[])
And I get back the help text that ack displays on an empty command line; I won't bother to reproduce it here because it's not necessarily germane to the question.
Then I try this:
{_message, errlevel} = System.cmd("ack",[""])
And it looks like iex hangs. Now I realize in the first case the output may be going to stderr rather than stdout. But there's another reason I'm asking about this; I found something even more interesting to me. Because I'm not 100% committed to using ack I thought I'd try ripgrep on the thought that it might interact with stdout better.
So if I do this:
{_message, errlevel} = System.cmd("rg",[])
Same as ack with no arguments--shows me the help text. Again I'm guessing it's probably out to stderr. I could check to confirm my assumption but what's even more interesting to me is that when I do this:
{_message, errlevel} = System.cmd("rg",[""])
It hangs again!
I had always figured the issue is with how ack interacts with stdout but now I'm not so sure since I see the same behavior with ripgrep. This is Elixir 1.13.2 on MacOSX 13.1. I've seen this same behavior with older versions of MacOSX.
Any idea how I can get the ack and/or ripgrep process to terminate so I get a response back? I've seen this https://hexdocs.pm/elixir/main/Port.html#module-zombie-operating-system-processes and I can try it but I was hoping for something slightly less hacky, I guess. Any suggestions? Also if I use the :stderr_to_stdout option set to true, it doesn't seem to make any difference.
I've seen this Q & A but I'm not totally clear on how using Task.start_link would help in this case. I mean would one do a Task.start_link on System.cmd?

You are executing a command that expects input on STDIN, but with System.cmd/3, there is no mechanism to provide the input.
Elixir has no way to know the behaviour of the command you are executing, so waits for the process to terminate, which never happens. As José mentioned on the issue Roger Lipscombe raised, this is expected behaviour.
If you want to send the OS process input via STDIN, you need to use Ports. However, there are limitations there too, which I asked about here.
For ack specifically, it reads from STDIN if you don't provide a filename. So you can workaround the limitation by putting the data in a file, and providing the filename as an argument, rather than piping the data via OS streams.

Looks like a bug. I've filed https://github.com/elixir-lang/elixir/issues/12321.

Related

Golang get command tty output

I'm using go's exec Run command to get command output, which works great when the command 'Stdout' field is set to os.Stdout, and the error is sent to os.Stderr.
I want to display the output and the error output to the console, but I also want my program to see what the output was.
I then made my own Writer type that did just that, wrote both to a buffer and printed to the terminal.
Here's the problem—some applications change their output to something much less readable by humans when it detects it's not writing to a tty. So the output I get changes to something ugly when I do it in the latter way. (cleaner for computers, uglier for humans)
I wanted to know if there was some way within Go to convince whatever command I'm running that I am a tty, despite not being os.Stdout/os.Stderr. I know it's possible to do using the script bash command, but that uses a different flag depending on Darwin/Linux, so I'm trying to avoid that.
Thanks in advance!
The only practical way to solve this is to allocate a pseudo terminal (PTY) and make your external process use it for its output: since PTY is still a terminal, a process checking whether it's connected to a real terminal thinks it is.
You may start with this query.
The github.com/creack/ptyis probably a good starting point.
The next step is to have a package implementing a PTY actually allocate it, and connect "the other end" of a PTY to your custom writer.
(By the way, there's no point in writing a custom "multi writer" as there exist io.MultiWriter).

Emacs: some programs only work in ansi-term, some programs only work in shell

Relative Emacs newbie here, just trying to adapt my programming workflow to fit with emacs. So far I've discovered shell-pop and I'm quite enjoying on-demand terminals that pop up when needed for banging out the odd commands.
What I understand so far about Emacs is that shell is a "dumb" terminal that doesn't support any ansi control codes, and that makes it incompatible with things like ncurses that attempt to draw complex UI's on a terminal emulator. This is why you can't use less or top or similar in shell-mode.
However, I seem to be having trouble with ansi-term, it's not the be-all, end-all that it's cracked up to be. Sure, it has no problems running less or git log or even nano, but there are a few things that can't quite seem to display properly when they're running in an ansi-term, such as apt-get and nosetests. I'm not sure quite what the name is for it, but apt-get's output is characterised by live-updating what is displayed on the very last line, and then having unchanging lines of text scroll out above that line. It seems to be halfway between something like less and something dumber, like cat. Somehow ansi-term doesn't like this at all, and I get very garbled output, where it seems to output everything on one line only or just generally lose it's place and output things all over, randomly. In the case of nosetests, it starts off ok, but if any libraries spew out any STDERR, the output all goes to hell in a similar way.
With some fiddling it seems possible to fix this by mashing C-l and RET, but it's not always reliable.
Does anybody know what's going on here? Is there some way to fix ansi-term so that it can display everything properly? Or is there perhaps some other mode that I don't know about that is way better? Ideally I'd like something that "just works" as effortlessly as, eg, Gnome Terminal, which can run all of the above mentioned programs without a single hiccup.
Thanks!
I resolved this issue by commenting out my entire .emacs.el and then uncommenting and restarting emacs for every single line in the file. I discovered that the following line alone was responsible for the issue:
'(fringe-mode 0 nil (fringe))
(this line disables the fringes from inside custom-set-variables).
I guess this is a bug in Emacs, that disabling the fringe causes term-mode to garble it's output really badly whenever any output line exceeds $COLUMN columns.
Anyway, I don't really like the fringes much at all, and it seems I was able to at least disable the left fringe without triggering this issue:
(set-fringe-mode (cons 0 8))
Maybe apt-get does different things based on the $TERM environment variable. What happens if you set TERM=dumb? If that makes things work, then you can experiment with different values until you find one that supports enough features but still works.
Note that git 2.0.1 (June 25th, 2014) now better detects dumb terminal when displaying verbose messages.
That might help Emacs better display some of the messages received from git, but the fringe-mode bug reported above is certainly the main cause.
See commit 38de156 by Michael Naumov (mnaoumov)
sideband.c: do not use ANSI control sequence on non-terminal
Diagnostic messages received on the sideband #2 from the server side are sent to the standard error with ANSI terminal control sequence "\033[K" that erases to the end of line appended at the end of each line.
However, some programs (e.g. GitExtensions for Windows) read and interpret and/or show the message without understanding the terminal control sequences, resulting them to be shown to their end users.
To help these programs, squelch the control sequence when the standard error stream is not being sent to a tty.

Perl's waitpid() and IPC::Open2 in Windows vs. Linux/UNIX

The following code works flawlessly in Linux, but when I put it on a Windows machine, all hell breaks loose:
$pid = open2($my_reader, $my_writer, "$JAVACALL $PARSER_CP $PARSER_CMD -");
$my_reader->autoflush(1);
$my_writer->autoflush(1);
print $my_writer "$line\n";
close($my_writer);
my $tree = <$my_reader>;
chomp($tree);
push #parse_trees, $tree;
close($my_reader);
waitpid($pid, 0);
In Windows, it just hangs waiting on waitpid($pid, 0). If we comment that line out, everything finishes properly, but then we're left with numerous orphaned processes. What confuses me is that ActiveState (who's Perl we are using) actually says to do things exactly how we're doing them.
Does anyone know a Windows-happy way to do this? Even if I have to maintain two separate versions of the code, I could care less at this point. :)
(For those curious, this code opens the Stanford Parser, which takes a file as input (hence the use of "-" (similar to/same as /dev/stdin in Linux)), throws a sentence at it, and gets back a parse tree. Not important but I know someone is going to ask. :) )
Well, for those who are curious, I got things working the way they should, but I'm not too crazy about part of the fix. I added
use POSIX ":sys_wait_h";
and changed that waitpid() call to
waitpid($pid, &WNOHANG);
This alone didn't do it, so immediately after this I added
kill(-9, ($pid));
to kill the process. Not really liking the call to kill() but it works, and oddly enough I still had zombie processes if I didn't also call waitpid(). Again, it'd be great if anyone knows a more elegant solution, but this will have to do for now. :)

Setting non-canonical mode on stdin with Ruby

I'm playing around with making a simple terminal-based game with Ruby, and I'm currently trying to come up with a way of reading input from the terminal.
So far I've been using gets, but I'd like to have the game react instantly without requiring a newline (so you don't need to press a key, THEN enter).
I've figured out I need to put the terminal in non-canonical mode, and I'm assuming I can do that by calling $stdin.ioctl. The problem is, I'm not sure what arguments or flags I should be passing to this, and the documentation and searches just lead to information about the underlying C function.
Can anyone tell me what I should be calling $stdin.ioctl with? I'm using Terminal.app/tcsh on OSX Leopard.
Edit: This is what I ended up using, thanks to MarkusQ:
%x{stty -icanon -echo}
key = STDIN.read(1)
Your problem is outside of ruby.
Easiest answer: wrap your IO in %x{stty -raw echo} and %x{stty -raw echo} to change the mode with stty.
You'll probably want to do and ensure an exit handler to make certain the mode is set back when you exit.
-- MarkusQ

What does CreateFile("CONIN$" ..) do?

I was hacking away the source code for plink to make it compatible with unison.
If you don't know, unison is a file synchronization tool, it runs an "ssh" command to connect to a remote server, but there's no ssh.exe for windows; there's plink, which is very close but not close enough (it doesn't behave like unison expects it to), so people usually make wrappers around it, like this one.
one of the problems is that unison expects the password prompt to print to stderr (but plink prints it to stdout, and causes unison to be confused), so I thought, well, should be simple enough, hack my thru plink's code and make it print the prompt to stdout. so I hacked my way through and did that.
Next problem: I can't respond to the prompt!! no matter what I type, it has no effect.
the code for getting input is roughly like this:
hin = GetStdHandle(STD_INPUT_HANDLE);
....
r = ReadFile(hin, .....);
I'm not sure why it's done this way, but I'm not an expert in designing command line tools for windows, so what do I know! But I figure something is missing in setting up the input handle.
I looked at the source code for the above wrapper tool and I see this:
hconin=CreateFile("CONIN$",GENERIC_READ|GENERIC_WRITE,FILE_SHARE_READ,0,OPEN_EXISTING,0,0)
and I try it (just for the heck of it)
hin=CreateFile("CONIN$",GENERIC_READ|GENERIC_WRITE,FILE_SHARE_READ,0,OPEN_EXISTING,0,0);
....
r = ReadFile( hin ...... )
and surprisingly it works! I can now respond to the prompt!
Why is this? what is "CONIN$"? and why is it different from the STD_INPUT_HANDLE?
I can sort of "guess" that FILE_SHARE_READ and OPEN_EXISTING are playing a role in this (since ssh is being run from within another process), but I want to understand what's going on here, and make sure that this code doesn't have some unwanted side effects or security holes or something scary like that!
CONIN$ is the console input device. Normally, stdin is an open file handle to this, but if stdin is redirected for some reason, then using CONIN$ will allow you to get access to the console despite the redirection. Reference.

Resources