Subprocess output to string in Racket - subprocess

How would you implement the following procedure in Racket:
(command->string "printf" "hello world")
in such a way that:
It runs the argv[] '("printf" "hello world") in a subprocess, finding printf in PATH.
If the subprocess exits with code zero, returns stdout as a string.
If the subprocess exits nonzero, raises an exception.
Stdin and stderr are ignored; they are the same as for the Racket process.

You can use
(with-output-to-string
(lambda ()
(or (system* (find-executable-path "printf") "hello world")
(error 'who "command failed"))))
If you try this at the Racket REPL, it works but it seems to confuse the REPL, and the next prompt is not shown. I think maybe the printf command is peeking at stdin, or perhaps there's a bug in XREPL. Wrapping the expression above with
(parameterize ((current-input-port (open-input-string ""))) _)
seems to fix the issue.

(define (command->string command . args)
(let-values (((sub stdout stdin stderr)
(apply subprocess #f
(current-input-port)
(current-error-port)
(find-executable-path command)
args)))
(let ((output (port->string stdout)))
(subprocess-wait sub)
(if (eqv? 0 (subprocess-status sub))
output
(error "Command failed:" (cons command args))))))
(writeln (command->string "printf" "%s" "hello world"))
In case anyone from Racket is reading, please consider adding something like this to the standard library. This is simple and quite commonly needed.

Related

How do I make my Racket-designed language run scripts from external files?

I've got a custom language I'm designing in Racket, let's call it waffle.
Let's say I have
(define (printstr . input)
(if (string? (car input))
(write (string-join input ""))
(write input)))
; ... a whole bunch of other definitions
(command-line
#:multi
[("-v" "--verbose") "more verbose" (set! loglevel (add1 loglevel))]
[("-q" "--quiet") "be quiet" (set! loglevel 0)]
#:once-any
[("-i" "--in-place") "edit in-place" (set! mode 'in-place)]
[("-c" "--create-new") "create a new file" (set! mode 'new)]
[("-n" "--dry-run") "do nothing" (set! mode #f)]
#:once-each
[("-d" "--directory") dir "work in a given directory" (set! root dir)]
#:help-labels "operations to perform:"
#:multi
[("+l" "++line") "add a line" (set! ops `(,#ops "add"))]
[("-l" "--line") "delete a line" (set! ops `(,#ops "delete"))]
[("-e" "--edit") "edit a line" (set! ops `(,#ops "edit"))]
#:args (file)
(define in (open-input-file file))
; This is probably where I'm going wrong with how my language REPL evaluates files passed to it.
(eval (file->list file) ns))
Then I create an executable from DrRacket using 'Racket [menu] -> Create Executable ... -> [Type] Launcher'. Name is e.g. waffle-test.
I've got a file written in my waffle language, hello.waffle:
(printstr "Hello!")
I expect this to print 'Hello!' on the command line and then exit without errors. But I get a strange error I don't understand, and I get my prompt back without a newline.
$ ./waffle-test hello.waffle
application: not a procedure;
expected a procedure that can be applied to arguments
given: #<void>
arguments...: [none]
context...:
eval-one-top12
"/home/connie/Desktop/racket-ffgardfghf/waffle": [running body]
temp37_0
for-loop
run-module-instance!125
perform-require!78
"Hello!" $
I know you're not supposed to use eval but I can't find out how to make my language executable read and run files I pass to it. What is the best approach to doing this?
Just with this simple test I figured out a couple of things:
#!racket
(file->list "test.waffle")
With test.waffle as:
(waffle me)
(waffle it)
The repl printed:
((waffle me)
(waffle it))
However that is not valid code, even if waffle is a valid procedure. You need it to look like this:
(begin
(waffle me)
(waffle it))
Now you can do this by require your language to have it, but you can also just cons a begin to the resulting structure and eval will evaluate each top level form one by one in order.
You will run into problems with using eval. You'll know that soon enough. The correct way to make an interpreter is by creating your own eval that implements your languages syntax and takes en environment with primitives. It has an interface to use the host, but not regardless. With eval waffle programs has access to all internals and you're not really creating an interpreter since you just expose Racket.
I remember someone did the same with Ruby and had a web page and someone just tried to type in a command that deleted all the files on the system and the web service went away.
By fixing a few bugs in my code I managed to solve my problem!
I changed
(eval (file->list file) ns))
to
(define program (cons 'begin (file->list file)))
(eval program ns))
I also changed the 'printstr' function from
(define (printstr . input)
(if (string? (car input))
(write (string-join input ""))
(write input)))
to
(define (printstr input)
(displayln input))

Racket equivalent of /dev/null?

If I have a function that prints to (current-output-port), is there an easy way to run the function without it printing to the output port?
Previously, I've used /dev/null as an output target:
(with-output-to-file "/dev/null" #:exists 'append
(lambda () (displayln "hello world")))
This is easy, but platform dependent. Also I'll sometimes forget the #:exists flag.
Yes! Use open-output-nowhere from racket/port.
(parameterize ([current-output-port (open-output-nowhere)])
(displayln "hello world"))
If you want to hide error output, override current-error-port instead / also.

How do you make an interactive input loop in Racket?

I'm trying to make a loop which asks for a command, executes the command, and then loops again. It only exits if the command entered is "exit". I have tried a couple different things, but when I run them they work once before (read-line) reads an # and then does this infinitely without waiting for input. My initial attempt looked like this:
(define (inputLoop)
(define command "init")
(do()((equal? command "exit") (display "exited successfully..."))
(display "What would you like to do?(start,stop,exit)")
(set! command (read-line))
(cond [(equal? command "start") (start)]
[(equal? command "stop") (stop)]
[else (void)])))
My next attempt was similar, but instead of using a do-loop, it just recursively called the function called inputLoop if the command was not "exit".
Here is a sample of the output:
What would you like to do?(start,stop,exit)start
What would you like to do?(start,stop,exit)
What would you like to do?(start,stop,exit)
...<thousands-of-lines-here>
What would you like to do?(start,stop,exit)
What would you like to do?(start,stop,exit)
What would you like to do?(start,stop,exit)exit
exited successfully...
This is a simple version of what you describe:
(define (input-loop)
(display "What would you like to do? (start,stop,exit)")
(define command (read-line))
(cond [(string=? command "start") (start) (input-loop)]
[(string=? command "stop") (stop) (input-loop)]
[(string=? command "exit") (displayln "exited successfully...")]
[else (displayln "unknown command") (input-loop)]))
You could also have an escape-continuation and break out of the loop; this comes closest to a classical while True: [...] break approach:
(define (input-loop)
(let/ec break
(let loop ()
(display "What would you like to do? (start,stop,exit)")
(define command (read-line))
(cond [(string=? command "start") (start)]
[(string=? command "stop") (stop) ]
[(string=? command "exit") (break)]
[else (displayln "unknown command")])
(loop)))
(displayln "exited successfully..."))
Note that in the second case, you can conveniently put the closing logic (display "exited successfully") at the end of the procedure, whereas in the first case you need to put it into the loop, otherwise the code may be executed several times.
Example execution on command-line (OS X):
pu#pumbair: ~/Projects/L-Racket racket input-loop.rkt
What would you like to do? (start,stop,exit)a
unknown command
What would you like to do? (start,stop,exit)starr
unknown command
What would you like to do? (start,stop,exit)start
start!
What would you like to do? (start,stop,exit)stop
stop!
What would you like to do? (start,stop,exit)exit
exited successfully...
using this (mock-up) version:
#lang racket
(define (start) (displayln "start!"))
(define (stop) (displayln "stop!"))
(define (input-loop)
(let/ec break
(let loop ()
(display "What would you like to do? (start,stop,exit)")
(define command (read-line))
(cond [(string=? command "start") (start)]
[(string=? command "stop") (stop) ]
[(string=? command "exit") (break)]
[else (displayln "unknown command")])
(loop)))
(displayln "exited successfully..."))
(input-loop)
After following #uselpa 's advice about checking my start and stop methods, I've come to the conclusion that my problem was in my start method. It created several places to run concurrently using the (place id body ...+) form documented in the racket documentation. The documentation states,
The bodys close only over id plus the top-level bindings of the enclosing module
Since my code had the call to the loop in the top-level, each created place executed the loop upon creation. My solution was instead to use the (dynamic-place module-path start-name) form documented here. I just had to move my code to an external file. There is no access to top-level bindings from the calling code for the place code using this method.
After making these changes, I was able to successfully run both of the loop techniques provided by #uselpa in their answer, without any infinite loops or errors.

Print output into a file or not print output?

I'd like to save or ignore outputs when I execute a specific function in lisp. I use Emacs and CCL. For example,
(defun foo (x) (format t "x = ~s~%" x))
and if I execute the function, it prints out "x = 5". But I don't want to printout in a buffer, because if I have a large amount of iteration, the speed of simulation will be decreased.
Any idea?
You can temporarily redirect standard output by binding *standard-output* to a stream. For example, a broadcast stream with no output streams will serve as a black hole for output:
(let ((*standard-output* (make-broadcast-stream)))
(foo 10)
(foo 20))
;; Does not output anything.
You can also do this with other binding constructs, such as with-output-to-string or with-open-file:
(with-output-to-string (*standard-output*)
(foo 10)
(foo 20))
;; Does not print anything;
;; returns the output as a string instead.
(with-open-file (*standard-output* "/tmp/foo.txt" :direction :output)
(foo 10)
(foo 20))
;; Does not print anything;
;; writes the output to /tmp/foo.txt instead.
Instead of t as the first argument to format, you can give it an output file stream and your output for that statement will be sent to that file stream.
However having excessive disk I/O will also will increase your running time, hence you can consider having two modes like a debug and a release mode for your program where the debug mode prints all the diagnostic messages and the release mode does not print anything at all.
I'm not sure I understand your question, but the second argument to format is a stream. If you set it to t it prints to standard output, but you can also set it to an open file.
So something like this would allow you to select where the output goes:
;;output to file:
(setf *stream* (open "myfile" :direction :output
:if-exists :supersede)
;;alternative to output to standard output:
;;(setf *stream* t)
(defun foo (x) (format *stream* "x = ~s~%" x))
(foo 10)
(close *stream*) ;; only if output sent to a file

Writing "Hello World" in Emacs?

I would like to write a few Unix scripts in Emacs Lisp. However, there doesn't seem to be a clean way to write to STDOUT so I can redirect the results to a file or pipe the output to another command. The print function places double quotes around the output strings so I get "Hello world!" instead of Hello world!.
Here's the emacs script.
#!/usr/bin/emacs --script
;;
;; Run me from a Unix shell: ./hello.el > x.txt
;;
(message "Hello world! I'm writing to STDERR.")
(print "Hello world! I'm writing to STDOUT but I'm in quotes")
(insert "Hello world! I'm writing to an Emacs buffer")
(write-file "y.txt")
And here's how I would like to call it.
hello.el > x.txt
hello.el | wc
Seems like you want princ instead of print. So, basically:
(princ "Hello world! I'm writing to STDOUT but I'm not in quotes!")
However, one caveat is that princ does not automatically terminate the output with \n.
As David Antaramian says, you probably want princ.
Also, message supports a format control string (akin to printf in C) that is adapted from format. So, you may eventually want to do something like
(princ (format "Hello, %s!\n" "World"))
As a couple of functions plus demonstration:
(defun fmt-stdout (&rest args)
(princ (apply 'format args)))
(defun fmtln-stdout (&rest args)
(princ (apply 'format
(if (and args (stringp (car args)))
(cons (concat (car args) "\n") (cdr args))
args))))
(defun test-fmt ()
(message "Hello, %s!" "message to stderr")
(fmt-stdout "Hello, %s!\n" "fmt-stdout, explict newline")
(fmtln-stdout "Hello, %s!" "fmtln-stdout, implicit newline"))