5

I want to be able to tell whether stdout is a pipe in an elisp script. This would allow to distinguish between:

$ emacs --script foo.el

and

$ emacs --script foo.el | cat

Is this possible in Emacs lisp? Most programming languages have this in the stdlib.

3 Answers 3

3

Wrapping isatty(3) in a dynamic module seems to work:

$ emacs --batch -Q --load isatty.so --eval '(princ  (isatty))'
t

$ emacs --batch -Q --load isatty.so --eval '(princ  (isatty))' | cat
nil

Build the module

$ EMACS_ROOT=/home/xcy/src/emacs/ # /path/to/root/of/emacs/source/code
$ gcc -I$EMACS_ROOT/src -fPIC -c isatty.c
$ gcc -shared isatty.o -o isatty.so

isatty.c's contents:

#include <emacs-module.h>
#include <stdio.h>
#include <unistd.h>

int plugin_is_GPL_compatible;

static emacs_value
Fisatty (emacs_env *env, int argc, emacs_env argv[], void *data)
{
  if (isatty (fileno (stdout)))
    return env->intern (env, "t");
  else
    return env->intern (env, "nil");
}

static void
bind_function (emacs_env *env, const char *name, emacs_value Sfun)
{
  emacs_value Qfset = env->intern (env, "fset");
  emacs_value Qsym = env->intern (env, name);

  emacs_value args[] = {Qsym, Sfun};

  env->funcall (env, Qfset, 2, args);
}

int emacs_module_init (struct emacs_runtime *ert)
{
  emacs_env *env = ert->get_environment (ert);
  emacs_value fun = env->make_function (env,
                                        0,
                                        0,
                                        Fisatty,
                                        "Return t if stdout is tty, nil otherwise.",
                                        NULL);
  bind_function (env, "isatty", fun);
  return 0;
}
2

There is a work-around by wrapping Emacs in a subshell:

$ ( [ -t 1 ] && export TTY_P=yes ; emacs ... )

For example, the output is a tty:

$ ( [ -t 1 ] && export TTY_P=yes ; emacs --batch --eval '(princ (getenv "TTY_P"))' )

yes

and the output is not a tty (piped or redirected)

$ ( [ -t 1 ] && export TTY_P=yes ; emacs --batch --eval '(princ (getenv "TTY_P"))' ) | cat

nil
1

On Linux, you can use /proc:

(let* ((stdout-proc-path
        (format "/proc/%s/fd/1" (emacs-pid)))
       (piped-p
        ;; If following the links gives us a path like
        ;; "/proc/26783/fd/pipe:[432241]" (which doesn't exist) rather than
        ;; "/dev/pts/2" (which exists), then we're piped into another
        ;; process. Assumes Emacs is not running in a GUI.
        (not (file-exists-p (file-chase-links stdout-proc-path)))))
  (princ (format "have we been piped? %s\n" piped-p)))

However, this isn't portable: not even OS X has /proc. Hopefully there are better alternatives.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.