5

It seems that emacs appends a newline to the return value for shell-command-to-string. For example, the return value for (shell-command-to-string "pwd") is:

"/path/to/directory
"

While the return value for (pwd) is "/path/to/directory". In other words:

(string= (shell-command-to-string "pwd") (pwd))

will return nil.

I am wondering:

  1. What is the reason for this?
  2. What would be an idiomatic way to remove the newline from the output so that (string= (shell-command-to-string "pwd") (pwd)) would return t?
YoungFrog
  • 3,526
  • 18
  • 28
elethan
  • 4,825
  • 3
  • 30
  • 56
  • 5
    The newline is produced by pwd, not Emacs. Also, you should use string=, or equal for strings, not eq. – npostavs Apr 27 '16 at 19:50
  • @npostavs I used pwd as a simple example, but the newline gets added in other situations I have tested. For example, running a python script that returns a string value with no newline will have a newline added when run from shell-command-to-string. Also, cating a file with only one line of text and no trailing newline will have the newline appended in the shell-command-to-string output. – elethan Apr 27 '16 at 20:06
  • 1
    I think @npostavs is correct - can you find an example where a shell command that doesn't include a final newline in a terminal has one inserted by shell-command-to-string? Note that in a terminal the prompt following the output of most commands is on the line after the output, not on the same line as the last line of the output, indicating that there's a newline at the end. – Tyler Apr 27 '16 at 21:34
  • 1
    Further to npostavs's comment on testing equality, you cannot use eq to compare two strings -- not unless they are the same lisp object. e.g. (eq "foo" "foo") is comparing two different lisp objects, and is therefore nil. – phils Apr 28 '16 at 02:24
  • 1
    In this particular example and similar cases I'd just avoid the shell entirely and use (car (process-lines "pwd")). –  Apr 28 '16 at 20:44
  • lunaryorn: Thanks! I'd either forgotten or just never noticed that. Added in 23.1, I see. Very useful. You should post that as an answer. – phils Apr 28 '16 at 20:48

2 Answers2

8

Shell commands usually terminate their output with a newline. shell-command-to-string doesn't add a newline, it merely stores the contents of the output of the shell command — including the final newline, if any — in a string. Compare

(shell-command-to-string "echo hello")

which contains the final newline generated by echo, and

(shell-command-to-string "echo -n hello")

which doesn't.

You can postprocess the returned string to get rid of the newline, for example with the following function:

(defun string-trim-final-newline (string)
  (let ((len (length string)))
    (cond
      ((and (> len 0) (eql (aref string (- len 1)) ?\n))
       (substring string 0 (- len 1)))
      (t string))))
jch
  • 5,720
  • 1
  • 23
  • 39
  • 2
    Alternatively, you can add a newline char to the end of the other string. ;-) – Drew Apr 28 '16 at 02:11
  • FYI: To remove the output of a shell command printing to STDOUT, both the Lisp approach and echo -n $(pwd) will work. – ctietze Aug 08 '20 at 08:19
  • 1
    For most purposes (trim-string-right (shell-command-to-string "...")) should be sufficient. – Tobias Apr 23 '21 at 07:45
5

As already explained, the newline is coming from the shell command output.

You can eliminate that by making the shell command format its output with printf (which is portable and reliable when it comes to displaying newlines, unlike echo).

(shell-command-to-string "printf %s \"$(pwd)\"")

Alternatively...

If you don't actually need to run the program via your shell, than another convenient option (suggested in the comments by another user, but never converted to an answer) is to use process-lines, which starts the specific program directly via call-process and (quoting the manual) "waits for it to finish, and returns its output as a list of strings. Each string in the list holds a single line of text output by the program; the end-of-line characters are stripped from each line."

So using that, you can simply take the first list item:

(car (process-lines "pwd"))

No shell is involved here but, as newlines are stripped, this approach can also resolve scenarios where you are obtaining unwanted trailing newlines from the program itself.

Note that using process-lines to take a single line from a large amount of output would be inadvisable, as the entire output will be converted to a list regardless of how much of it you use, which could be very inefficient. If you can't tell the program to limit its output then it may be preferable to use the shell approach, as you can then pipe the program's output through head.

phils
  • 50,977
  • 3
  • 79
  • 122