Common lisp: run shell command containing the string "\n"

828 views Asked by At

I'm trying to run something that simplifies to

(trivial-shell:shell-command (concatenate 'string "echo -e " "one" "\n" "two"))

And trying to get it to return something like

"one two"

Whereas what it returns instead is

"onentwo "

I'd be open to using something different from trivial-shell (I've tried inferior-shell, but it had the same problem)

Using SBCL 1.1.18 on 64-bit Gentoo Linux

2

There are 2 answers

0
coredump On BEST ANSWER

The simplest way to output a newline is to have a newline character inside your string:

(print "one
two")

You can also coerce the newline character into a string:

(concatenate 'string "one" (string #\newline) "two")

You cannot use \n in a string to insert newlines: the backslash is only used to escape double-quotes inside strings (e.g. "\""). Necessarily, it is used to escape actual backslashes character too. So if you want to print a backslash followed by n, you need to escape the backslash:

(print "\\n")

But then, why do you need 4 backslashes?

Because trivial-shell:shell-command invokes a shell, as given by *bourne-compatible-shell*, and gives it a command as a string. And so, your string might be subject to shell escaping rules too.

(trivial-shell:shell-command "echo -e one \\\\n two")

Knowing that, you might want to take into account the fact that bourne compatible shells have single-quotes, which do not require escaping characters inside of them. The following works as expected:

(trivial-shell:shell-command "echo 'one 
two'")

Note that the default value for trivial-shell:*bourne-compatible-shell* is "/bin/sh", which as far as I know does not recognize the -e option for echo. You might already know this, but you might want to use a different shell. Here is a program that calls echo with newlines between arguments:

(defun echo-args (&rest args)
  (let ((trivial-shell:*bourne-compatible-shell* #P"/bin/bash"))
    (trivial-shell:shell-command
     (format nil "echo -e '~{~A~^~%~}'" args))))
0
Kahr Kunne On

Solved it by using four backslashes instead of one.