I have these lines in my ~/.inputrc:
set editing-mode vi
set keymap vi
This allows me to use vi keybindings in every program that uses GNU readlines for text input. Examples: python, irb, sftp, bash, sqlite3, and so on. It makes working with a command line a breeze. Matlab doesn't use readlines, but vi keybindings would be amazing to have when debugging or working interactively. Is there an existing solution?
I tend to use matlab -nosplash -nodesktop from the command line and that got me thinking: would it be possible to write a wrapper that does use readlines and pass the input to matlab? (If I have to implement this, I'd probably prefer to do so in Ruby.)
Update:
Thanks for the help. This almost works:
# See also: http://bogojoker.com/readline/
require 'readline'
puts 'Starting Matlab...'
io = IO.popen('matlab -nosplash -nodesktop 2>&1', 'w+')
while input_line = Readline.readline('>> ', true)
io.puts input_line
puts io.gets
end
But it only reads a single line from Matlab at a time (because I'm using gets). Any ideas on how to get everything until the next time it's waiting for input? Here's what's happening (I'm entering stuff at the >> prompt):
Starting Matlab...
>> 1
>> 2
< M A T L A B (R) >
>> 3
Copyright 1984-2009 The MathWorks, Inc.
>> 4
Version 7.8.0.347 (R2009a) 32-bit (glnx86)
>> 5
February 12, 2009
>> 6
>> 7
>> 8
To get started, type one of these: helpwin, helpdesk, or demo.
>> 9
For product information, visit www.mathworks.com.
>> 0
>> 1
>>
>> 2
ans =
>> 3
>> 4
1
>> 5
>> 6
>>
>> 7
ans =
>> 8
>> 9
2
>> 0
>> 1
>>
>> 2
ans =
>> 3
>> 4
3
Yes, that should be easy enough. It's just a special case of the general "open a process and bind to its stdin and stdout" problem, and that's not difficult.
A bit of Google searching finds that IO.popen() is the right piece of Ruby for that, and there's a little more detail in the replies here: http://groups.google.com/group/ruby-talk-google/browse_thread/thread/0bbf0a3f1668184c. Hopefully, that's enough to get you started!
Update: Looks like you're almost there with your wrapper. What you need to get finished is recognize when Matlab is asking for input, and only ask the user for input then. I'd suggest trying this pseudocode:
while input_line = Readline.readline('>> ', true)
io.puts input_line
while ((output_line = io.gets) != '>> ') // Loop until we get a prompt.
puts io.gets
end
end
That's not quite right, as you need to do the inner loop once before you ask for the first input line, but it should give you the idea. You might need to adjust the prompt text that it's looking for, too.
Update 2: Okay, so we also need to account for the fact that there's no EOL after a prompt and so io.gets will hang. Here's a revised version that uses the fact that you can give a blank line to a Matlab prompt and it will just give you another prompt without doing anything. I've rearranged the loop to make things a little clearer, though this means you now have to add logic to figure out when you're done.
while [not done] // figure this out somehow
io.puts blank_line // This will answer the first
// prompt we get.
while ((output_line = io.gets) != '>> ') // Loop until we get a prompt.
puts io.gets // This won't hang, since the
end // prompt will get the blank
// line we just sent.
input_line = Readline.readline('>> ', true) // Get something, feed it
io.puts input_line // to the next prompt.
output_line = io.gets // This will eat the prompt that corresponds to
// the line we just fed in.
end
You could have used rlwrap in a straight-forward manner.
rlwrap is a wrapper that uses the GNU readline library to allow the editing
of keyboard input for any other command.
http://utopia.knoware.nl/~hlub/rlwrap/#rlwrap
Unfortunately it will block context-sensitive tab completion in MATLAB, which is useful on its own.
Actually, you might be better off writing this in C - then you can call the matlab engine directly. This basically allows you to write your own front-end to matlab, if you are so inclined, using the GNU Readline library.
Related
I am writing a program in which I am taking in a csv file via the < operator on the command line. After I read in the file I would also like to ask the user questions and have them input their response via the command line. However, whenever I ask for user input, my program skips right over it.
When I searched stack overflow I found what seems to be the python version here, but it doesn't really help me since the methods are obviously different.
I read my file using $stdin.read. And I have tried to use regular gets, STDIN.gets, and $stdin.gets. However, the program always skips over them.
Sample input ruby ./bin/kata < items.csv
Current File
require 'csv'
n = $stdin.read
arr = CSV.parse(n)
input = ''
while true
puts "What is your choice: "
input = $stdin.gets.to_i
if input.zero?
break
end
end
My expected result is to have What is your choice: display in the command and wait for user input. However, I am getting that phrase displayed over and over in an infinite loop. Any help would be appreciated!
You can't read both file and user input from stdin. You must choose. But since you want both, how about this:
Instead of piping the file content to stdin, pass just the filename to your script. The script will then open and read the file. And stdin will be available for interaction with the user (through $stdin or STDIN).
Here is a minor modification of your script:
arr = CSV.parse(ARGF) # the important part.
input = ''
while true
puts "What is your choice: "
input = STDIN.gets.to_i
if input.zero?
break
end
end
And you can call it like this:
ruby ./bin/kata items.csv
You can read more about ARGF in the documentation: https://ruby-doc.org/core-2.6/ARGF.html
This has nothing to do with Ruby. It is a feature of the shell.
A file descriptor is connected to exactly one file at any one time. The file descriptor 0 (standard input) can be connected to a file or it can be connected to the terminal. It can't be connected to both.
So, therefore, what you want is simply not possible. And it is not just not possible in Ruby, it is fundamentally impossible by the very nature of how shell redirection works.
If you want to change this, there is nothing you can do in your program or in Ruby. You need to modify how your shell works.
I'm writing a language interpreter in Haskell, and for the most part it works (hooray!). But some of the tests fail in an odd way that depends on whether stdout is sent to the terminal or a pipe...
I'm using tasty-golden to test the REPL. There are text files with pasted REPL sessions like this:
Welcome to the ShortCut interpreter!
Type :help for a list of the available commands.
shortcut >> v1 = "one"
shortcut >> v2 = "two"
shortcut >> v3 = [v1, v2]
shortcut >> :show
v1 = "one"
v2 = "two"
v3 = [v1, v2]
shortcut >> :rdepends v1
v3 = [v1, v2]
shortcut >> :quit
Bye for now!
I have a function that reads those files and splits them into user input (anything after shortcut >>) and what the program should print to stdout. It passes the input lines to the REPL, captures stdout using the silently package, intercalates them, and checks that the combined output is the same as that file.
Everything works, but only if I pipe the overall program output through less! When printing directly to the terminal, certain lines slip through and are actually printed, then the tests fail because they aren't part of the captured output.
I think it might be a bug in the interaction between Tasty and Silently, which both do fancy things with terminal output, but have no idea how to debug it or write a reproducible example.
Here's the complete output run in three different ways:
Copied directly from the terminal
Piped through less and copied from the terminal
Piped through tee and copied from the resulting file
As you can see:
Each FAIL is preceded by printing part of the REPL output to the terminal instead of capturing it
less somehow gets around that and the tests pass
tee misses the printed bits (they end up in terminal but not the file)
Any ideas about what could be going on? I can post any parts of the code you think might be relevant, but didn't want to muddy the question with hundreds and hundreds of mostly-unrelated lines.
Update: tried replacing Silently's hCapture_ with this function from another question:
catchOutput :: IO () -> IO String
catchOutput action = do
tmpd <- getTemporaryDirectory
(tmpf, tmph) <- openTempFile tmpd "haskell_stdout"
stdout_dup <- hDuplicate stdout
hDuplicateTo tmph stdout
hClose tmph
action
hDuplicateTo stdout_dup stdout
str <- readFile tmpf
removeFile tmpf
return str
Unfortunately it does the same thing.
Update 2:
Well, figured it out. Sorry for everyone who tried to puzzle through it, as I don't think I gave enough information! The bug was in my REPL monad, which was defined like this:
type ReplM a = StateT CutState (MaybeT (InputT IO)) a
runReplM :: ReplM a -> CutState -> IO (Maybe CutState)
runReplM r s = runInputT defaultSettings $ runMaybeT $ execStateT r s
prompt :: String -> ReplM (Maybe String)
prompt = lift . lift . getInputLine
print :: String -> ReplM ()
print = lift . lift . outputStrLn
I noticed that all the erroneous prints came from functions that use print, whereas a couple cases where I used liftIO . putStrLn instead worked as expected. So I just redefined print as that:
print :: String -> ReplM ()
print = liftIO . putStrLn
I still don't really get why the other version didn't work though, so I'll give the answer to whoever can explain it. outputStrLn is defined in Haskeline.hs.
How do I add a line-break/new-line in IRB/Ruby? The book I'm learning from shows this code:
print "2+3 is equal to "
print 2 + 3
without telling how to go to the second line without hitting Enter, which obviously just runs the program.
You could use semicolon at the end of statement like this puts "hello";puts"world"
That book might be taking very tiny steps to introducing this idea:
print "Continues..."
puts "(Up to here)"
The print function just outputs to the terminal exactly what it's given. The puts function does the same but also adds a newline, which is what you want.
The more Ruby way of doing this is either:
puts "2+3 equals #{2+3}" # Using string interpolation
puts "2+3 equals %d" % (2 + 3) # Using sprintf-style interpolation
Now if you're using irb, that's a Read-Evaluate-Print-Loop (REPL) which means it executes everything you type in as soon as you press enter, by design. If you want to use your original code, you need to force it on one line:
print "2+3 equals "; print 2+3
Then that will work as expected. The ; line separator is rarely used in Ruby, most style guides encourage you to split things up onto multiple lines, but if you do need to do a one-liner, this is how.
When writing code in, say a .rb file the return key is just used for formatting and doesn't execute any code.
You can put a semicolon after the first line, like this:
print "2+3 is equal to ";
print 2 + 3
I am using "pipe to program" option in Evolution email client, that runs following ruby script
#!/usr/bin/ruby
%% example code below
junk_mail = 2
junk_mail
Now this program always returns 0, irrespective of what the value of junk_mail variable is.
I guess it has something to do with Evolution forking a child process to run this code, and always 0 (clean exit) is received back?
Help needed.
EDIT
I figured out actual problem is with data being read from pipe. Following code works fine when tested in command line, but it is unable to read pipe data when called from Evolution client
#!/usr/bin/ruby
email_txt = ARGF.read
File.open("~/debug.txt", 'a') { |file| file.write(email_txt + "\n") }
$cat email.txt | ./myprog.rb
This gives debug.txt as expected, but when called from Evolution pipe-to-program, it gives empty data.
Am I using the correct way to read piped stream data when called from external program? (I am under Fedora 20).
Use exit:
#!/usr/bin/ruby
junk_mail = 2
exit junk_mail
You can test this by running it from the command line in linux, then echoing the exit value via
echo $?
EDIT
To read STDIN into a single string:
email_txt = STDIN.readlines.join
I am currently attempting to figure out a way to call a Perl script from Ruby and have it output as if I was in the terminal and would allow me to provide input if it is needed.
I have figured out how I can do this and get the input after the fact but because the Perl script is still running, I am not able to run anything else.
I should note that I can not edit the Perl scripts. These scripts are being provided and this Ruby script is being made to make the process of running all of the Perl scripts easier and ensuring they are in the right order.
upgradestatus = `#{upgradearray[arraylocation]}`
This would be the relevant part my code for this. I have attempted a few other variations of how to do this but I get the same situation every time. When the script starts running it requires input so it just sits there.
You can't do what you want using backticks, %x or as a normal sub-shell, because they lack the ability to watch the output of the sub-command's output.
You could do it using Open3's popen2 or popen3 methods. They let you send to the STDIN stream for the called program, and receive data from the STDOUT. popen3 also lets you see/capture the STDOUT stream too. Unfortunately, often you have to send, then close the STDIN stream before the called program will return its information, which might be the case of the Perl scripts.
If you need more control, look into using Ruby's built-in Pty module. It's designed to let you talk to a running app through a scripting mechanism. You have to set up code to look for prompts, then respond to them by sending back the appropriate data. It can be simple, or it can be a major PITA, depending on the code you're talking to.
This is the example for the open command:
PTY.open {|m, s|
p m #=> #<IO:masterpty:/dev/pts/1>
p s #=> #<File:/dev/pts/1>
p s.path #=> "/dev/pts/1"
}
# Change the buffering type in factor command,
# assuming that factor uses stdio for stdout buffering.
# If IO.pipe is used instead of PTY.open,
# this code deadlocks because factor's stdout is fully buffered.
require 'io/console' # for IO#raw!
m, s = PTY.open
s.raw! # disable newline conversion.
r, w = IO.pipe
pid = spawn("factor", :in=>r, :out=>s)
r.close
s.close
w.puts "42"
p m.gets #=> "42: 2 3 7\n"
w.puts "144"
p m.gets #=> "144: 2 2 2 2 3 3\n"
w.close
# The result of read operation when pty slave is closed is platform
# dependent.
ret = begin
m.gets # FreeBSD returns nil.
rescue Errno::EIO # GNU/Linux raises EIO.
nil
end
p ret #=> nil